From 3428c0e8464ebf13f59f5c7b1762366f1e1c99a9 Mon Sep 17 00:00:00 2001 From: JJenkx Date: Fri, 23 Oct 2026 13:09:21 -0400 Subject: [PATCH 01/46] fix(libc): compute atanh with FreeBSD msun's algorithm, as titles' libc.prx does atanh returned the host's atanh, whose last bit differs from the titles' copy on Windows. The titles' copy is FreeBSD msun's e_atanh.c over msun's log1p, and it sets EDOM for |x| > 1, the infinities and every NaN, and ERANGE for +-1. atanh now computes that algorithm over this file's log1p and sets errno there. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libc/src/Math.cpp | 29 ++++++++++++++++++++++++++++- core/libs/tests/GuestMath.cpp | 27 +++++++++++++++++++++++++++ docs/dev/debt/libc.md | 1 + 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 9c34d18ed7..c8ea682e14 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -1234,7 +1234,34 @@ float APS5_VABI acoshf_nid_postfix(float x) { const float t = x - 1.0f; return AtanhfLog1p(t + std::sqrt(2.0f * t + t * t)); } -double APS5_VABI atanh_nid_postfix(double x) { return std::atanh(x); } +double APS5_VABI atanh_nid_postfix(double x) { + const double huge = 1e300; + static volatile double zero = 0.0; + const std::uint64_t bits = std::bit_cast(x); + const std::int32_t hx = static_cast(bits >> 32); + const std::uint32_t lx = static_cast(bits); + const std::int32_t ix = hx & 0x7fffffff; + if ((static_cast(ix) | ((lx | (0u - lx)) >> 31)) > 0x3ff00000u) { + errno = EDOM; + return (x - x) / (x - x); + } + if (ix == 0x3ff00000) { + errno = ERANGE; + return x / zero; + } + if (ix < 0x3e300000 && huge + x > 0.0) { + return x; + } + x = std::bit_cast((static_cast(static_cast(ix)) << 32) | lx); + double t; + if (ix < 0x3fe00000) { + t = x + x; + t = 0.5 * log1p_nid_postfix(t + t * x / (1.0 - x)); + } else { + t = 0.5 * log1p_nid_postfix((x + x) / (1.0 - x)); + } + return hx >= 0 ? t : -t; +} float APS5_VABI atanhf_nid_postfix(float x) { const std::uint32_t magnitude = std::bit_cast(x) & 0x7fffffffu; diff --git a/core/libs/tests/GuestMath.cpp b/core/libs/tests/GuestMath.cpp index 80f1217e86..ef410c483e 100644 --- a/core/libs/tests/GuestMath.cpp +++ b/core/libs/tests/GuestMath.cpp @@ -1039,6 +1039,32 @@ static void CheckExpm1ScalingRounding() { _mm_setcsr(savedCsr); } +static void CheckAtanhConsoleBits() { + constexpr struct { + std::uint64_t input; + std::uint64_t expected; + int error; + } cases[] = { + {0x0000000000000000ull, 0x0000000000000000ull, 0}, {0x8000000000000000ull, 0x8000000000000000ull, 0}, + {0x3e2fffffffffffffull, 0x3e2fffffffffffffull, 0}, {0x3e30000000000000ull, 0x3e30000000000000ull, 0}, + {0xbe30000000000000ull, 0xbe30000000000000ull, 0}, {0x3fdfffffffffffffull, 0x3fe193ea7aad030aull, 0}, + {0x3fe0000000000000ull, 0x3fe193ea7aad030aull, 0}, {0xbfe0000000000000ull, 0xbfe193ea7aad030aull, 0}, + {0x3fefffffffffffffull, 0x4032b708872320e2ull, 0}, {0xbfefffffffffffffull, 0xc032b708872320e2ull, 0}, + {0x3ff0000000000000ull, 0x7ff0000000000000ull, ERANGE}, {0xbff0000000000000ull, 0xfff0000000000000ull, ERANGE}, + {0x3ff0000000000001ull, 0xfff8000000000000ull, EDOM}, {0xc000000000000000ull, 0xfff8000000000000ull, EDOM}, + {0x7ff0000000000000ull, 0xfff8000000000000ull, EDOM}, {0xfff0000000000000ull, 0xfff8000000000000ull, EDOM}, + {0x7ff8000000000001ull, 0x7ff8000000000001ull, EDOM}, {0xfff8000000000002ull, 0xfff8000000000002ull, EDOM}, + {0x3f03a67d5567227bull, 0x3f03a67d558ea76eull, 0}, {0x3faa28c4621dd8d7ull, 0x3faa2e9a77f5a83full, 0}, + {0x3f4742fce55398bbull, 0x3f4742fd26e249ceull, 0}, {0x3f2cf9c68a297d4aull, 0x3f2cf9c69214cc60ull, 0}, + {0x3fc796d8c6ddd135ull, 0x3fc7dca409c1ba11ull, 0}, {0x3f73b85b718bdc38ull, 0x3f73b8656ddda145ull, 0}, + }; + for (const auto& item : cases) { + errno = 1234; + Require(std::bit_cast(atanh_nid_postfix(std::bit_cast(item.input))) == item.expected); + Require(errno == (item.error != 0 ? item.error : 1234)); + } +} + static void CheckAsinl() { const struct { std::uint64_t inputMantissa; std::uint16_t inputSignExponent; std::uint64_t outputMantissa; std::uint16_t outputSignExponent; } cases[] = { {0, 0x0000, 0, 0x0000}, {0, 0x8000, 0, 0x8000}, @@ -2369,6 +2395,7 @@ int main() { CheckNextafterEnvironment(nextafterf_nid_postfix, 0x80000000u, 0x3f800000u, 0x00800000u, 0x7f7fffffu, 0x7f800000u); CheckNextafterEnvironment(nextafter_nid_postfix, std::uint64_t{0x8000000000000000ull}, std::uint64_t{0x3ff0000000000000ull}, std::uint64_t{0x0010000000000000ull}, std::uint64_t{0x7fefffffffffffffull}, std::uint64_t{0x7ff0000000000000ull}); CheckExpm1ScalingRounding(); + CheckAtanhConsoleBits(); CheckAsinl(); Require(scalbn_nid_postfix(0.75, -2) == 0.1875); Require(scalbnf_nid_postfix(0.75f, 4) == 12.f); diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 568e1931c1..3ef9660417 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -142,6 +142,7 @@ - [log1p](../../../core/libs/prx/libc/src/Math.cpp) (libc) does not compare floating-point exceptions, and its double inputs are checked by sampling, not exhaustively. It is FreeBSD msun's [`s_log1p.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/s_log1p.c). As the titles' `libc.prx` does, it sets `EDOM` at msun's NaN return (arguments below -1, `-inf` and negative NaNs) and leaves `errno` alone for `log1p(-1)`, where it returns `-inf`. It gives the same bits, NaN payloads included, and `errno` as the `log1p` bundled with PPSA07923 and PPSA05916 on 320 million random and 68,216 structured arguments on a Windows host. The FMA-contracted build bundled with PPSA13934 differs on 73,682 of the random ones. - [nextafter and nextafterf](../../../core/libs/prx/libc/src/Math.cpp) (libc) have no physical-console or unmasked-exception validation. A Linux CPU probe compares result bits, `errno` and masked floating-point flags with the unchanged math code in FAR: Changing Tides PPSA03766 bundled `libc.prx` (`nextafter` 0x51b30, `nextafterf` 0x51c80, `_Feraise` 0x11da0): 16,864 calls across both precisions, four rounding modes and all DAZ/FTZ combinations, not exhaustive inputs. Earlier Windows sampling of eleven bundled libraries compares only bits and `errno`; exception behavior outside the FAR module remains unverified. - [tanh](../../../core/libs/prx/libc/src/Math.cpp) (libc) does not compare floating-point exceptions, and its double inputs are checked by sampling, not exhaustively. It is FreeBSD msun's [`s_tanh.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/s_tanh.c) over this file's msun `Expm1`, with `tiny` read at run time; it sets no `errno`, as the titles' `libc.prx` does not. It gives the same bits as the `tanh` bundled with PPSA07923 and PPSA05916 on 16 million random and 68,211 structured arguments on a Windows host; the FMA-contracted build bundled with PPSA13934 differs on 1,054 of them. +- [atanh](../../../core/libs/prx/libc/src/Math.cpp) (libc) does not compare floating-point exceptions, and its double inputs are checked by sampling, not exhaustively. It is FreeBSD msun's [`e_atanh.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/e_atanh.c) over this file's msun `log1p`. As the titles' `libc.prx` does, it sets `EDOM` for |x| > 1, the infinities and every NaN, and `ERANGE` for ±1, msun's two error returns. It gives the same bits, NaN payloads included, and `errno` as the `atanh` bundled with PPSA07923 and PPSA05916 on 16 million random and 68,211 structured arguments on a Windows host; the FMA-contracted build bundled with PPSA13934 differs on 2,778 of them. - [sinhf](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the `sinhf` of the `libc.prx` bundled with PPSA28319 (disassembly): FreeBSD msun's [`e_sinhf.c`](https://github.com/freebsd/freebsd-src/blob/5aa554c7e5b6fd9042986df07f12ac5687e41392/lib/msun/src/e_sinhf.c) from before `__ldexp_expf`, which squares `expf(|x| / 2)` between `logf(FLT_MAX)` and the overflow threshold, calling msun's [`expm1f`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/s_expm1f.c) and [`expf`](https://github.com/freebsd/freebsd-src/blob/59bc2f095289a10b8cfa55cbb2b45c3bd6c2d4ae/lib/msun/src/e_expf.c), whose paths for the arguments it passes are inlined here. Past the threshold Sony's error helper sets `errno` to `ERANGE` before returning `x * 1e37`; msun itself sets no `errno`. That code, run on a Windows host, gives the same bits as this implementation for every finite argument up to the threshold, of both signs; it was not run on Linux. That the system `libSceLibcInternal`, which Ruffle (PPSA68091) and RetroArch (PPSA99169) import while bundling a zero-return `libc.prx`, has the same code is assumed - [acoshf](../../../core/libs/prx/libc/src/Math.cpp) (libc) exact system `libSceLibcInternal` behavior and FMA-contracted copies remain unverified. Positive finite inputs use [msun](https://github.com/freebsd/freebsd-src/blob/5aa554c7e5b6fd9042986df07f12ac5687e41392/lib/msun/src/e_acoshf.c) range handling and this file's `logf`; the middle range divides -1 before addition under directed rounding. Bits, errno and SSE/x87 status and controls match the supplied PPSA03766/PPSA07923 copies on 236,592 positive finite calls (4,929 inputs, four rounding modes, DAZ/FTZ combinations and sticky flags); these samples are not exhaustive. Invalid-domain errno and SSE/x87 invalid flags are checked against two supplied bundled copies; those samples are not exhaustive either. - [acoshl, asinhl, atanhl, atan2l, cbrtl, erfl, erfcl, expm1l, hypotl, log1pl, log2l, lgammal, tgammal, j0f, j1f, jnf, y0f, y1f and ynf](../../../core/libs/prx/libc/src/Math.cpp) (libc) return the host C library's results (the float Bessel functions through its double ones), not those of the console's own code, so the last bits can differ (`y0f`, `y1f`, `ynf` follow the console at `+-0`). `tgammaf` delegates to the host's double `tgamma` as FreeBSD 11 does ([`s_tgammaf.c`](https://github.com/freebsd/freebsd-src/blob/stable/11/lib/msun/src/s_tgammaf.c)), then restores the sign of a zero result for negative non-integer inputs from the parity of `floor(-x)`; nine title-bundled `libc.prx` builds return that sign for underflowing inputs, where Windows UCRT can return the opposite sign ([#9707](https://github.com/boykopovar/AnyPS5/issues/9707)). As in the console's `libSceLibcInternal.sprx` (SDK 4.5x build of `J03950001`, 0x56410), `lgammal` does not store the sign in `signgam`; `hypotl` returns infinity for an infinite argument even when the other is a NaN, as C11 F.10.4.3 requires. [hypot3, hypot3f and hypot3l](../../../core/libs/prx/libc/src/Math.cpp) follow that module (0x51D30, 0x520D0, 0x52420): the first NaN of x, y and z is returned unchanged, any infinity gives positive infinity, a zero argument gives the two-argument hypot of the other two, and otherwise the largest magnitude is combined with the hypot of the other two From 6aea92f4350bc48870c61664c30f013ad2631a4b Mon Sep 17 00:00:00 2001 From: JJenkx Date: Sat, 24 Oct 2026 06:06:32 -0400 Subject: [PATCH 02/46] test(libc): check atanh pole and domain exception flags --- core/libs/tests/GuestMath.cpp | 50 +++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/core/libs/tests/GuestMath.cpp b/core/libs/tests/GuestMath.cpp index ef410c483e..703e8a3536 100644 --- a/core/libs/tests/GuestMath.cpp +++ b/core/libs/tests/GuestMath.cpp @@ -1038,6 +1038,55 @@ static void CheckExpm1ScalingRounding() { } _mm_setcsr(savedCsr); } +static void CheckAtanhDomainEnvironment() { + const RestoreFloatingPointState saved; + const unsigned savedCsr = _mm_getcsr(); + const struct { + std::uint64_t input; + std::uint64_t result; + unsigned raised; + int error; + } cases[] = { + {0x3ff0000000000000ull, 0x7ff0000000000000ull, 4u, ERANGE}, + {0xbff0000000000000ull, 0xfff0000000000000ull, 4u, ERANGE}, + {0x3ff0000000000001ull, 0xfff8000000000000ull, 1u, EDOM}, + {0xbff0000000000001ull, 0xfff8000000000000ull, 1u, EDOM}, + {0x4000000000000000ull, 0xfff8000000000000ull, 1u, EDOM}, + {0xc000000000000000ull, 0xfff8000000000000ull, 1u, EDOM}, + {0x7ff0000000000000ull, 0xfff8000000000000ull, 1u, EDOM}, + {0xfff0000000000000ull, 0xfff8000000000000ull, 1u, EDOM}, + {0x7ff8000000001234ull, 0x7ff8000000001234ull, 1u, EDOM}, + {0xfff8000000001234ull, 0xfff8000000001234ull, 1u, EDOM}, + {0x7ff0000000000001ull, 0x7ff8000000000001ull, 1u, EDOM}, + {0xfff0000000000001ull, 0xfff8000000000001ull, 1u, EDOM}, + }; + const int rounding[] = {FE_TONEAREST, FE_DOWNWARD, FE_UPWARD, FE_TOWARDZERO}; + double (APS5_VABI * volatile call)(double) = atanh_nid_postfix; + for (unsigned mode = 0; mode != 4; ++mode) { + for (unsigned denorm = 0; denorm != 4; ++denorm) { + const unsigned control = 0x1f80u | (mode << 13u) | ((denorm & 1u) ? 0x40u : 0u) | ((denorm & 2u) ? 0x8000u : 0u); + for (std::uint16_t sticky : {0u, 5u, 32u}) { + for (const auto& item : cases) { + Require(std::fesetenv(FE_DFL_ENV) == 0 && std::fesetround(rounding[mode]) == 0); + Require(fesetexceptflag_nid_postfix(&sticky, 0x3f) == 0); + _mm_setcsr(control | sticky); + GuestFloatingPointEnvironment before; + Require(fegetenv_nid_postfix(&before) == 0 && (before.status & 0x3fu) == sticky); + errno = 1234; + const auto result = std::bit_cast(call(std::bit_cast(item.input))); + const int error = errno; + GuestFloatingPointEnvironment after; + Require(fegetenv_nid_postfix(&after) == 0); + if (result != item.result || after.mxcsr != (control | sticky | item.raised) || (after.status & 0x3fu) != (sticky | item.raised) || after.control != before.control || error != item.error) { + std::fprintf(stderr, "atanh domain: input=%016llx mode=%u denorm=%u sticky=%u bits=%016llx csr=%x x87=%x errno=%d\n", static_cast(item.input), mode, denorm, sticky, static_cast(result), after.mxcsr, after.status, error); + Require(false); + } + } + } + } + } + _mm_setcsr(savedCsr); +} static void CheckAtanhConsoleBits() { constexpr struct { @@ -2396,6 +2445,7 @@ int main() { CheckNextafterEnvironment(nextafter_nid_postfix, std::uint64_t{0x8000000000000000ull}, std::uint64_t{0x3ff0000000000000ull}, std::uint64_t{0x0010000000000000ull}, std::uint64_t{0x7fefffffffffffffull}, std::uint64_t{0x7ff0000000000000ull}); CheckExpm1ScalingRounding(); CheckAtanhConsoleBits(); + CheckAtanhDomainEnvironment(); CheckAsinl(); Require(scalbn_nid_postfix(0.75, -2) == 0.1875); Require(scalbnf_nid_postfix(0.75f, 4) == 12.f); From 26c43ad44c145126c7ab24707957d520a5bdf9fc Mon Sep 17 00:00:00 2001 From: JJenkx Date: Sat, 24 Oct 2026 06:07:34 -0400 Subject: [PATCH 03/46] fix(libc): raise atanh pole and domain flags in both exception banks --- core/libs/prx/libc/src/Math.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index c8ea682e14..22cb3b9c10 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -1242,10 +1242,12 @@ double APS5_VABI atanh_nid_postfix(double x) { const std::uint32_t lx = static_cast(bits); const std::int32_t ix = hx & 0x7fffffff; if ((static_cast(ix) | ((lx | (0u - lx)) >> 31)) > 0x3ff00000u) { + _Feraise_nid_postfix(0x01); errno = EDOM; return (x - x) / (x - x); } if (ix == 0x3ff00000) { + _Feraise_nid_postfix(0x04); errno = ERANGE; return x / zero; } From 802656fbca0f20514911e8c2d8234a5cb8c911f3 Mon Sep 17 00:00:00 2001 From: JJenkx Date: Fri, 23 Oct 2026 14:01:55 -0400 Subject: [PATCH 04/46] fix(libc): set EDOM in fmod and fmodf where titles' libc.prx does fmod and fmodf preserved errno, following FreeBSD msun, but the libc.prx of eleven titles sets EDOM exactly where msun returns (x*y)/(x*y): a zero divisor, an infinite or NaN dividend, or a NaN divisor. There a NaN divisor is returned quieted, then a NaN dividend, then the default NaN. Both functions now do the same; finite results still come from the host. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libc/src/Math.cpp | 28 +++++++++++++-- core/libs/tests/GuestMath.cpp | 63 +++++++++++++++++++++++++++++++-- docs/dev/debt/libc.md | 2 +- 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 7b6f8ff4ca..2de7f4cf80 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -428,6 +428,18 @@ std::int64_t APS5_VABI imaxabs_nid_postfix(std::int64_t x) { } float APS5_VABI fmodf_nid_postfix(float x, float y) { + const std::uint32_t ix = std::bit_cast(x) & 0x7fffffffu; + const std::uint32_t iy = std::bit_cast(y) & 0x7fffffffu; + if (iy == 0 || ix >= 0x7f800000u || iy > 0x7f800000u) { + errno = EDOM; + if (iy > 0x7f800000u) { + return y + y; + } + if (ix > 0x7f800000u) { + return x + x; + } + return (x * y) / (x * y); + } const int saved_errno = errno; const float result = std::fmod(x, y); errno = saved_errno; @@ -1786,8 +1798,20 @@ float APS5_VABI ldexpf_nid_postfix(float x, int exp) { return result; } double APS5_VABI fmod_nid_postfix(double x, double y) { - int saved_errno = errno; - double result = std::fmod(x, y); + const std::uint64_t ix = std::bit_cast(x) & 0x7fffffffffffffffull; + const std::uint64_t iy = std::bit_cast(y) & 0x7fffffffffffffffull; + if (iy == 0 || ix >= 0x7ff0000000000000ull || iy > 0x7ff0000000000000ull) { + errno = EDOM; + if (iy > 0x7ff0000000000000ull) { + return y + y; + } + if (ix > 0x7ff0000000000000ull) { + return x + x; + } + return (x * y) / (x * y); + } + const int saved_errno = errno; + const double result = std::fmod(x, y); errno = saved_errno; return result; } diff --git a/core/libs/tests/GuestMath.cpp b/core/libs/tests/GuestMath.cpp index 58c693c4c5..7d4c7c0762 100644 --- a/core/libs/tests/GuestMath.cpp +++ b/core/libs/tests/GuestMath.cpp @@ -761,7 +761,7 @@ static void CheckFmodfErrno() { const std::uint32_t bits = std::bit_cast(result); const int invalid = std::fetestexcept(FE_INVALID) != 0; const bool nan = std::isnan(result); - if (nan != test.nan || (!nan && bits != test.expected) || invalid != test.invalid || *__error_nid_postfix() != 73) { + if (nan != test.nan || (!nan && bits != test.expected) || invalid != test.invalid || *__error_nid_postfix() != (test.nan ? EDOM : 73)) { std::fprintf(stderr, "Guest fmodf failed for %08x and %08x: got %08x, invalid %d, errno %d\n", test.dividend, test.divisor, bits, invalid, *__error_nid_postfix()); std::abort(); @@ -771,7 +771,7 @@ static void CheckFmodfErrno() { std::feraiseexcept(FE_DIVBYZERO); *__error_nid_postfix() = 73; (void)fmodf_nid_postfix(1.0f, 0.0f); - if (*__error_nid_postfix() != 73 || std::fetestexcept(FE_DIVBYZERO) == 0) { + if (*__error_nid_postfix() != EDOM || std::fetestexcept(FE_DIVBYZERO) == 0) { std::fprintf(stderr, "Guest fmodf dropped the floating-point environment\n"); std::abort(); } @@ -852,7 +852,7 @@ static void CheckNearbyintl() { static void CheckFmodErrno() { errno = 0; (void)fmod_nid_postfix(1.0, 0.0); - Require(errno == 0); + Require(errno == EDOM); Require(fmod_nid_postfix(1e300, 3.0) == std::fmod(1e300, 3.0)); } @@ -1262,6 +1262,62 @@ static void CheckExpm1ScalingRounding() { } _mm_setcsr(savedCsr); } +static void CheckFmodConsoleBits() { + constexpr struct { + std::uint64_t x; + std::uint64_t y; + std::uint64_t expected; + int expectedErrno; + } cases[] = { + {0x3ff0000000000000ull, 0x0000000000000000ull, 0xfff8000000000000ull, EDOM}, + {0xbff0000000000000ull, 0x8000000000000000ull, 0xfff8000000000000ull, EDOM}, + {0x7ff0000000000000ull, 0x4000000000000000ull, 0xfff8000000000000ull, EDOM}, + {0xfff0000000000000ull, 0x7ff0000000000000ull, 0xfff8000000000000ull, EDOM}, + {0x7ff8000000000123ull, 0x4000000000000000ull, 0x7ff8000000000123ull, EDOM}, + {0xfff8000000000123ull, 0x0000000000000000ull, 0xfff8000000000123ull, EDOM}, + {0x3ff0000000000000ull, 0x7ff8000000000456ull, 0x7ff8000000000456ull, EDOM}, + {0x7ff8000000000123ull, 0xfff8000000000456ull, 0xfff8000000000456ull, EDOM}, + {0x7ff4000000000000ull, 0x4000000000000000ull, 0x7ffc000000000000ull, EDOM}, + {0x3ff0000000000000ull, 0x7ff4000000000000ull, 0x7ffc000000000000ull, EDOM}, + {0x7ff0000000000000ull, 0x7ff8000000000456ull, 0x7ff8000000000456ull, EDOM}, + {0x3ff0000000000000ull, 0x7ff0000000000000ull, 0x3ff0000000000000ull, 0}, + {0x4016000000000000ull, 0x4000000000000000ull, 0x3ff8000000000000ull, 0}, + {0x0000000000000000ull, 0x8000000000000000ull, 0xfff8000000000000ull, EDOM}, + {0x0000000000000003ull, 0x0000000000000002ull, 0x0000000000000001ull, 0}, + }; + for (const auto& item : cases) { + errno = 1234; + Require(std::bit_cast(fmod_nid_postfix(std::bit_cast(item.x), std::bit_cast(item.y))) == item.expected); + Require(errno == (item.expectedErrno != 0 ? item.expectedErrno : 1234)); + } + constexpr struct { + std::uint32_t x; + std::uint32_t y; + std::uint32_t expected; + int expectedErrno; + } floatCases[] = { + {0x3f800000u, 0x00000000u, 0xffc00000u, EDOM}, + {0xbf800000u, 0x80000000u, 0xffc00000u, EDOM}, + {0x7f800000u, 0x40000000u, 0xffc00000u, EDOM}, + {0xff800000u, 0x7f800000u, 0xffc00000u, EDOM}, + {0x7fc00123u, 0x40000000u, 0x7fc00123u, EDOM}, + {0xffc00123u, 0x00000000u, 0xffc00123u, EDOM}, + {0x3f800000u, 0x7fc00456u, 0x7fc00456u, EDOM}, + {0x7fc00123u, 0xffc00456u, 0xffc00456u, EDOM}, + {0x7fa00000u, 0x40000000u, 0x7fe00000u, EDOM}, + {0x3f800000u, 0x7fa00000u, 0x7fe00000u, EDOM}, + {0x7f800000u, 0x7fc00456u, 0x7fc00456u, EDOM}, + {0x3f800000u, 0x7f800000u, 0x3f800000u, 0}, + {0x40b00000u, 0x40000000u, 0x3fc00000u, 0}, + {0x00000000u, 0x80000000u, 0xffc00000u, EDOM}, + {0x00000003u, 0x00000002u, 0x00000001u, 0}, + }; + for (const auto& item : floatCases) { + errno = 1234; + Require(std::bit_cast(fmodf_nid_postfix(std::bit_cast(item.x), std::bit_cast(item.y))) == item.expected); + Require(errno == (item.expectedErrno != 0 ? item.expectedErrno : 1234)); + } +} static void CheckAsinl() { const struct { std::uint64_t inputMantissa; std::uint16_t inputSignExponent; std::uint64_t outputMantissa; std::uint16_t outputSignExponent; } cases[] = { @@ -2610,6 +2666,7 @@ int main() { CheckNextafterEnvironment(nextafterf_nid_postfix, 0x80000000u, 0x3f800000u, 0x00800000u, 0x7f7fffffu, 0x7f800000u); CheckNextafterEnvironment(nextafter_nid_postfix, std::uint64_t{0x8000000000000000ull}, std::uint64_t{0x3ff0000000000000ull}, std::uint64_t{0x0010000000000000ull}, std::uint64_t{0x7fefffffffffffffull}, std::uint64_t{0x7ff0000000000000ull}); CheckExpm1ScalingRounding(); + CheckFmodConsoleBits(); CheckAsinl(); Require(scalbn_nid_postfix(0.75, -2) == 0.1875); Require(scalbnf_nid_postfix(0.75f, 4) == 12.f); diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 0da74d906f..4810ab2c02 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -103,7 +103,6 @@ - [Math.cpp](../../../core/libs/prx/libc/src/Math.cpp) (libc) and [SignalList.cpp](../../../core/libs/prx/libSceLibcInternal/SignalList.cpp) (libSceLibcInternal) start with comments that keep the upstream FreeBSD, Sun Microsystems and University of California licence notices of the code they are derived from. The licences require the notices to stay with the code, so they are comments although they are not technical debt, and the conventions check cannot tell them from other comments. Closes when: the conventions check accepts licence notices without a debt entry - [_Cosh](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows the two-argument Dinkumware CRT helper used by `std::complex`, including a scaled large-finite-input path; the signature, exact result and floating-point exception behavior are not verified against the console. - [std::thread::hardware_concurrency](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `CHrhwd8QSBs`) - the starting mask of a title's process, and so the value a title sees, are not measured, and neither is whether the console's count follows a process-wide `cpuset_setaffinity`. This returns 13, the number of CPUs in libkernel's default thread mask (`0x1FFF`), and a process-wide `cpuset_setaffinity` does not change it. In payload processes whose threads started with mask `0xea00` it returned 5 on PS5 firmware 13.60 and 12.70, while `hw.ncpu` read 16 and `sceKernelGetAvailableCpumask` `0x1fff` (both on 13.60). On 12.70 it still returned 5 when called first after `scePthreadSetaffinity` had narrowed the calling thread to one CPU, after the mask was restored, and in a thread created with a two-CPU mask. PPSA34015 imports it. -- [fmodf](../../../core/libs/prx/libc/src/Math.cpp) (libc) - console `errno`, exception flags and NaN payloads remain unverified. The wrapper preserves `errno` like the existing double `fmod`, following FreeBSD 11.4 [`e_fmodf.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/e_fmodf.c); host `std::fmod` still supplies the result and exception flags. - [_LCosh and _LSinh](../../../core/libs/prx/libc/src/Math.cpp) (libc) follow the two-argument Dinkumware CRT helpers `y * cosh(x)` and `y * sinh(x)`, including scaled large-finite-input paths; the signatures are confirmed by Dinkumware's public `ymath.h`, but the exact console results, range-error thresholds and floating-point exception behavior are not verified. - [modf and modff](../../../core/libs/prx/libc/src/Math.cpp) (libc) - native PS5 execution, Windows and unmasked exceptions remain unverified. Linux comparisons with bundled libraries from PPSA03766 and PPSA07923 cover result and integral bits, errno, SSE/x87 flags and control state. Signaling NaNs retain their payload and quiet bit; the measured float path raises SSE invalid while the double path leaves flags unchanged. - [_FSinh and _FCosh](../../../core/libs/prx/libc/src/Math.cpp) (libc) finite results are not verified bit for bit against the console: the scaled product uses the host's binary64 hyperbolic function before rounding to binary32, rather than the float rational and `_FExp` sequence in PRAGMATA's bundled `libc.prx`. NaN preference and payloads, signed zeros, infinite operands and the range-error thresholds follow that binary's instructions; the system `libSceLibcInternal` implementation and nondefault flush-to-zero modes are unverified. Against `libSceLibcInternal.sprx` on a PS5 (firmware 13.60), 12 of 36 measured results differ by one unit in the last place (`_FSinh(0.5, y)` and `_FCosh(1, y)`, `_FCosh(10, y)` for y of 0.5, 1 and 2); zeros, the overflow to infinity at 89 and 90 and the tiny-argument results matched @@ -112,6 +111,7 @@ - [Host stderr preservation](../../../core/libs/prx/libc/src/General.cpp) (WebAssembly) uses descriptor 2 directly, so host diagnostics do not retain an independent stream across guest stderr redirection - [Guest arena](../../../core/libs/prx/libc/src/GuestArena.cpp) (WebAssembly) is limited to an 8 GiB reservation, WebAssembly linear memory does not enforce guest page read, write or execute protections, and host region release throws because host virtual-address reservations are unavailable - [Guest long double conversions](../../../core/libs/prx/libc/include/GuestLongDouble.hpp) (WebAssembly) - storing a binary128 value discards fraction bits beyond x87 precision instead of rounding them; noncanonical x87 encodings are unverified +- [fmod and fmodf](../../../core/libs/prx/libc/src/Math.cpp) (libc) take their finite results and exception flags from the host `std::fmod`; the flags were not compared with the console. They set `EDOM` exactly where FreeBSD 11.4 [`e_fmod.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/e_fmod.c) returns `(x*y)/(x*y)`: a zero divisor, an infinite or NaN dividend, or a NaN divisor. There a NaN divisor is returned quieted, then a NaN dividend, then the default NaN. The `libc.prx` bundled with eleven titles (PPSA04158 to PPSA35169) gives the same bits and `errno` on 4,000,256 sampled pairs per function on a Windows host. - [sscanf_s and vsscanf_s](../../../core/libs/prx/libc/src/Formatting.cpp) (libc) return `EOF` when a reached conversion has a null destination, stopping before further assignments, following C11 K.3.5.3.7 and K.3.5.3.14. Earlier assignments are retained. The constraint handler is not invoked, and console behaviour is unverified. - [memmem and memccpy](../../../core/libs/prx/libc/src/Strings.cpp) (libc) - `memmem` searches for needle occurrences in memory buffers and `memccpy` copies buffers until a character match, following FreeBSD and POSIX specifications; behavior is unverified on console. Neither is exported by `libSceLibcInternal.sprx` on a PS5 (firmware 13.60), so a payload process cannot call them there. From 6215d19689c83c8bd0d33239a341e7394cac215e Mon Sep 17 00:00:00 2001 From: JJenkx Date: Wed, 28 Oct 2026 07:06:19 -0400 Subject: [PATCH 05/46] fix(libc): raise FE_INVALID from fmod and fmodf for a NaN argument Titles' libc.prx raises FE_INVALID wherever fmod and fmodf store EDOM, a quiet NaN argument included, where returning x + x or y + y raised nothing. The domain paths now raise through _Feraise, which stores EDOM and raises the flag in both units. Results and errno are unchanged. CheckFmodEdgeFlags covers both functions. CheckFmodfErrno expected no FE_INVALID for its three quiet NaN rows, as C specifies; PPSA07923's and PPSA13934's libc.prx raise it there, so those rows now expect it. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libc/src/Math.cpp | 4 ++-- core/libs/tests/GuestMath.cpp | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 2de7f4cf80..6fdac889ec 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -431,7 +431,7 @@ float APS5_VABI fmodf_nid_postfix(float x, float y) { const std::uint32_t ix = std::bit_cast(x) & 0x7fffffffu; const std::uint32_t iy = std::bit_cast(y) & 0x7fffffffu; if (iy == 0 || ix >= 0x7f800000u || iy > 0x7f800000u) { - errno = EDOM; + _Feraise_nid_postfix(0x01); if (iy > 0x7f800000u) { return y + y; } @@ -1801,7 +1801,7 @@ double APS5_VABI fmod_nid_postfix(double x, double y) { const std::uint64_t ix = std::bit_cast(x) & 0x7fffffffffffffffull; const std::uint64_t iy = std::bit_cast(y) & 0x7fffffffffffffffull; if (iy == 0 || ix >= 0x7ff0000000000000ull || iy > 0x7ff0000000000000ull) { - errno = EDOM; + _Feraise_nid_postfix(0x01); if (iy > 0x7ff0000000000000ull) { return y + y; } diff --git a/core/libs/tests/GuestMath.cpp b/core/libs/tests/GuestMath.cpp index 7d4c7c0762..3624319726 100644 --- a/core/libs/tests/GuestMath.cpp +++ b/core/libs/tests/GuestMath.cpp @@ -740,14 +740,14 @@ static void CheckFmodfErrno() { const RestoreFloatingPointState saved; const struct { std::uint32_t dividend; std::uint32_t divisor; std::uint32_t expected; bool invalid; bool nan; } cases[] = { {0x3f800000u, 0x00000000u, 0u, true, true}, {0x3f800000u, 0x80000000u, 0u, true, true}, - {0x7f800000u, 0x40000000u, 0u, true, true}, {0x7fc00000u, 0x40000000u, 0u, false, true}, + {0x7f800000u, 0x40000000u, 0u, true, true}, {0x7fc00000u, 0x40000000u, 0u, true, true}, {0x3f800000u, 0x7f800000u, 0x3f800000u, false, false}, {0xbf800000u, 0x7f800000u, 0xbf800000u, false, false}, {0x41280000u, 0x40000000u, 0x3f000000u, false, false}, {0xc1280000u, 0x40000000u, 0xbf000000u, false, false}, {0x41280000u, 0xc0000000u, 0x3f000000u, false, false}, {0xc1280000u, 0xc0000000u, 0xbf000000u, false, false}, {0x00000000u, 0x40000000u, 0x00000000u, false, false}, {0x80000000u, 0x40000000u, 0x80000000u, false, false}, {0x00000000u, 0x80000000u, 0u, true, true}, - {0x3f800000u, 0x7fc00001u, 0u, false, true}, - {0x00000000u, 0x7fc00001u, 0u, false, true}, + {0x3f800000u, 0x7fc00001u, 0u, true, true}, + {0x00000000u, 0x7fc00001u, 0u, true, true}, {0x7f800001u, 0x40000000u, 0u, true, true}, {0x3f800000u, 0x7f800001u, 0u, true, true}, {0x00000003u, 0x00000002u, 0x00000001u, false, false}, @@ -1262,6 +1262,21 @@ static void CheckExpm1ScalingRounding() { } _mm_setcsr(savedCsr); } +static void CheckFmodEdgeFlags() { + const double nan = std::numeric_limits::quiet_NaN(); + const double infinity = std::numeric_limits::infinity(); + constexpr double one = 1.0; + const std::pair cases[] = {{0.0, nan}, {one, -nan}, {nan, one}, {-nan, 0.0}, {one, 0.0}, {infinity, one}, {-infinity, -2.0}}; + for (const auto& [x, y] : cases) { + errno = 0; + std::feclearexcept(FE_ALL_EXCEPT); + Require(std::isnan(fmod_nid_postfix(x, y)) && errno == EDOM && std::fetestexcept(FE_INVALID) != 0); + errno = 0; + std::feclearexcept(FE_ALL_EXCEPT); + Require(std::isnan(fmodf_nid_postfix(static_cast(x), static_cast(y))) && errno == EDOM && std::fetestexcept(FE_INVALID) != 0); + } +} + static void CheckFmodConsoleBits() { constexpr struct { std::uint64_t x; @@ -2667,6 +2682,7 @@ int main() { CheckNextafterEnvironment(nextafter_nid_postfix, std::uint64_t{0x8000000000000000ull}, std::uint64_t{0x3ff0000000000000ull}, std::uint64_t{0x0010000000000000ull}, std::uint64_t{0x7fefffffffffffffull}, std::uint64_t{0x7ff0000000000000ull}); CheckExpm1ScalingRounding(); CheckFmodConsoleBits(); + CheckFmodEdgeFlags(); CheckAsinl(); Require(scalbn_nid_postfix(0.75, -2) == 0.1875); Require(scalbnf_nid_postfix(0.75f, 4) == 12.f); From e89a05b55b7660c4d2717e4725c618a40684881d Mon Sep 17 00:00:00 2001 From: Noxy-HNR Date: Sun, 1 Nov 2026 23:17:50 +0000 Subject: [PATCH 06/46] feat(libSceJxr): implement the pixel format hash, image IID lookups, aligned allocation and IDPEmpty --- core/libs/prx/libSceJxr/CMakeLists.txt | 17 +- core/libs/prx/libSceJxr/Export.cpp | 24 --- core/libs/prx/libSceJxr/include/Api.hpp | 14 +- core/libs/prx/libSceJxr/src/GlueHelpers.cpp | 109 +++++++++++ core/libs/prx/libSceJxr/src/NativeHash.c | 6 + core/libs/tests/GuestJxrGlue.cpp | 199 ++++++++++++++++++++ docs/dev/debt/libSceJxr.md | 6 +- 7 files changed, 342 insertions(+), 33 deletions(-) create mode 100644 core/libs/prx/libSceJxr/src/GlueHelpers.cpp create mode 100644 core/libs/prx/libSceJxr/src/NativeHash.c create mode 100644 core/libs/tests/GuestJxrGlue.cpp diff --git a/core/libs/prx/libSceJxr/CMakeLists.txt b/core/libs/prx/libSceJxr/CMakeLists.txt index 5d76a6870f..d9eabc2728 100644 --- a/core/libs/prx/libSceJxr/CMakeLists.txt +++ b/core/libs/prx/libSceJxr/CMakeLists.txt @@ -1,8 +1,8 @@ -add_library(jxr_lookup_native STATIC src/NativeLookup.c) +add_library(jxr_lookup_native STATIC src/NativeLookup.c src/NativeHash.c) configure_jxr_native(jxr_lookup_native) target_include_directories(jxr_lookup_native PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(jxr_lookup_native PRIVATE jxr) -add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp src/PixelFormatLookup.cpp src/GuidExports.cpp) +add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp src/GlueHelpers.cpp src/PixelFormatLookup.cpp src/GuidExports.cpp) target_link_libraries(libSceJxr PRIVATE jxr_lookup_native) if(NOT APPLE) target_link_options(libSceJxr PRIVATE LINKER:--exclude-libs,ALL) @@ -69,3 +69,16 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() endif() + +if(BUILD_TESTING) + add_test_executable(guest_jxr_glue_tests ${LIBS_INCLUDE_DIR}/tests/GuestJxrGlue.cpp) + target_include_directories(guest_jxr_glue_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_jxr_glue_tests PRIVATE libSceJxr libc jxr) + configure_windows_unwind(guest_jxr_glue_tests) + add_test(NAME guest_jxr_glue COMMAND guest_jxr_glue_tests) + set_tests_properties(guest_jxr_glue PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_jxr_glue PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceJxr/Export.cpp b/core/libs/prx/libSceJxr/Export.cpp index 50f91d2edc..f18c21e59d 100644 --- a/core/libs/prx/libSceJxr/Export.cpp +++ b/core/libs/prx/libSceJxr/Export.cpp @@ -88,18 +88,6 @@ int APS5_VABI FreeDescMetadata_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI GetImageDecodeIID_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - -int APS5_VABI GetImageEncodeIID_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - -int APS5_VABI GetPixelFormatFromHash_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI GetPosWS_File_nid_postfix() { NotImplemented_nid_no_patch(__func__); } @@ -112,18 +100,10 @@ int APS5_VABI GetPosWS_Memory_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI IDPEmpty_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI InitZigzagScan_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI PKAllocAligned_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI PKAlloc_nid_postfix() { NotImplemented_nid_no_patch(__func__); } @@ -200,10 +180,6 @@ int APS5_VABI PKFormatConverter_Release_nid_postfix() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI PKFreeAligned_nid_postfix() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI PKImageDecode_Copy_nid_postfix() { NotImplemented_nid_no_patch(__func__); } diff --git a/core/libs/prx/libSceJxr/include/Api.hpp b/core/libs/prx/libSceJxr/include/Api.hpp index b57921294e..c8c78da72e 100644 --- a/core/libs/prx/libSceJxr/include/Api.hpp +++ b/core/libs/prx/libSceJxr/include/Api.hpp @@ -1,6 +1,8 @@ #pragma once #include "SceTypes.hpp" +#include +#include #include "prx/libSceJxr/GuestPixelInfo.h" #include "prx/libSceJxr/Stream.hpp" @@ -10,6 +12,12 @@ std::int64_t APS5_VABI PKFree_nid_postfix(void** memory); std::int64_t APS5_VABI PixelFormatLookup_nid_postfix(JxrPixelInfo* value, std::uint8_t mode); std::int64_t APS5_VABI readIS_nid_postfix(void* codec, Jxr::BitIo* io); std::int64_t APS5_VABI writeIS_nid_postfix(void* codec, Jxr::BitIo* io); +std::int64_t APS5_VABI GetImageDecodeIID_nid_postfix(const char* extension, const std::uint32_t** iid); +std::int64_t APS5_VABI GetImageEncodeIID_nid_postfix(const char* extension, const std::uint32_t** iid); +const void* APS5_VABI GetPixelFormatFromHash_nid_postfix(std::uint8_t hash); +std::int32_t APS5_VABI IDPEmpty_nid_postfix(void* codec); +std::int64_t APS5_VABI PKAllocAligned_nid_postfix(void** memory, std::size_t size, std::size_t alignment); +std::int64_t APS5_VABI PKFreeAligned_nid_postfix(void** memory); int APS5_VABI AllocateCodingContextDec_nid_postfix(); int APS5_VABI CloseWS_List_nid_postfix(); @@ -18,15 +26,10 @@ int APS5_VABI CreateWS_Memory_nid_postfix(); int APS5_VABI EOSWS_File_nid_postfix(); int APS5_VABI EOSWS_Memory_nid_postfix(); int APS5_VABI FreeDescMetadata_nid_postfix(); -int APS5_VABI GetImageDecodeIID_nid_postfix(); -int APS5_VABI GetImageEncodeIID_nid_postfix(); -int APS5_VABI GetPixelFormatFromHash_nid_postfix(); int APS5_VABI GetPosWS_File_nid_postfix(); int APS5_VABI GetPosWS_List_nid_postfix(); int APS5_VABI GetPosWS_Memory_nid_postfix(); -int APS5_VABI IDPEmpty_nid_postfix(); int APS5_VABI InitZigzagScan_nid_postfix(); -int APS5_VABI PKAllocAligned_nid_postfix(); int APS5_VABI PKAlloc_nid_postfix(); int APS5_VABI PKCodecFactory_CreateCodec_nid_postfix(); int APS5_VABI PKCodecFactory_CreateDecoderFromFile_nid_postfix(); @@ -46,7 +49,6 @@ int APS5_VABI PKFormatConverter_GetSourcePixelFormat_nid_postfix(); int APS5_VABI PKFormatConverter_InitializeConvert_nid_postfix(); int APS5_VABI PKFormatConverter_Initialize_nid_postfix(); int APS5_VABI PKFormatConverter_Release_nid_postfix(); -int APS5_VABI PKFreeAligned_nid_postfix(); int APS5_VABI PKImageDecode_Copy_nid_postfix(); int APS5_VABI PKImageDecode_Create_WMP_nid_postfix(); int APS5_VABI PKImageDecode_Create_nid_postfix(); diff --git a/core/libs/prx/libSceJxr/src/GlueHelpers.cpp b/core/libs/prx/libSceJxr/src/GlueHelpers.cpp new file mode 100644 index 0000000000..d360c499b8 --- /dev/null +++ b/core/libs/prx/libSceJxr/src/GlueHelpers.cpp @@ -0,0 +1,109 @@ +#include "prx/libSceJxr/libSceJxr.hpp" +#include "prx/libc/include/Api.hpp" +#include +#include +#include +#include +#include +#include + +extern "C" const void* JxrHashLookup_nid_no_patch(std::uint8_t hash); + +namespace { + +constexpr std::int64_t WmpErrSuccess = 0; +constexpr std::int64_t WmpErrOutOfMemory = -101; +constexpr std::int64_t WmpErrUnsupportedFormat = -106; +constexpr std::uint32_t ImageUnsupported = 100; + +struct IidInfo { + const char* extension; + const std::uint32_t* encode; + const std::uint32_t* decode; +}; + +int Lower(char value) { + const auto byte = static_cast(value); + return byte >= 'A' && byte <= 'Z' ? byte + ('a' - 'A') : byte; +} + +bool MatchesPrefix(const char* extension, const char* expected) { + auto count = std::strlen(expected); + for (; Lower(*extension) == Lower(*expected) && *extension && *expected && count; ++extension, ++expected, --count) {} + return count == 0; +} + +const IidInfo* FindIid(const char* extension, const char* function) { + if (!extension) throw std::invalid_argument(std::string(function) + " requires an extension"); + static const IidInfo table[] = { + {".jxr", &JxrGuest_IID_PKImageWmpEncode_nid_no_patch, &JxrGuest_IID_PKImageWmpDecode_nid_no_patch}, + {".wdp", &ImageUnsupported, &JxrGuest_IID_PKImageWmpDecode_nid_no_patch}, + {".hdp", &ImageUnsupported, &JxrGuest_IID_PKImageWmpDecode_nid_no_patch}, + }; + for (const auto& entry : table) + if (MatchesPrefix(extension, entry.extension)) return &entry; + return nullptr; +} + +std::int64_t StoreIid(const std::uint32_t* IidInfo::*field, const char* extension, const std::uint32_t** iid, + const char* function) { + const auto* info = FindIid(extension, function); + if (!info) return WmpErrUnsupportedFormat; + if (!iid) throw std::invalid_argument(std::string(function) + " requires an output for a known extension"); + *iid = info->*field; + return WmpErrSuccess; +} + +} // namespace + +extern "C" { + +const void* APS5_VABI GetPixelFormatFromHash_nid_postfix(std::uint8_t hash) { + return JxrHashLookup_nid_no_patch(hash); +} + +std::int64_t APS5_VABI GetImageEncodeIID_nid_postfix(const char* extension, const std::uint32_t** iid) { + return StoreIid(&IidInfo::encode, extension, iid, "GetImageEncodeIID"); +} + +std::int64_t APS5_VABI GetImageDecodeIID_nid_postfix(const char* extension, const std::uint32_t** iid) { + return StoreIid(&IidInfo::decode, extension, iid, "GetImageDecodeIID"); +} + +std::int64_t APS5_VABI PKAllocAligned_nid_postfix(void** memory, std::size_t size, std::size_t alignment) { + if (!memory) throw std::invalid_argument("PKAllocAligned requires an output pointer"); + *memory = nullptr; + if (alignment == 0) throw std::invalid_argument("PKAllocAligned: zero alignment divides by zero"); + constexpr auto maximum = std::numeric_limits::max(); + if (size > maximum - sizeof(void*) || alignment - 1 > maximum - sizeof(void*) - size) + throw std::length_error("PKAllocAligned: block size overflows size_t"); + const auto blockSize = size + sizeof(void*) + alignment - 1; + auto* original = static_cast(calloc_nid_postfix(1, blockSize)); + if (!original) return WmpErrOutOfMemory; + auto correction = alignment - reinterpret_cast(original) % alignment; + if (correction < sizeof(void*)) correction += alignment; + if (correction < sizeof(void*)) { + free_nid_postfix(original); + throw std::invalid_argument("PKAllocAligned: the alignment leaves no room for the original pointer"); + } + auto* aligned = original + correction; + std::memcpy(aligned - sizeof(void*), &original, sizeof(original)); + *memory = aligned; + return WmpErrSuccess; +} + +std::int64_t APS5_VABI PKFreeAligned_nid_postfix(void** memory) { + if (memory && *memory) { + void* original = nullptr; + std::memcpy(&original, static_cast(*memory) - sizeof(void*), sizeof(original)); + free_nid_postfix(original); + *memory = nullptr; + } + return WmpErrSuccess; +} + +std::int32_t APS5_VABI IDPEmpty_nid_postfix(void*) { + return 0; +} + +} diff --git a/core/libs/prx/libSceJxr/src/NativeHash.c b/core/libs/prx/libSceJxr/src/NativeHash.c new file mode 100644 index 0000000000..7ce57bf5a4 --- /dev/null +++ b/core/libs/prx/libSceJxr/src/NativeHash.c @@ -0,0 +1,6 @@ +#include "strcodec.h" +#include "JXRGlue.h" + +const void* JxrHashLookup_nid_no_patch(U8 hash) { + return GetPixelFormatFromHash(hash); +} diff --git a/core/libs/tests/GuestJxrGlue.cpp b/core/libs/tests/GuestJxrGlue.cpp new file mode 100644 index 0000000000..4b2961c1d5 --- /dev/null +++ b/core/libs/tests/GuestJxrGlue.cpp @@ -0,0 +1,199 @@ +#include "prx/libc/include/Api.hpp" +#include "prx/libSceJxr/include/Api.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include +#include +#include + +extern "C" const void* GetPixelFormatFromHash(unsigned char hash); + +namespace { + +void Require(bool value) { if (!value) std::abort(); } + +void* lastCalloc = nullptr; +std::size_t lastCount = 0; +std::size_t lastSize = 0; +void* lastFreed = nullptr; +int frees = 0; +bool failCalloc = false; + +void* APS5_VABI Allocate(std::size_t size) { return std::malloc(size); } +void APS5_VABI Free(void* pointer) { + if (pointer != nullptr) { + lastFreed = pointer; + ++frees; + } + std::free(pointer); +} +void* APS5_VABI Reallocate(void* pointer, std::size_t size) { return std::realloc(pointer, size); } +void* APS5_VABI Calloc(std::size_t count, std::size_t size) { + lastCount = count; + lastSize = size; + lastCalloc = failCalloc ? nullptr : std::calloc(count, size); + return lastCalloc; +} +void* APS5_VABI Align(std::size_t, std::size_t size) { return std::malloc(size); } +void* APS5_VABI Realign(void* pointer, std::size_t size, std::size_t) { return std::realloc(pointer, size); } +int APS5_VABI PosixAlign(void** pointer, std::size_t, std::size_t size) { + *pointer = std::malloc(size); + return *pointer ? 0 : 12; +} + +void InitializeHeap() { + alignas(16) static std::array process{}; + alignas(16) static std::array libc{}; + alignas(16) static std::array replacement{}; + const auto write = [](auto& data, std::size_t offset, auto value) { + std::memcpy(data.data() + offset, &value, sizeof(value)); + }; + write(process, 0, std::uint64_t{0x40}); + write(process, 8, std::uint32_t{0x4942524f}); + write(process, 0x38, libc.data()); + write(libc, 0, std::uint64_t{0x38}); + write(libc, 0x30, replacement.data()); + write(replacement, 0, std::uint64_t{0x78}); + write(replacement, 8, std::uint64_t{2}); + write(replacement, 0x20, reinterpret_cast(&Allocate)); + write(replacement, 0x28, reinterpret_cast(&Free)); + write(replacement, 0x30, reinterpret_cast(&Calloc)); + write(replacement, 0x38, reinterpret_cast(&Reallocate)); + write(replacement, 0x40, reinterpret_cast(&Align)); + write(replacement, 0x48, reinterpret_cast(&Realign)); + write(replacement, 0x50, reinterpret_cast(&PosixAlign)); + ApplicationHeapInitialize_nid_no_patch(process.data()); +} + +void TestPixelFormatHash() { + int found = 0; + for (int hash = 0; hash < 256; ++hash) { + const void* expected = GetPixelFormatFromHash(static_cast(hash)); + const void* actual = GetPixelFormatFromHash_nid_postfix(static_cast(hash)); + Require((expected == nullptr) == (actual == nullptr)); + if (!actual) continue; + ++found; + Require(std::memcmp(expected, actual, 16) == 0); + Require(static_cast(actual)[15] == hash); + } + Require(found > 0 && found < 256); + const std::uint8_t gray8[16] = {0x24, 0xc3, 0xdd, 0x6f, 0x03, 0x4e, 0xfe, 0x4b, + 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x08}; + const void* gray = GetPixelFormatFromHash_nid_postfix(0x08); + Require(gray && std::memcmp(gray, gray8, sizeof(gray8)) == 0); +} + +template +void RequireThrows(Function function) { + bool threw = false; + try { + function(); + } catch (const std::invalid_argument&) { + threw = true; + } catch (const std::length_error&) { + threw = true; + } + Require(threw); +} + +void TestImageIids() { + const std::uint32_t sentinel = 7; + const std::uint32_t* iid = &sentinel; + Require(GetImageEncodeIID_nid_postfix(".jxr", &iid) == 0 && *iid == 101); + Require(GetImageDecodeIID_nid_postfix(".jxr", &iid) == 0 && *iid == 201); + Require(GetImageEncodeIID_nid_postfix(".JXR", &iid) == 0 && *iid == 101); + Require(GetImageEncodeIID_nid_postfix(".jxr.tmp", &iid) == 0 && *iid == 101); + Require(GetImageEncodeIID_nid_postfix(".wdp", &iid) == 0 && *iid == 100); + Require(GetImageDecodeIID_nid_postfix(".WDP", &iid) == 0 && *iid == 201); + Require(GetImageEncodeIID_nid_postfix(".hdp", &iid) == 0 && *iid == 100); + Require(GetImageDecodeIID_nid_postfix(".Hdp", &iid) == 0 && *iid == 201); + const std::uint32_t* encode = nullptr; + const std::uint32_t* again = nullptr; + Require(GetImageEncodeIID_nid_postfix(".jxr", &encode) == 0); + Require(GetImageEncodeIID_nid_postfix(".jxr", &again) == 0 && encode == again); + for (const char* extension : {".png", ".jx", "", "jxr", ".j\xc3r", ".jpg"}) { + iid = &sentinel; + Require(GetImageEncodeIID_nid_postfix(extension, &iid) == -106 && iid == &sentinel); + Require(GetImageDecodeIID_nid_postfix(extension, &iid) == -106 && iid == &sentinel); + Require(GetImageEncodeIID_nid_postfix(extension, nullptr) == -106); + Require(GetImageDecodeIID_nid_postfix(extension, nullptr) == -106); + } + RequireThrows([] { GetImageEncodeIID_nid_postfix(".jxr", nullptr); }); + RequireThrows([] { GetImageDecodeIID_nid_postfix(".wdp", nullptr); }); + RequireThrows([] { + const std::uint32_t* out = nullptr; + GetImageEncodeIID_nid_postfix(nullptr, &out); + }); + RequireThrows([] { + const std::uint32_t* out = nullptr; + GetImageDecodeIID_nid_postfix(nullptr, &out); + }); +} + +void TestAlignedAllocation() { + for (std::size_t alignment : {std::size_t{4}, std::size_t{8}, std::size_t{16}, std::size_t{64}, + std::size_t{4096}}) { + for (std::size_t size : {std::size_t{0}, std::size_t{1}, std::size_t{100}}) { + void* memory = reinterpret_cast(std::uintptr_t{1}); + const int before = frees; + Require(PKAllocAligned_nid_postfix(&memory, size, alignment) == 0); + Require(memory != nullptr && lastCalloc != nullptr); + Require(lastCount == 1 && lastSize == size + sizeof(void*) + alignment - 1); + const auto address = reinterpret_cast(memory); + Require(address % alignment == 0); + const auto offset = address - reinterpret_cast(lastCalloc); + Require(offset >= sizeof(void*) && offset + size <= lastSize); + void* stored = nullptr; + std::memcpy(&stored, static_cast(memory) - sizeof(void*), sizeof(stored)); + Require(stored == lastCalloc); + for (std::size_t index = 0; index < size; ++index) + Require(static_cast(memory)[index] == 0); + void* const original = lastCalloc; + Require(PKFreeAligned_nid_postfix(&memory) == 0); + Require(memory == nullptr && frees == before + 1 && lastFreed == original); + } + } + void* memory = reinterpret_cast(std::uintptr_t{1}); + failCalloc = true; + Require(PKAllocAligned_nid_postfix(&memory, 32, 16) == -101 && memory == nullptr); + failCalloc = false; + const int before = frees; + Require(PKFreeAligned_nid_postfix(&memory) == 0 && frees == before); + Require(PKFreeAligned_nid_postfix(nullptr) == 0 && frees == before); + RequireThrows([] { PKAllocAligned_nid_postfix(nullptr, 32, 16); }); + memory = reinterpret_cast(std::uintptr_t{1}); + RequireThrows([&memory] { PKAllocAligned_nid_postfix(&memory, 32, 0); }); + Require(memory == nullptr); + for (std::size_t alignment : {std::size_t{1}, std::size_t{2}}) { + const int before = frees; + memory = reinterpret_cast(std::uintptr_t{1}); + RequireThrows([&memory, alignment] { PKAllocAligned_nid_postfix(&memory, 32, alignment); }); + Require(memory == nullptr && frees == before + 1 && lastFreed == lastCalloc); + } + constexpr auto maximum = std::numeric_limits::max(); + RequireThrows([] { + void* out = nullptr; + PKAllocAligned_nid_postfix(&out, maximum - 4, 16); + }); + RequireThrows([] { + void* out = nullptr; + PKAllocAligned_nid_postfix(&out, 16, maximum - 8); + }); +} + +} + +int main() { + InitializeHeap(); + TestPixelFormatHash(); + TestImageIids(); + TestAlignedAllocation(); + Require(IDPEmpty_nid_postfix(nullptr) == 0); + int codec = 0; + Require(IDPEmpty_nid_postfix(&codec) == 0 && codec == 0); + return 0; +} diff --git a/docs/dev/debt/libSceJxr.md b/docs/dev/debt/libSceJxr.md index 249c82055a..9f443a47dc 100644 --- a/docs/dev/debt/libSceJxr.md +++ b/docs/dev/debt/libSceJxr.md @@ -12,4 +12,8 @@ - [readIS and writeIS](../../../core/libs/prx/libSceJxr/Export.cpp) use the non-ARM, LP64 `BitIOInfo` and `WMPStream` ABI and packet algorithm from [Microsoft's JPEG XR reference implementation](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/image/sys/strcodec.c#L1090-L1177). Console structure layouts and execution remain unverified. Profiling callbacks are omitted as in the reference Unix build with `DISABLE_PERF_MEASUREMENT`; the codec argument is unused. Where the reference uses an invalid pointer, `std::invalid_argument` is thrown instead: a null bit state, stream or callback, a read-only read packet, and a null packet when a read copies its shadow or a successful write offsets it; a null packet still reaches the callback first. No codec is instantiated by these helpers; callers must supply the reference ring buffer and guest-ABI stream callbacks. -- The 132 additional exports in [Export.cpp](../../../core/libs/prx/libSceJxr/Export.cpp) are present in the pinned firmware 13.60 `libSceJxr` export inventory. Their parameter lists, return types and behavior are unverified. Parameterless `int` declarations are import-resolution placeholders, not verified ABI prototypes; every entry throws through `NotImplemented_nid_no_patch`. The `_nid_postfix` aliases keep the guest bindings distinct from private native helpers. No runtime functionality is implemented. +- [GetPixelFormatFromHash, GetImageEncodeIID and GetImageDecodeIID](../../../core/libs/prx/libSceJxr/src/GlueHelpers.cpp) follow jxrlib [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L256-L329); that the console's module is jxrlib with these prototypes is inferred from the export names, not checked against the console. The hash lookup calls the pinned jxrlib through a C bridge and, like `PixelFormatLookup`, borrows the bridge's read-only GUID instead of the module's. `.wdp` and `.hdp` encoding return a private `IID_PKImageUnsupported` (100), which the module does not export. A null extension, or a null output for a known extension, throws where jxrlib dereferences it. Closes when: the console's functions are disassembled or measured on a PS5 + +- [PKAllocAligned, PKFreeAligned and IDPEmpty](../../../core/libs/prx/libSceJxr/src/GlueHelpers.cpp) follow jxrlib [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L64-L102) and [strcodec.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/image/sys/strcodec.c#L239-L244), unverified on a console. That the allocation goes through the guest `calloc` and `free` is inferred, not checked. A null `PKAllocAligned` output, a zero alignment, a block size over `SIZE_MAX` and an alignment that leaves fewer than 8 bytes for the original pointer throw where jxrlib faults, divides by zero, wraps or writes before the block. Closes when: the console's functions are disassembled or measured on a PS5 + +- The 126 additional exports in [Export.cpp](../../../core/libs/prx/libSceJxr/Export.cpp) are present in the pinned firmware 13.60 `libSceJxr` export inventory. Their parameter lists, return types and behavior are unverified. Parameterless `int` declarations are import-resolution placeholders, not verified ABI prototypes; every entry throws through `NotImplemented_nid_no_patch`. The `_nid_postfix` aliases keep the guest bindings distinct from private native helpers. No runtime functionality is implemented. From 1c522615fe47482e4208b2425ad0fee80637457d Mon Sep 17 00:00:00 2001 From: Noxy-HNR Date: Mon, 2 Nov 2026 01:00:33 +0000 Subject: [PATCH 07/46] refactor(libSceJxr): build the glue helpers from the pinned jxrlib Address review: the pixel-format hash, image IID lookups, aligned allocation and IDPEmpty now call jxrlib's JXRGlue.c and strcodec.c from 3rdparty/jxrlib through a C bridge instead of adapted copies in core/. A dedicated build of JXRGlue.c routes its calloc and free to the guest heap; the C++ adapters keep only the guest ABI, argument checks and IID object mapping. Alignments below 8 are now rejected up front, since jxrlib would write the original pointer before the block. --- core/libs/prx/libSceJxr/CMakeLists.txt | 11 ++- core/libs/prx/libSceJxr/src/GlueHelpers.cpp | 104 ++++++++------------ core/libs/prx/libSceJxr/src/GuestHeap.h | 15 +++ core/libs/prx/libSceJxr/src/NativeGlue.c | 36 +++++++ core/libs/prx/libSceJxr/src/NativeHash.c | 6 -- core/libs/tests/GuestJxrGlue.cpp | 9 +- docs/dev/debt/libSceJxr.md | 4 +- 7 files changed, 108 insertions(+), 77 deletions(-) create mode 100644 core/libs/prx/libSceJxr/src/GuestHeap.h create mode 100644 core/libs/prx/libSceJxr/src/NativeGlue.c delete mode 100644 core/libs/prx/libSceJxr/src/NativeHash.c diff --git a/core/libs/prx/libSceJxr/CMakeLists.txt b/core/libs/prx/libSceJxr/CMakeLists.txt index d9eabc2728..b950f2b27d 100644 --- a/core/libs/prx/libSceJxr/CMakeLists.txt +++ b/core/libs/prx/libSceJxr/CMakeLists.txt @@ -1,9 +1,16 @@ -add_library(jxr_lookup_native STATIC src/NativeLookup.c src/NativeHash.c) +add_library(jxr_lookup_native STATIC src/NativeLookup.c) configure_jxr_native(jxr_lookup_native) target_include_directories(jxr_lookup_native PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(jxr_lookup_native PRIVATE jxr) add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp src/GlueHelpers.cpp src/PixelFormatLookup.cpp src/GuidExports.cpp) -target_link_libraries(libSceJxr PRIVATE jxr_lookup_native) +# Pinned jxrlib JXRGlue.c with its allocator routed to the guest heap, plus the C bridge +# GlueHelpers.cpp adapts; both sit in one archive ahead of jxr so this copy is the one linked. +add_library(jxr_glue_guest STATIC ${JXR_SOURCE_DIR}/jxrgluelib/JXRGlue.c src/NativeGlue.c) +configure_jxr_native(jxr_glue_guest) +set_source_files_properties(${JXR_SOURCE_DIR}/jxrgluelib/JXRGlue.c TARGET_DIRECTORY jxr_glue_guest + PROPERTIES COMPILE_OPTIONS "-include;${CMAKE_CURRENT_SOURCE_DIR}/src/GuestHeap.h") +target_link_libraries(jxr_glue_guest PRIVATE jxr) +target_link_libraries(libSceJxr PRIVATE jxr_glue_guest jxr_lookup_native) if(NOT APPLE) target_link_options(libSceJxr PRIVATE LINKER:--exclude-libs,ALL) endif() diff --git a/core/libs/prx/libSceJxr/src/GlueHelpers.cpp b/core/libs/prx/libSceJxr/src/GlueHelpers.cpp index d360c499b8..4f13804117 100644 --- a/core/libs/prx/libSceJxr/src/GlueHelpers.cpp +++ b/core/libs/prx/libSceJxr/src/GlueHelpers.cpp @@ -2,56 +2,49 @@ #include "prx/libc/include/Api.hpp" #include #include -#include #include #include #include -extern "C" const void* JxrHashLookup_nid_no_patch(std::uint8_t hash); +// Thin guest adapters over the pinned jxrlib built from 3rdparty/jxrlib (see NativeGlue.c and the +// jxr_glue_guest target): they check arguments jxrlib would fault on, map its IID values to the +// module's exported IID objects and route its allocator to the guest heap. + +extern "C" { +const void* JxrHashLookup_nid_no_patch(std::uint8_t hash); +std::int64_t JxrEncodeIid_nid_no_patch(const char* extension, std::uint32_t* value); +std::int64_t JxrDecodeIid_nid_no_patch(const char* extension, std::uint32_t* value); +std::int64_t JxrAllocAligned_nid_no_patch(void** memory, std::size_t size, std::size_t alignment); +std::int64_t JxrFreeAligned_nid_no_patch(void** memory); +std::int32_t JxrIdpEmpty_nid_no_patch(void* codec); + +void* JxrGuestCalloc_nid_no_patch(std::size_t count, std::size_t size) { + return calloc_nid_postfix(count, size); +} + +void JxrGuestFree_nid_no_patch(void* memory) { + free_nid_postfix(memory); +} +} namespace { -constexpr std::int64_t WmpErrSuccess = 0; -constexpr std::int64_t WmpErrOutOfMemory = -101; -constexpr std::int64_t WmpErrUnsupportedFormat = -106; constexpr std::uint32_t ImageUnsupported = 100; -struct IidInfo { - const char* extension; - const std::uint32_t* encode; - const std::uint32_t* decode; -}; - -int Lower(char value) { - const auto byte = static_cast(value); - return byte >= 'A' && byte <= 'Z' ? byte + ('a' - 'A') : byte; -} - -bool MatchesPrefix(const char* extension, const char* expected) { - auto count = std::strlen(expected); - for (; Lower(*extension) == Lower(*expected) && *extension && *expected && count; ++extension, ++expected, --count) {} - return count == 0; -} - -const IidInfo* FindIid(const char* extension, const char* function) { +std::int64_t StoreIid(std::int64_t (*lookup)(const char*, std::uint32_t*), const char* extension, + const std::uint32_t** iid, const char* function) { if (!extension) throw std::invalid_argument(std::string(function) + " requires an extension"); - static const IidInfo table[] = { - {".jxr", &JxrGuest_IID_PKImageWmpEncode_nid_no_patch, &JxrGuest_IID_PKImageWmpDecode_nid_no_patch}, - {".wdp", &ImageUnsupported, &JxrGuest_IID_PKImageWmpDecode_nid_no_patch}, - {".hdp", &ImageUnsupported, &JxrGuest_IID_PKImageWmpDecode_nid_no_patch}, - }; - for (const auto& entry : table) - if (MatchesPrefix(extension, entry.extension)) return &entry; - return nullptr; -} - -std::int64_t StoreIid(const std::uint32_t* IidInfo::*field, const char* extension, const std::uint32_t** iid, - const char* function) { - const auto* info = FindIid(extension, function); - if (!info) return WmpErrUnsupportedFormat; + std::uint32_t value = 0; + const auto result = lookup(extension, &value); + if (result < 0) return result; if (!iid) throw std::invalid_argument(std::string(function) + " requires an output for a known extension"); - *iid = info->*field; - return WmpErrSuccess; + switch (value) { + case 101: *iid = &JxrGuest_IID_PKImageWmpEncode_nid_no_patch; break; + case 201: *iid = &JxrGuest_IID_PKImageWmpDecode_nid_no_patch; break; + case ImageUnsupported: *iid = &ImageUnsupported; break; + default: throw std::logic_error(std::string(function) + ": jxrlib returned an unknown IID " + std::to_string(value) + " for " + extension); + } + return result; } } // namespace @@ -63,47 +56,30 @@ const void* APS5_VABI GetPixelFormatFromHash_nid_postfix(std::uint8_t hash) { } std::int64_t APS5_VABI GetImageEncodeIID_nid_postfix(const char* extension, const std::uint32_t** iid) { - return StoreIid(&IidInfo::encode, extension, iid, "GetImageEncodeIID"); + return StoreIid(JxrEncodeIid_nid_no_patch, extension, iid, "GetImageEncodeIID"); } std::int64_t APS5_VABI GetImageDecodeIID_nid_postfix(const char* extension, const std::uint32_t** iid) { - return StoreIid(&IidInfo::decode, extension, iid, "GetImageDecodeIID"); + return StoreIid(JxrDecodeIid_nid_no_patch, extension, iid, "GetImageDecodeIID"); } std::int64_t APS5_VABI PKAllocAligned_nid_postfix(void** memory, std::size_t size, std::size_t alignment) { if (!memory) throw std::invalid_argument("PKAllocAligned requires an output pointer"); *memory = nullptr; - if (alignment == 0) throw std::invalid_argument("PKAllocAligned: zero alignment divides by zero"); + if (alignment < sizeof(void*)) + throw std::invalid_argument("PKAllocAligned: alignments below 8 can leave no room for the original pointer"); constexpr auto maximum = std::numeric_limits::max(); if (size > maximum - sizeof(void*) || alignment - 1 > maximum - sizeof(void*) - size) throw std::length_error("PKAllocAligned: block size overflows size_t"); - const auto blockSize = size + sizeof(void*) + alignment - 1; - auto* original = static_cast(calloc_nid_postfix(1, blockSize)); - if (!original) return WmpErrOutOfMemory; - auto correction = alignment - reinterpret_cast(original) % alignment; - if (correction < sizeof(void*)) correction += alignment; - if (correction < sizeof(void*)) { - free_nid_postfix(original); - throw std::invalid_argument("PKAllocAligned: the alignment leaves no room for the original pointer"); - } - auto* aligned = original + correction; - std::memcpy(aligned - sizeof(void*), &original, sizeof(original)); - *memory = aligned; - return WmpErrSuccess; + return JxrAllocAligned_nid_no_patch(memory, size, alignment); } std::int64_t APS5_VABI PKFreeAligned_nid_postfix(void** memory) { - if (memory && *memory) { - void* original = nullptr; - std::memcpy(&original, static_cast(*memory) - sizeof(void*), sizeof(original)); - free_nid_postfix(original); - *memory = nullptr; - } - return WmpErrSuccess; + return JxrFreeAligned_nid_no_patch(memory); } -std::int32_t APS5_VABI IDPEmpty_nid_postfix(void*) { - return 0; +std::int32_t APS5_VABI IDPEmpty_nid_postfix(void* codec) { + return JxrIdpEmpty_nid_no_patch(codec); } } diff --git a/core/libs/prx/libSceJxr/src/GuestHeap.h b/core/libs/prx/libSceJxr/src/GuestHeap.h new file mode 100644 index 0000000000..0270e05b97 --- /dev/null +++ b/core/libs/prx/libSceJxr/src/GuestHeap.h @@ -0,0 +1,15 @@ +#ifndef CORE_LIBS_PRX_LIBSCEJXR_SRC_GUESTHEAP_H +#define CORE_LIBS_PRX_LIBSCEJXR_SRC_GUESTHEAP_H + +/* Force-included into the pinned jxrlib JXRGlue.c built for libSceJxr, so its PKAlloc, + PKFree, PKAllocAligned and PKFreeAligned allocate from the guest heap. */ +#include +#include + +void* JxrGuestCalloc_nid_no_patch(size_t count, size_t size); +void JxrGuestFree_nid_no_patch(void* memory); + +#define calloc JxrGuestCalloc_nid_no_patch +#define free JxrGuestFree_nid_no_patch + +#endif diff --git a/core/libs/prx/libSceJxr/src/NativeGlue.c b/core/libs/prx/libSceJxr/src/NativeGlue.c new file mode 100644 index 0000000000..7718a39ac6 --- /dev/null +++ b/core/libs/prx/libSceJxr/src/NativeGlue.c @@ -0,0 +1,36 @@ +#include "strcodec.h" +#include "JXRGlue.h" +#include + +const void* JxrHashLookup_nid_no_patch(U8 hash) { + return GetPixelFormatFromHash(hash); +} + +typedef ERR (*IidLookup)(const char*, const PKIID**); + +static int64_t StoreIid(IidLookup lookup, const char* extension, uint32_t* value) { + const PKIID* iid = NULL; + const ERR result = lookup(extension, &iid); + if (result >= 0 && iid) *value = *iid; + return result; +} + +int64_t JxrEncodeIid_nid_no_patch(const char* extension, uint32_t* value) { + return StoreIid(GetImageEncodeIID, extension, value); +} + +int64_t JxrDecodeIid_nid_no_patch(const char* extension, uint32_t* value) { + return StoreIid(GetImageDecodeIID, extension, value); +} + +int64_t JxrAllocAligned_nid_no_patch(void** memory, size_t size, size_t alignment) { + return PKAllocAligned(memory, size, alignment); +} + +int64_t JxrFreeAligned_nid_no_patch(void** memory) { + return PKFreeAligned(memory); +} + +int32_t JxrIdpEmpty_nid_no_patch(void* codec) { + return IDPEmpty((CWMImageStrCodec*)codec); +} diff --git a/core/libs/prx/libSceJxr/src/NativeHash.c b/core/libs/prx/libSceJxr/src/NativeHash.c deleted file mode 100644 index 7ce57bf5a4..0000000000 --- a/core/libs/prx/libSceJxr/src/NativeHash.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "strcodec.h" -#include "JXRGlue.h" - -const void* JxrHashLookup_nid_no_patch(U8 hash) { - return GetPixelFormatFromHash(hash); -} diff --git a/core/libs/tests/GuestJxrGlue.cpp b/core/libs/tests/GuestJxrGlue.cpp index 4b2961c1d5..7c4031147a 100644 --- a/core/libs/tests/GuestJxrGlue.cpp +++ b/core/libs/tests/GuestJxrGlue.cpp @@ -135,7 +135,7 @@ void TestImageIids() { } void TestAlignedAllocation() { - for (std::size_t alignment : {std::size_t{4}, std::size_t{8}, std::size_t{16}, std::size_t{64}, + for (std::size_t alignment : {std::size_t{8}, std::size_t{16}, std::size_t{24}, std::size_t{64}, std::size_t{4096}}) { for (std::size_t size : {std::size_t{0}, std::size_t{1}, std::size_t{100}}) { void* memory = reinterpret_cast(std::uintptr_t{1}); @@ -168,11 +168,14 @@ void TestAlignedAllocation() { memory = reinterpret_cast(std::uintptr_t{1}); RequireThrows([&memory] { PKAllocAligned_nid_postfix(&memory, 32, 0); }); Require(memory == nullptr); - for (std::size_t alignment : {std::size_t{1}, std::size_t{2}}) { + for (std::size_t alignment : {std::size_t{1}, std::size_t{2}, std::size_t{4}, std::size_t{7}}) { const int before = frees; + void* const previous = lastCalloc; + lastCalloc = nullptr; memory = reinterpret_cast(std::uintptr_t{1}); RequireThrows([&memory, alignment] { PKAllocAligned_nid_postfix(&memory, 32, alignment); }); - Require(memory == nullptr && frees == before + 1 && lastFreed == lastCalloc); + Require(memory == nullptr && frees == before && lastCalloc == nullptr); + lastCalloc = previous; } constexpr auto maximum = std::numeric_limits::max(); RequireThrows([] { diff --git a/docs/dev/debt/libSceJxr.md b/docs/dev/debt/libSceJxr.md index 9f443a47dc..98831643ea 100644 --- a/docs/dev/debt/libSceJxr.md +++ b/docs/dev/debt/libSceJxr.md @@ -12,8 +12,8 @@ - [readIS and writeIS](../../../core/libs/prx/libSceJxr/Export.cpp) use the non-ARM, LP64 `BitIOInfo` and `WMPStream` ABI and packet algorithm from [Microsoft's JPEG XR reference implementation](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/image/sys/strcodec.c#L1090-L1177). Console structure layouts and execution remain unverified. Profiling callbacks are omitted as in the reference Unix build with `DISABLE_PERF_MEASUREMENT`; the codec argument is unused. Where the reference uses an invalid pointer, `std::invalid_argument` is thrown instead: a null bit state, stream or callback, a read-only read packet, and a null packet when a read copies its shadow or a successful write offsets it; a null packet still reaches the callback first. No codec is instantiated by these helpers; callers must supply the reference ring buffer and guest-ABI stream callbacks. -- [GetPixelFormatFromHash, GetImageEncodeIID and GetImageDecodeIID](../../../core/libs/prx/libSceJxr/src/GlueHelpers.cpp) follow jxrlib [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L256-L329); that the console's module is jxrlib with these prototypes is inferred from the export names, not checked against the console. The hash lookup calls the pinned jxrlib through a C bridge and, like `PixelFormatLookup`, borrows the bridge's read-only GUID instead of the module's. `.wdp` and `.hdp` encoding return a private `IID_PKImageUnsupported` (100), which the module does not export. A null extension, or a null output for a known extension, throws where jxrlib dereferences it. Closes when: the console's functions are disassembled or measured on a PS5 +- [GetPixelFormatFromHash, GetImageEncodeIID and GetImageDecodeIID](../../../core/libs/prx/libSceJxr/src/GlueHelpers.cpp) call jxrlib's [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L256-L329), built from `3rdparty/jxrlib`, through a C bridge. That the console's module is jxrlib with these prototypes is inferred from the export names. The hash lookup, like `PixelFormatLookup`, borrows the bridge's read-only GUID. IID values map to the module's exported IID objects; `.wdp` and `.hdp` encoding return a private `IID_PKImageUnsupported` (100), which the module does not export. A null extension, or a null output for a known extension, throws where jxrlib dereferences it. Closes when: the console's functions are disassembled or measured on a PS5 -- [PKAllocAligned, PKFreeAligned and IDPEmpty](../../../core/libs/prx/libSceJxr/src/GlueHelpers.cpp) follow jxrlib [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L64-L102) and [strcodec.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/image/sys/strcodec.c#L239-L244), unverified on a console. That the allocation goes through the guest `calloc` and `free` is inferred, not checked. A null `PKAllocAligned` output, a zero alignment, a block size over `SIZE_MAX` and an alignment that leaves fewer than 8 bytes for the original pointer throw where jxrlib faults, divides by zero, wraps or writes before the block. Closes when: the console's functions are disassembled or measured on a PS5 +- [PKAllocAligned, PKFreeAligned and IDPEmpty](../../../core/libs/prx/libSceJxr/src/GlueHelpers.cpp) call jxrlib's [JXRGlue.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/jxrgluelib/JXRGlue.c#L64-L102) and [strcodec.c](https://github.com/4creators/jxrlib/blob/f7521879862b9085318e814c6157490dd9dbbdb4/image/sys/strcodec.c#L239-L244), with that `JXRGlue.c` copy's `calloc` and `free` routed to the guest heap; that the console allocates there is inferred. A null output, an alignment below 8 and a block size over `SIZE_MAX` throw before jxrlib can fault, write before the block or wrap. Closes when: the console's functions are disassembled or measured on a PS5 - The 126 additional exports in [Export.cpp](../../../core/libs/prx/libSceJxr/Export.cpp) are present in the pinned firmware 13.60 `libSceJxr` export inventory. Their parameter lists, return types and behavior are unverified. Parameterless `int` declarations are import-resolution placeholders, not verified ABI prototypes; every entry throws through `NotImplemented_nid_no_patch`. The `_nid_postfix` aliases keep the guest bindings distinct from private native helpers. No runtime functionality is implemented. From c7a34875b06a3f309354be4464c1dbab46c1abff Mon Sep 17 00:00:00 2001 From: Noxy-HNR Date: Mon, 2 Nov 2026 01:07:24 +0000 Subject: [PATCH 08/46] refactor(libSceJxr): drop explanatory comments from the jxrlib glue --- core/libs/prx/libSceJxr/CMakeLists.txt | 2 -- core/libs/prx/libSceJxr/src/GlueHelpers.cpp | 4 ---- core/libs/prx/libSceJxr/src/GuestHeap.h | 2 -- 3 files changed, 8 deletions(-) diff --git a/core/libs/prx/libSceJxr/CMakeLists.txt b/core/libs/prx/libSceJxr/CMakeLists.txt index b950f2b27d..616b7da9b6 100644 --- a/core/libs/prx/libSceJxr/CMakeLists.txt +++ b/core/libs/prx/libSceJxr/CMakeLists.txt @@ -3,8 +3,6 @@ configure_jxr_native(jxr_lookup_native) target_include_directories(jxr_lookup_native PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(jxr_lookup_native PRIVATE jxr) add_library(libSceJxr SHARED EXCLUDE_FROM_ALL Export.cpp src/GlueHelpers.cpp src/PixelFormatLookup.cpp src/GuidExports.cpp) -# Pinned jxrlib JXRGlue.c with its allocator routed to the guest heap, plus the C bridge -# GlueHelpers.cpp adapts; both sit in one archive ahead of jxr so this copy is the one linked. add_library(jxr_glue_guest STATIC ${JXR_SOURCE_DIR}/jxrgluelib/JXRGlue.c src/NativeGlue.c) configure_jxr_native(jxr_glue_guest) set_source_files_properties(${JXR_SOURCE_DIR}/jxrgluelib/JXRGlue.c TARGET_DIRECTORY jxr_glue_guest diff --git a/core/libs/prx/libSceJxr/src/GlueHelpers.cpp b/core/libs/prx/libSceJxr/src/GlueHelpers.cpp index 4f13804117..a328b6b262 100644 --- a/core/libs/prx/libSceJxr/src/GlueHelpers.cpp +++ b/core/libs/prx/libSceJxr/src/GlueHelpers.cpp @@ -6,10 +6,6 @@ #include #include -// Thin guest adapters over the pinned jxrlib built from 3rdparty/jxrlib (see NativeGlue.c and the -// jxr_glue_guest target): they check arguments jxrlib would fault on, map its IID values to the -// module's exported IID objects and route its allocator to the guest heap. - extern "C" { const void* JxrHashLookup_nid_no_patch(std::uint8_t hash); std::int64_t JxrEncodeIid_nid_no_patch(const char* extension, std::uint32_t* value); diff --git a/core/libs/prx/libSceJxr/src/GuestHeap.h b/core/libs/prx/libSceJxr/src/GuestHeap.h index 0270e05b97..777f12743a 100644 --- a/core/libs/prx/libSceJxr/src/GuestHeap.h +++ b/core/libs/prx/libSceJxr/src/GuestHeap.h @@ -1,8 +1,6 @@ #ifndef CORE_LIBS_PRX_LIBSCEJXR_SRC_GUESTHEAP_H #define CORE_LIBS_PRX_LIBSCEJXR_SRC_GUESTHEAP_H -/* Force-included into the pinned jxrlib JXRGlue.c built for libSceJxr, so its PKAlloc, - PKFree, PKAllocAligned and PKFreeAligned allocate from the guest heap. */ #include #include From 8ed02595bc799a5972f87c63637e7cb752832ae9 Mon Sep 17 00:00:00 2001 From: Eduardo Ruiz Date: Mon, 2 Nov 2026 02:41:34 +0100 Subject: [PATCH 09/46] feat(libSceContentSearch): implement the application queries Recover the signatures of sceContentSearchGetNumOfApplication, sceContentSearchGetApplicationLastUpdateId and sceContentSearchGetMyApplicationIndex from the 11.00 module wrappers and answer them from the empty host library: zero applications, update id 0 and application index 0. The wrappers establish the pointer/length pair checks, the 64-bit update-id output, the 32-bit index output and the initialization and null-pointer error order. sceContentSearchSearchApplication and the metadata and AV-event exports still throw. --- core/libs/prx/libSceContentSearch/Export.cpp | 21 ++++++++---- .../prx/libSceContentSearch/include/Api.hpp | 6 ++-- core/libs/tests/GuestContentSearch.cpp | 32 +++++++++++++++++-- docs/dev/debt/libSceContentSearch.md | 3 +- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceContentSearch/Export.cpp b/core/libs/prx/libSceContentSearch/Export.cpp index 13c17b161a..72a644817c 100644 --- a/core/libs/prx/libSceContentSearch/Export.cpp +++ b/core/libs/prx/libSceContentSearch/Export.cpp @@ -190,16 +190,25 @@ int APS5_VABI sceContentBinderStart() { int APS5_VABI sceContentBinderTerminate() { return static_cast(0x809D000F); } -int APS5_VABI sceContentSearchGetApplicationLastUpdateId() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceContentSearchGetApplicationLastUpdateId(std::int64_t* lastUpdateId) { + if (!g_initialized.load()) return ErrorNotInitialized; + if (lastUpdateId == nullptr) return ErrorInvalidArgument; + *lastUpdateId = LibraryUpdateId; return 0; } -int APS5_VABI sceContentSearchGetMyApplicationIndex() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceContentSearchGetMyApplicationIndex(std::int32_t* index) { + if (!g_initialized.load()) return ErrorNotInitialized; + if (index == nullptr) return ErrorInvalidArgument; + *index = 0; return 0; } -int APS5_VABI sceContentSearchGetNumOfApplication() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceContentSearchGetNumOfApplication(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, const ContentSearchContentOrderByCondition* orderByConditions, std::uint32_t orderByConditionsLength, std::int64_t* numOfApplication, std::int64_t* lastUpdateId) { + if (!g_initialized.load()) return ErrorNotInitialized; + if ((columnSet == nullptr) != (columnSetLength == 0) || (orderByConditions == nullptr) != (orderByConditionsLength == 0) || numOfApplication == nullptr || lastUpdateId == nullptr) return ErrorInvalidArgument; + ValidateColumnSets(__func__, columnSet, columnSetLength); + ValidateOrderBy(__func__, orderByConditions, orderByConditionsLength); + *numOfApplication = 0; + *lastUpdateId = LibraryUpdateId; return 0; } int APS5_VABI sceContentSearchSearchApplication() { diff --git a/core/libs/prx/libSceContentSearch/include/Api.hpp b/core/libs/prx/libSceContentSearch/include/Api.hpp index b54e8efb17..c648e0c87f 100644 --- a/core/libs/prx/libSceContentSearch/include/Api.hpp +++ b/core/libs/prx/libSceContentSearch/include/Api.hpp @@ -23,9 +23,9 @@ int APS5_VABI sceContentBinderReset(); int APS5_VABI sceContentBinderSearchDifference(); int APS5_VABI sceContentBinderStart(); int APS5_VABI sceContentBinderTerminate(); -int APS5_VABI sceContentSearchGetApplicationLastUpdateId(); -int APS5_VABI sceContentSearchGetMyApplicationIndex(); -int APS5_VABI sceContentSearchGetNumOfApplication(); +int APS5_VABI sceContentSearchGetApplicationLastUpdateId(std::int64_t* lastUpdateId); +int APS5_VABI sceContentSearchGetMyApplicationIndex(std::int32_t* index); +int APS5_VABI sceContentSearchGetNumOfApplication(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, const ContentSearchContentOrderByCondition* orderByConditions, std::uint32_t orderByConditionsLength, std::int64_t* numOfApplication, std::int64_t* lastUpdateId); int APS5_VABI sceContentSearchSearchApplication(); } diff --git a/core/libs/tests/GuestContentSearch.cpp b/core/libs/tests/GuestContentSearch.cpp index 7101d21c48..76c6660d85 100644 --- a/core/libs/tests/GuestContentSearch.cpp +++ b/core/libs/tests/GuestContentSearch.cpp @@ -49,7 +49,11 @@ int main() { std::int64_t size = -1; std::int64_t updateId = -1; std::int32_t metadataId = -1; + std::int32_t applicationIndex = -1; + Require(sceContentSearchGetApplicationLastUpdateId(&updateId) == ErrorNotInitialized); + Require(sceContentSearchGetMyApplicationIndex(&applicationIndex) == ErrorNotInitialized); + Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, &count, &updateId) == ErrorNotInitialized); Require(sceContentSearchGetContentLastUpdateId(&updateId) == ErrorNotInitialized); Require(sceContentSearchGetNumOfContent(&columnSet, 1, &count, &updateId) == ErrorNotInitialized); Require(sceContentSearchGetTotalContentSize(&columnSet, 1, &size, &updateId) == ErrorNotInitialized); @@ -92,8 +96,28 @@ int main() { updateId = -1; Require(sceContentSearchSearchContent(&columnSet, 1, &orderBy, 1, 0, 93, &count, infos, &updateId) == ErrorLimitTooBig); Require(count == -1 && updateId == -1); + updateId = -1; + Require(sceContentSearchGetApplicationLastUpdateId(&updateId) == 0 && updateId == 0); + applicationIndex = -1; + Require(sceContentSearchGetMyApplicationIndex(&applicationIndex) == 0 && applicationIndex == 0); + count = -1; + updateId = -1; + Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, &count, &updateId) == 0); + Require(count == 0 && updateId == 0); + count = -1; + updateId = -1; + Require(sceContentSearchGetNumOfApplication(nullptr, 0, nullptr, 0, &count, &updateId) == 0); + Require(count == 0 && updateId == 0); + count = -1; + updateId = -1; Require(sceContentSearchGetContentLastUpdateId(nullptr) == ErrorInvalidArgument); + Require(sceContentSearchGetApplicationLastUpdateId(nullptr) == ErrorInvalidArgument); + Require(sceContentSearchGetMyApplicationIndex(nullptr) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, nullptr, &updateId) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, &count, nullptr) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 0, &count, &updateId) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfApplication(&columnSet, 0, &orderBy, 1, &count, &updateId) == ErrorInvalidArgument); Require(sceContentSearchGetNumOfContent(&columnSet, 1, nullptr, &updateId) == ErrorInvalidArgument); Require(sceContentSearchGetNumOfContent(&columnSet, 1, &count, nullptr) == ErrorInvalidArgument); Require(sceContentSearchGetTotalContentSize(nullptr, 1, &size, &updateId) == ErrorInvalidArgument); @@ -113,6 +137,7 @@ int main() { ContentSearchContentColumn unknownColumn{8, 0, &photo, 2}; ContentSearchContentColumnSet unknownColumnSet{&unknownColumn, 1, 2}; Require(Throws([&] { sceContentSearchGetNumOfContent(&unknownColumnSet, 1, &count, &updateId); })); + Require(Throws([&] { sceContentSearchGetNumOfApplication(&unknownColumnSet, 1, &orderBy, 1, &count, &updateId); })); ContentSearchContentColumn missingValue{3, 0, nullptr, 2}; ContentSearchContentColumnSet missingValueSet{&missingValue, 1, 2}; Require(Throws([&] { sceContentSearchGetTotalContentSize(&missingValueSet, 1, &size, &updateId); })); @@ -121,6 +146,7 @@ int main() { Require(Throws([&] { sceContentSearchSearchContent(&unknownOperatorSet, 1, &orderBy, 1, 0, 2, &count, infos, &updateId); })); ContentSearchContentOrderByCondition unknownSort{3, 0}; Require(Throws([&] { sceContentSearchSearchContent(&columnSet, 1, &unknownSort, 1, 0, 2, &count, infos, &updateId); })); + Require(Throws([&] { sceContentSearchGetNumOfApplication(&columnSet, 1, &unknownSort, 1, &count, &updateId); })); Require(count == -1 && updateId == -1); Require(sceContentSearchSearchContent(nullptr, 0, nullptr, 0, 0, 0, nullptr, nullptr, nullptr) == ErrorInvalidArgument); Require(sceContentSearchSearchContent(nullptr, 0, nullptr, 0, 0, 93, nullptr, nullptr, nullptr) == ErrorLimitTooBig); @@ -141,6 +167,9 @@ int main() { Require(sceContentSearchSearchContent(&columnSet, 1, &orderBy, 1, 0, 2, &count, infos, &updateId) == ErrorNotInitialized); Require(sceContentSearchCloseMetadata(1) == ErrorNotInitialized); Require(sceContentSearchGetContentLastUpdateId(&updateId) == ErrorNotInitialized); + Require(sceContentSearchGetApplicationLastUpdateId(&updateId) == ErrorNotInitialized); + Require(sceContentSearchGetMyApplicationIndex(&applicationIndex) == ErrorNotInitialized); + Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, &count, &updateId) == ErrorNotInitialized); Require(updateId == -1); constexpr int binderResult = static_cast(0x809D000F); Require(sceContentBinderInitialize() == binderResult); @@ -152,8 +181,5 @@ int main() { Require(sceContentBinderEnd() == binderResult); Require(sceContentBinderTerminate() == binderResult); - Require(ThrowsRuntimeError([] { sceContentSearchGetApplicationLastUpdateId(); })); - Require(ThrowsRuntimeError([] { sceContentSearchGetMyApplicationIndex(); })); - Require(ThrowsRuntimeError([] { sceContentSearchGetNumOfApplication(); })); Require(ThrowsRuntimeError([] { sceContentSearchSearchApplication(); })); } diff --git a/docs/dev/debt/libSceContentSearch.md b/docs/dev/debt/libSceContentSearch.md index 8051fd4b39..7ee5185575 100644 --- a/docs/dev/debt/libSceContentSearch.md +++ b/docs/dev/debt/libSceContentSearch.md @@ -11,11 +11,12 @@ | `sceContentSearchEnableAvContentCreatedEvent` | `vlOqhFPOLDs` | | `sceContentSearchGetContentIdByAvContentCreatedEvent` | `2Dj56xvkS5M` | -- [sceContentBinderEnd, sceContentBinderGetNumOfDifference, sceContentBinderInitialize, sceContentBinderIsUpdated, sceContentBinderReset, sceContentBinderSearchDifference, sceContentBinderStart, sceContentBinderTerminate, sceContentSearchGetApplicationLastUpdateId, sceContentSearchGetMyApplicationIndex, sceContentSearchGetNumOfApplication and sceContentSearchSearchApplication](../../../core/libs/prx/libSceContentSearch/Export.cpp) (libSceContentSearch) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceContentSearch.c), but no credible prototypes were found in the cited sources. They are declared without parameters solely to resolve known imports and make no ABI claim. +- [sceContentBinderEnd, sceContentBinderGetNumOfDifference, sceContentBinderInitialize, sceContentBinderIsUpdated, sceContentBinderReset, sceContentBinderSearchDifference, sceContentBinderStart, sceContentBinderTerminate and sceContentSearchSearchApplication](../../../core/libs/prx/libSceContentSearch/Export.cpp) (libSceContentSearch) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceContentSearch.c), but no credible prototypes were found in the cited sources. They are declared without parameters solely to resolve known imports and make no ABI claim. ### Functional - [Content queries](../../../core/libs/prx/libSceContentSearch/Export.cpp) use an empty host library and always report update id 0; the console's initial update id is unverified. No content rows are produced. Every title is assumed to have search permission. Nested filter/sort validation is incomplete: empty nested column sets, null filter values and unknown enum values throw. Guest heap allocation and the platform-dependent minimum heap size are unimplemented. Initialization and outer pointer/count/limit validation follow the supplied module. +- [sceContentSearchGetNumOfApplication, sceContentSearchGetApplicationLastUpdateId and sceContentSearchGetMyApplicationIndex](../../../core/libs/prx/libSceContentSearch/Export.cpp) (libSceContentSearch) - signatures from the 11.00 module wrapper: `GetNumOfApplication` takes the column-set and order-by pairs plus the count and update-id outputs; `GetApplicationLastUpdateId` writes a 64-bit update id; `GetMyApplicationIndex` writes a 32-bit index, defaulting it to 0 before delegating. The host reports zero applications and update id 0, and index 0, because the host library has no application records; the console values are unverified. All three check initialization and null outputs first, and `GetNumOfApplication` validates the column-set and order-by contents as the content queries do. `sceContentSearchSearchApplication` still throws because its eleven-argument contract is not mapped. - [Metadata access](../../../core/libs/prx/libSceContentSearch/Export.cpp) cannot open metadata by path or content id because missing-content errors remain unresolved: past their argument checks, both opening calls go through `NotImplemented_nid_no_patch`. No metadata handles are issued. The field-info and value queries reject invalid outer pointers, then go through `NotImplemented_nid_no_patch`, as no metadata id is ever opened and the console's error for one depends on the requested field; closing an unopened handle returns the confirmed invalid-argument error. ### Additional callable exports From 50327e7c8c58014c0f549739963bd523d656e79a Mon Sep 17 00:00:00 2001 From: BunnyFl <166477956+BunnyFl@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:39:59 +0100 Subject: [PATCH 10/46] feat(libSceVideoOut): answer seven inventory calls as firmware 13.42 does sceVideoOutAddBufferHdrPrivilege, sceVideoOutRazorRecallCallbacks and sceVideoOutSysUpdateRenderingMode return 0, sceVideoOutCurveSamplePointsCalc_ returns 0x8029001C, sceVideoOutDriverGetLatestEopHandle 0x802900FF and sceVideoOutSysGetMonitorInfo_ -1, and sceVideoOutSysModeIsVr returns bit 1 of the dword at offset 0x20 of its argument, as firmware 13.42's libSceVideoOut does (read to the return, disassembly only, not measured on a console). Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceVideoOut/CMakeLists.txt | 11 ++++++ .../prx/libSceVideoOut/InventoryExports.cpp | 25 +++++++++----- .../include/InventoryExports.hpp | 2 +- .../tests/GuestVideoOutFirmwareAnswers.cpp | 34 +++++++++++++++++++ docs/dev/debt/libSceVideoOut.md | 11 ++---- 5 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 core/libs/tests/GuestVideoOutFirmwareAnswers.cpp diff --git a/core/libs/prx/libSceVideoOut/CMakeLists.txt b/core/libs/prx/libSceVideoOut/CMakeLists.txt index 0f2e75f24b..e485095689 100644 --- a/core/libs/prx/libSceVideoOut/CMakeLists.txt +++ b/core/libs/prx/libSceVideoOut/CMakeLists.txt @@ -81,6 +81,17 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() + add_test_executable(guest_video_out_firmware_answers_tests ${LIBS_INCLUDE_DIR}/tests/GuestVideoOutFirmwareAnswers.cpp) + target_include_directories(guest_video_out_firmware_answers_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_video_out_firmware_answers_tests PRIVATE libSceVideoOut libkernel libc) + configure_windows_unwind(guest_video_out_firmware_answers_tests) + add_test(NAME guest_video_out_firmware_answers COMMAND guest_video_out_firmware_answers_tests) + set_tests_properties(guest_video_out_firmware_answers PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_video_out_firmware_answers PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() + add_test_executable(guest_video_out_high_frame_rate_tests ${LIBS_INCLUDE_DIR}/tests/GuestVideoOutHighFrameRate.cpp) target_include_directories(guest_video_out_high_frame_rate_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_video_out_high_frame_rate_tests PRIVATE libSceVideoOut libSceAgcDriver libkernel libc) diff --git a/core/libs/prx/libSceVideoOut/InventoryExports.cpp b/core/libs/prx/libSceVideoOut/InventoryExports.cpp index e298727bb5..b52f34735b 100644 --- a/core/libs/prx/libSceVideoOut/InventoryExports.cpp +++ b/core/libs/prx/libSceVideoOut/InventoryExports.cpp @@ -2,6 +2,13 @@ #include "prx/libc/include/general/ExportMacros.hpp" #include "prx/libSceVideoOut/include/InventoryExports.hpp" #include "prx/libc/include/General.hpp" +#include +#include + +namespace { +constexpr int VideoOutErrorCurveSamplePoints = static_cast(0x8029001Cu); +constexpr int VideoOutErrorLatestEopHandle = static_cast(0x802900FFu); +} extern "C" { @@ -18,7 +25,7 @@ int APS5_VABI sceVideoOutAddBufferAnisoPrivilege() { } int APS5_VABI sceVideoOutAddBufferHdrPrivilege() { - NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceVideoOutAddBufferYccPrivilege() { @@ -90,7 +97,7 @@ int APS5_VABI sceVideoOutCursorSetPositionStereo() { } int APS5_VABI sceVideoOutCurveSamplePointsCalc_() { - NotImplemented_nid_no_patch(__func__); + return VideoOutErrorCurveSamplePoints; } int APS5_VABI sceVideoOutDebugAddVrrPrivilege() { @@ -154,7 +161,7 @@ int APS5_VABI sceVideoOutDriverGetFinishedEopHandle() { } int APS5_VABI sceVideoOutDriverGetLatestEopHandle() { - NotImplemented_nid_no_patch(__func__); + return VideoOutErrorLatestEopHandle; } int APS5_VABI sceVideoOutDriverIncrementBufferLabel() { @@ -254,7 +261,7 @@ int APS5_VABI sceVideoOutRazorAddCallback() { } int APS5_VABI sceVideoOutRazorRecallCallbacks() { - NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceVideoOutRazorRemoveCallback() { @@ -382,7 +389,7 @@ int APS5_VABI sceVideoOutSysGetKsvList() { } int APS5_VABI sceVideoOutSysGetMonitorInfo_() { - NotImplemented_nid_no_patch(__func__); + return -1; } int APS5_VABI sceVideoOutSysGetOutputStatus() { @@ -461,8 +468,10 @@ int APS5_VABI sceVideoOutSysModeIsVrNative() { NotImplemented_nid_no_patch(__func__); } -int APS5_VABI sceVideoOutSysModeIsVr() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceVideoOutSysModeIsVr(const void* mode) { + std::uint32_t flags; + std::memcpy(&flags, static_cast(mode) + 0x20, sizeof(flags)); + return static_cast((flags >> 1) & 1u); } int APS5_VABI sceVideoOutSysOpenInternal() { @@ -522,7 +531,7 @@ int APS5_VABI sceVideoOutSysUpdatePrivilege() { } int APS5_VABI sceVideoOutSysUpdateRenderingMode() { - NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceVideoOutUnlockBuffer() { diff --git a/core/libs/prx/libSceVideoOut/include/InventoryExports.hpp b/core/libs/prx/libSceVideoOut/include/InventoryExports.hpp index 5c65f81866..6b9f6feedc 100644 --- a/core/libs/prx/libSceVideoOut/include/InventoryExports.hpp +++ b/core/libs/prx/libSceVideoOut/include/InventoryExports.hpp @@ -118,7 +118,7 @@ int APS5_VABI sceVideoOutSysModeIsHdr(); int APS5_VABI sceVideoOutSysModeIsSystemDefault(); int APS5_VABI sceVideoOutSysModeIsVrLegacy(); int APS5_VABI sceVideoOutSysModeIsVrNative(); -int APS5_VABI sceVideoOutSysModeIsVr(); +int APS5_VABI sceVideoOutSysModeIsVr(const void* mode); int APS5_VABI sceVideoOutSysOpenInternal(); int APS5_VABI sceVideoOutSysOpenVrNative(); int APS5_VABI sceVideoOutSysReadCrc64(); diff --git a/core/libs/tests/GuestVideoOutFirmwareAnswers.cpp b/core/libs/tests/GuestVideoOutFirmwareAnswers.cpp new file mode 100644 index 0000000000..c463fc5d09 --- /dev/null +++ b/core/libs/tests/GuestVideoOutFirmwareAnswers.cpp @@ -0,0 +1,34 @@ +#include "prx/libSceVideoOut/include/InventoryExports.hpp" +#include +#include +#include +#include +#include + +static void Require(bool value, const char* message) { + if (!value) { + std::fprintf(stderr, "%s\n", message); + std::abort(); + } +} + +static int ModeIsVr(std::uint32_t flags, std::uint8_t fill) { + std::array mode; + mode.fill(fill); + std::memcpy(mode.data() + 0x20, &flags, sizeof(flags)); + return sceVideoOutSysModeIsVr(mode.data()); +} + +int main() { + Require(sceVideoOutAddBufferHdrPrivilege() == 0, "sceVideoOutAddBufferHdrPrivilege did not return 0"); + Require(sceVideoOutRazorRecallCallbacks() == 0, "sceVideoOutRazorRecallCallbacks did not return 0"); + Require(sceVideoOutSysUpdateRenderingMode() == 0, "sceVideoOutSysUpdateRenderingMode did not return 0"); + Require(sceVideoOutCurveSamplePointsCalc_() == static_cast(0x8029001Cu), "sceVideoOutCurveSamplePointsCalc_ did not return 0x8029001C"); + Require(sceVideoOutDriverGetLatestEopHandle() == static_cast(0x802900FFu), "sceVideoOutDriverGetLatestEopHandle did not return 0x802900FF"); + Require(sceVideoOutSysGetMonitorInfo_() == -1, "sceVideoOutSysGetMonitorInfo_ did not return -1"); + Require(ModeIsVr(0x2u, 0) == 1, "bit 1 of the dword at 0x20 did not report VR"); + Require(ModeIsVr(0xFFFFFFFFu, 0xFF) == 1, "a mode with every bit set did not report VR"); + Require(ModeIsVr(0xFFFFFFFDu, 0xFF) == 0, "the other bits of the dword at 0x20 reported VR"); + Require(ModeIsVr(0u, 0xFF) == 0, "bytes outside the dword at 0x20 reported VR"); + return 0; +} diff --git a/docs/dev/debt/libSceVideoOut.md b/docs/dev/debt/libSceVideoOut.md index 1439e1d254..63187a8771 100644 --- a/docs/dev/debt/libSceVideoOut.md +++ b/docs/dev/debt/libSceVideoOut.md @@ -56,14 +56,13 @@ - The [revision-pinned identity inventory](https://gist.github.com/kyon-eth/ea0a096e6231dbe7b98b5a308e2ac91e/00f8422a1ae71e32816d72ae579c2f2d097f63a0) records module, logical library, symbol type and NIDs for [InventoryExports.cpp](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp); it is a metadata summary rather than independent verification. Some NIDs belong to multiple logical libraries within the module. -- [Additional callable exports](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp) - 182 entries observed in a 3.20-labelled image and a later 13.60-labelled image; 107 match the public 3.20 catalogue and 75 do not. These are import-resolution declarations only. Neither image label authenticates a retail firmware version. Parameter lists, return types and behavior are unverified; parameterless `int` declarations are placeholders, not verified ABI contracts. Every entry throws through `NotImplemented_nid_no_patch`. Unidentified names use literal NID aliases. No runtime functionality is implemented; data exports need separate analysis. Identified candidate names match their NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). +- [Additional callable exports](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp) - 175 entries observed in a 3.20-labelled image and a later 13.60-labelled image; of the original 182, 107 match the public 3.20 catalogue and 75 do not. These are import-resolution declarations only. Neither image label authenticates a retail firmware version. Parameter lists, return types and behavior are unverified; parameterless `int` declarations are placeholders, not verified ABI contracts. Every entry throws through `NotImplemented_nid_no_patch`. Unidentified names use literal NID aliases. No runtime functionality is implemented; data exports need separate analysis. Identified candidate names match their NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). | Name | NID | | --- | --- | | `sceVideoOutAddBuffer` | `4iy9hG9Du1s` | | `sceVideoOutAddBuffer4k2kPrivilege` | `twrSaoZ9cJs` | | `sceVideoOutAddBufferAnisoPrivilege` | `lm+1oyWKwy0` | -| `sceVideoOutAddBufferHdrPrivilege` | `VAlir52gpQI` | | `sceVideoOutAddBufferYccPrivilege` | `D3s8DoEnuR4` | | `sceVideoOutAddDisplayPositionEvent` | `Cxb2w82t7lw` | | `sceVideoOutAddDriver` | `HtwSd4H2Tws` | @@ -81,7 +80,6 @@ | `sceVideoOutControlHdcpEncryption` | `M-JkRj7X+No` | | `sceVideoOutCursorIsUpdatePending` | `HibOtezzT6M` | | `sceVideoOutCursorSetPositionStereo` | `735GfVO9v2s` | -| `sceVideoOutCurveSamplePointsCalc_` | `kazBRXPqR7c` | | `sceVideoOutDebugAddVrrPrivilege` | `rAYR4zzw414` | | `sceVideoOutDebugBlankPipe` | `-9YA0lVAq+U` | | `sceVideoOutDebugReadCrc` | `4E8qvHf7Cj0` | @@ -97,7 +95,6 @@ | `sceVideoOutDriverGetBufferLabelAddress` | `NGyEl5aT4fc` | | `sceVideoOutDriverGetEopHandle` | `P1bIHoga4jE` | | `sceVideoOutDriverGetFinishedEopHandle` | `G6peSSDP4iA` | -| `sceVideoOutDriverGetLatestEopHandle` | `GL8+3RHW5vk` | | `sceVideoOutDriverIncrementBufferLabel` | `Ztx4b2LhRDk` | | `sceVideoOutDriverResetBufferLabel` | `soAIjspgdt8` | | `sceVideoOutDriverTriggerEvent` | `OHnnkrUcShM` | @@ -122,7 +119,6 @@ | `sceVideoOutHdmiMonitorInfoIsSupportedVideoOutMode_` | `WBZrUaQY+EU` | | `sceVideoOutLockBuffer` | `7ifZFxWJ9Q4` | | `sceVideoOutRazorAddCallback` | `ZX7C79uOujU` | -| `sceVideoOutRazorRecallCallbacks` | `hGcTks-qqIU` | | `sceVideoOutRazorRemoveCallback` | `zUDW7kIFclI` | | `sceVideoOutRegisterBufferAttribute` | `EJQifjOp8s4` | | `sceVideoOutRegisterStereoBuffers` | `lCTCOogRbk0` | @@ -154,7 +150,6 @@ | `sceVideoOutSysGetDeviceInfo` | `2sgenMqwczM` | | `sceVideoOutSysGetFlipStatus` | `21sS8EU45cw` | | `sceVideoOutSysGetKsvList` | `Ur8iOIm0tyE` | -| `sceVideoOutSysGetMonitorInfo_` | `vMLWuJckGv0` | | `sceVideoOutSysGetOutputStatus` | `Ip78YCQUrTo` | | `sceVideoOutSysGetPipelineStatus` | `dFhciCfO31s` | | `sceVideoOutSysGetResolutionStatus2` | `qLDCAl8ygCw` | @@ -174,7 +169,6 @@ | `sceVideoOutSysModeIsSystemDefault` | `5Y8uUbGMio0` | | `sceVideoOutSysModeIsVrLegacy` | `zecl8Gox+P0` | | `sceVideoOutSysModeIsVrNative` | `SgA2MtojPto` | -| `sceVideoOutSysModeIsVr` | `QEiuHxjungc` | | `sceVideoOutSysOpenInternal` | `kP4l+nyBjRA` | | `sceVideoOutSysOpenVrNative` | `WUU+ufqgLJg` | | `sceVideoOutSysReadCrc64` | `eBvoaLwfBBQ` | @@ -189,7 +183,6 @@ | `sceVideoOutSysSetOverscanRatio` | `nMbAqI1ty8w` | | `sceVideoOutSysUpdateDisplayParameter` | `UFDoqv4XXC0` | | `sceVideoOutSysUpdatePrivilege` | `Jl91AhxKc0w` | -| `sceVideoOutSysUpdateRenderingMode` | `4G37EIMMD+Q` | | `sceVideoOutUnlockBuffer` | `amghnA-Ev5s` | | `sceVideoOutUnregisterBufferAttribute` | `SRQSpP4QK5U` | | `Unknown` | `-5AIEU9+11g` | @@ -243,6 +236,8 @@ | `Unknown` | `yoK0AjkkBkI` | | `Unknown` | `ys239q8VgXY` | +- [Firmware 13.42 answers](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp) (libSceVideoOut) - `sceVideoOutAddBufferHdrPrivilege`, `sceVideoOutRazorRecallCallbacks` and `sceVideoOutSysUpdateRenderingMode` return 0, `sceVideoOutCurveSamplePointsCalc_` returns 0x8029001C, `sceVideoOutDriverGetLatestEopHandle` 0x802900FF and `sceVideoOutSysGetMonitorInfo_` -1, and `sceVideoOutSysModeIsVr` returns bit 1 of the dword at offset 0x20 of its argument, as firmware 13.42's libSceVideoOut does: read to the return, disassembly only, not measured on a console. Their parameter lists are unverified beyond what the code reads; the layout of the structure `sceVideoOutSysModeIsVr` reads is unknown apart from that dword + - [sceVideoOutSetConfigurationMode](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp): the name hashes to NID `N1+j0RymMUQ`, a function export in both older and later version-labelled images. Its signature and behavior are unverified; this import-resolution declaration throws. - [Older callable NIDs](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp): 7 unidentified exports have unverified names, signatures and behavior. These are throwing import-resolution placeholders; no functionality is implemented. No matching NIDs were found in the later module inventory. Six use the primary library qualifier and one uses the nosubmission qualifier; qualified guest import resolution is unverified. From 072371afe905abbb8a1aa0296d43c7e67cfffe22 Mon Sep 17 00:00:00 2001 From: BunnyFl <166477956+BunnyFl@users.noreply.github.com> Date: Mon, 2 Nov 2026 03:56:22 +0100 Subject: [PATCH 11/46] docs(libSceVideoOut): record that sceVideoOutSysModeIsVr does not check its pointer Co-Authored-By: Claude Opus 5.5 --- docs/dev/debt/libSceVideoOut.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceVideoOut.md b/docs/dev/debt/libSceVideoOut.md index 63187a8771..84cc0c64ec 100644 --- a/docs/dev/debt/libSceVideoOut.md +++ b/docs/dev/debt/libSceVideoOut.md @@ -236,7 +236,7 @@ | `Unknown` | `yoK0AjkkBkI` | | `Unknown` | `ys239q8VgXY` | -- [Firmware 13.42 answers](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp) (libSceVideoOut) - `sceVideoOutAddBufferHdrPrivilege`, `sceVideoOutRazorRecallCallbacks` and `sceVideoOutSysUpdateRenderingMode` return 0, `sceVideoOutCurveSamplePointsCalc_` returns 0x8029001C, `sceVideoOutDriverGetLatestEopHandle` 0x802900FF and `sceVideoOutSysGetMonitorInfo_` -1, and `sceVideoOutSysModeIsVr` returns bit 1 of the dword at offset 0x20 of its argument, as firmware 13.42's libSceVideoOut does: read to the return, disassembly only, not measured on a console. Their parameter lists are unverified beyond what the code reads; the layout of the structure `sceVideoOutSysModeIsVr` reads is unknown apart from that dword +- [Firmware 13.42 answers](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp) (libSceVideoOut) - `sceVideoOutAddBufferHdrPrivilege`, `sceVideoOutRazorRecallCallbacks` and `sceVideoOutSysUpdateRenderingMode` return 0, `sceVideoOutCurveSamplePointsCalc_` returns 0x8029001C, `sceVideoOutDriverGetLatestEopHandle` 0x802900FF and `sceVideoOutSysGetMonitorInfo_` -1, and `sceVideoOutSysModeIsVr` returns bit 1 of the dword at offset 0x20 of its argument, as firmware 13.42's libSceVideoOut does: read to the return, disassembly only, not measured on a console. Their parameter lists are unverified beyond what the code reads; the layout of the structure `sceVideoOutSysModeIsVr` reads is unknown apart from that dword. The module reads that dword without checking the pointer, so a null argument faults on the console as it does here - [sceVideoOutSetConfigurationMode](../../../core/libs/prx/libSceVideoOut/InventoryExports.cpp): the name hashes to NID `N1+j0RymMUQ`, a function export in both older and later version-labelled images. Its signature and behavior are unverified; this import-resolution declaration throws. From bbfc44a57c0cbc2f710643ce6e11a90f79477e49 Mon Sep 17 00:00:00 2001 From: BunnyFl <166477956+BunnyFl@users.noreply.github.com> Date: Mon, 2 Nov 2026 04:02:49 +0100 Subject: [PATCH 12/46] feat(libSceLibcInternal): answer _Daysto, _FXp_getw and _Isdst as firmware 13.42 does _Isdst returns -1. _Daysto(year, month) adds the leap days before the year to the common or leap days-before-month table, as Dinkumware's _Daysto does, and throws for a month outside the twelve table entries the module would index past. _FXp_getw sums up to four float parts as Dinkumware's _Xp_getw does. Read to the return from firmware 13.42's libSceLibcInternal, disassembly only, not measured on a console. Co-Authored-By: Claude Opus 5.5 --- .../prx/libSceLibcInternal/CMakeLists.txt | 13 ++++ core/libs/prx/libSceLibcInternal/Export.cpp | 27 +++++--- .../include/MathInternals.hpp | 2 +- .../include/RuntimeInternals.hpp | 2 +- .../GuestLibcInternalFirmwareAnswers.cpp | 65 +++++++++++++++++++ docs/dev/debt/libSceLibcInternal.md | 7 +- 6 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp diff --git a/core/libs/prx/libSceLibcInternal/CMakeLists.txt b/core/libs/prx/libSceLibcInternal/CMakeLists.txt index 2655d73c0b..ef2bf4ca52 100644 --- a/core/libs/prx/libSceLibcInternal/CMakeLists.txt +++ b/core/libs/prx/libSceLibcInternal/CMakeLists.txt @@ -222,3 +222,16 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() endif() + +if(BUILD_TESTING) + add_test_executable(guest_libc_internal_firmware_answers_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibcInternalFirmwareAnswers.cpp) + target_include_directories(guest_libc_internal_firmware_answers_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_libc_internal_firmware_answers_tests PRIVATE libSceLibcInternal libkernel libc) + configure_windows_unwind(guest_libc_internal_firmware_answers_tests) + add_test(NAME guest_libc_internal_firmware_answers COMMAND guest_libc_internal_firmware_answers_tests) + set_tests_properties(guest_libc_internal_firmware_answers PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_libc_internal_firmware_answers PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceLibcInternal/Export.cpp b/core/libs/prx/libSceLibcInternal/Export.cpp index 628e648c33..51ae83331a 100644 --- a/core/libs/prx/libSceLibcInternal/Export.cpp +++ b/core/libs/prx/libSceLibcInternal/Export.cpp @@ -1129,9 +1129,15 @@ int APS5_VABI _Closreg_nid_postfix() { return 0; } -int APS5_VABI _Daysto_nid_postfix() { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI _Daysto_nid_postfix(int year, int month) { + static constexpr short DaysBeforeMonth[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + static constexpr short LeapDaysBeforeMonth[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; + if (month < 0 || month >= 12) throw std::out_of_range("_Daysto: the month index is outside the twelve-entry tables"); + int leapDays = 0; + if (year > 0) leapDays = (year - 1) / 4; + else if (year <= -4) leapDays = 1 + (4 - year) / 4; + const bool common = (year & 3) != 0 || year == 0; + return leapDays + (common ? DaysBeforeMonth[month] : LeapDaysBeforeMonth[month]); } int APS5_VABI _Deletegloballocale_nid_postfix() { @@ -1241,9 +1247,15 @@ int APS5_VABI _FXp_addx_nid_postfix() { return 0; } -int APS5_VABI _FXp_getw_nid_postfix() { - NotImplemented_nid_no_patch(__func__); - return 0; +float APS5_VABI _FXp_getw_nid_postfix(const float* parts, int count) { + if (count == 0) return 0.0f; + if (count == 1 || parts[0] == 0.0f || parts[1] == 0.0f) return parts[0]; + if (count == 2 || parts[2] == 0.0f) return parts[0] + parts[1]; + const float head = parts[0] + parts[1]; + float tail = parts[2]; + if (count >= 4) tail += parts[3]; + if (head - parts[0] == parts[1]) return head + tail; + return parts[0] + (parts[1] + tail); } int APS5_VABI _FXp_invx_nid_postfix() { @@ -1433,8 +1445,7 @@ int APS5_VABI sceLibcInternalUnknown_wUa_oPQvFZ0_nid_no_patch() { APS5_EXPORT("wUa+oPQvFZ0", sceLibcInternalUnknown_wUa_oPQvFZ0_nid_no_patch); int APS5_VABI _Isdst_nid_postfix() { - NotImplemented_nid_no_patch(__func__); - return 0; + return -1; } int APS5_VABI _LAtan_nid_postfix() { diff --git a/core/libs/prx/libSceLibcInternal/include/MathInternals.hpp b/core/libs/prx/libSceLibcInternal/include/MathInternals.hpp index d706b9c36f..508e2c90dd 100644 --- a/core/libs/prx/libSceLibcInternal/include/MathInternals.hpp +++ b/core/libs/prx/libSceLibcInternal/include/MathInternals.hpp @@ -16,7 +16,7 @@ int APS5_VABI _FTan_nid_postfix(); int APS5_VABI _FTgamma_nid_postfix(); int APS5_VABI _FXp_addh_nid_postfix(); int APS5_VABI _FXp_addx_nid_postfix(); -int APS5_VABI _FXp_getw_nid_postfix(); +float APS5_VABI _FXp_getw_nid_postfix(const float* parts, int count); int APS5_VABI _FXp_invx_nid_postfix(); int APS5_VABI _FXp_ldexpx_nid_postfix(); int APS5_VABI _FXp_movx_nid_postfix(); diff --git a/core/libs/prx/libSceLibcInternal/include/RuntimeInternals.hpp b/core/libs/prx/libSceLibcInternal/include/RuntimeInternals.hpp index 4d3634a435..b3a225156c 100644 --- a/core/libs/prx/libSceLibcInternal/include/RuntimeInternals.hpp +++ b/core/libs/prx/libSceLibcInternal/include/RuntimeInternals.hpp @@ -11,7 +11,7 @@ int APS5_VABI _CStrxfrm_nid_postfix(); int APS5_VABI _CWcsxfrm_nid_postfix(); int APS5_VABI _Call_onceEx_nid_postfix(); int APS5_VABI _Closreg_nid_postfix(); -int APS5_VABI _Daysto_nid_postfix(); +int APS5_VABI _Daysto_nid_postfix(int year, int month); int APS5_VABI _Deletegloballocale_nid_postfix(); int APS5_VABI _Do_call_nid_postfix(); int APS5_VABI _Fac_tidy_nid_postfix(); diff --git a/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp b/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp new file mode 100644 index 0000000000..43dd6300d9 --- /dev/null +++ b/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp @@ -0,0 +1,65 @@ +#include "prx/libSceLibcInternal/include/MathInternals.hpp" +#include "prx/libSceLibcInternal/include/RuntimeInternals.hpp" +#include +#include +#include +#include + +static void Require(bool value, const char* message) { + if (!value) { + std::fprintf(stderr, "%s\n", message); + std::abort(); + } +} + +static bool Throws(int year, int month) { + try { + _Daysto_nid_postfix(year, month); + } catch (const std::out_of_range&) { + return true; + } + return false; +} + +static void TestDaysto() { + Require(_Daysto_nid_postfix(1, 0) == 0, "_Daysto(1, 0)"); + Require(_Daysto_nid_postfix(1, 1) == 31, "_Daysto(1, 1)"); + Require(_Daysto_nid_postfix(4, 2) == 60, "a positive multiple of four uses the leap table"); + Require(_Daysto_nid_postfix(5, 2) == 60, "year 5 adds one leap day to the common table"); + Require(_Daysto_nid_postfix(0, 2) == 59, "year 0 uses the common table with no leap days"); + Require(_Daysto_nid_postfix(-1, 2) == 59, "years -1 to -3 add no leap days"); + Require(_Daysto_nid_postfix(-3, 11) == 334, "_Daysto(-3, 11)"); + Require(_Daysto_nid_postfix(-4, 2) == 63, "year -4 adds 1 + 8 / 4 days to the leap table"); + Require(_Daysto_nid_postfix(-5, 0) == 3, "year -5 adds 1 + 9 / 4 days to the common table"); + Require(_Daysto_nid_postfix(100, 11) == 359, "every fourth year uses the leap table, centuries included"); + Require(_Daysto_nid_postfix(2023, 11) == 334 + 505, "_Daysto(2023, 11)"); + Require(_Daysto_nid_postfix(2024, 11) == 335 + 505, "_Daysto(2024, 11)"); + Require(Throws(2024, 12) && Throws(2024, -1), "a month outside the tables throws"); +} + +static void TestXpGetw() { + const float one[] = {5.0f, 7.0f, 11.0f, 13.0f}; + Require(_FXp_getw_nid_postfix(one, 0) == 0.0f, "no parts sum to 0"); + Require(_FXp_getw_nid_postfix(one, 1) == 5.0f, "one part is returned as it is"); + const float zeroHead[] = {0.0f, 7.0f, 11.0f}; + Require(_FXp_getw_nid_postfix(zeroHead, 3) == 0.0f, "a zero first part ends the sum"); + const float zeroSecond[] = {5.0f, 0.0f, 11.0f}; + Require(_FXp_getw_nid_postfix(zeroSecond, 3) == 5.0f, "a zero second part ends the sum"); + Require(_FXp_getw_nid_postfix(one, 2) == 12.0f, "two parts are added"); + const float zeroThird[] = {5.0f, 7.0f, 0.0f, 13.0f}; + Require(_FXp_getw_nid_postfix(zeroThird, 4) == 12.0f, "a zero third part ends the sum"); + Require(_FXp_getw_nid_postfix(one, 3) == 23.0f, "three exact parts are added"); + Require(_FXp_getw_nid_postfix(one, 4) == 36.0f, "the fourth part joins the third"); + const float carry[] = {16777216.0f, 1.0f, 1.0f, 2.0f}; + Require(_FXp_getw_nid_postfix(carry, 3) == 16777218.0f, "a second part lost in the first sum is folded with the third first"); + Require(_FXp_getw_nid_postfix(carry, 4) == 16777220.0f, "a second part lost in the first sum is folded with the third and fourth first"); + const float nan[] = {std::nanf(""), 1.0f}; + Require(std::isnan(_FXp_getw_nid_postfix(nan, 2)), "a NaN first part is summed, not compared away"); +} + +int main() { + Require(_Isdst_nid_postfix() == -1, "_Isdst did not return -1"); + TestDaysto(); + TestXpGetw(); + return 0; +} diff --git a/docs/dev/debt/libSceLibcInternal.md b/docs/dev/debt/libSceLibcInternal.md index 82d71328ac..742308f9db 100644 --- a/docs/dev/debt/libSceLibcInternal.md +++ b/docs/dev/debt/libSceLibcInternal.md @@ -351,20 +351,21 @@ The named entries below use provider-specific NID aliases to avoid interposing h - [3.20 pending imports](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - The following PS5 3.20 inventory names are declared for import resolution only; their prototypes and behavior are unverified, and each call throws through `NotImplemented_nid_no_patch`. The source list is [PS5-3.20_Libs](https://github.com/DNNDHH/PS5-3.20_Libs). - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Atan`, `_Atqexit`, `_Atthreadexit`, `_CStrftime`, `_CStrxfrm`, `_CWcsxfrm`, `_Call_onceEx`, `_Closreg` -- [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Daysto`, `_Deletegloballocale`, `_Do_call`, `_Dtento`, `_Erfc`, `_FAtan` +- [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Deletegloballocale`, `_Do_call`, `_Dtento`, `_Erfc`, `_FAtan` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_FDtento`, `_FErfc`, `_FHypot`, `_FLogpoly`, `_FPlsw`, `_FPmsw`, `_FPow`, `_FQuad` -- [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_FQuadph`, `_FRint`, `_FSincos`, `_FTan`, `_FTgamma`, `_FXp_addh`, `_FXp_addx`, `_FXp_getw`, `_FXp_invx` +- [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_FQuadph`, `_FRint`, `_FSincos`, `_FTan`, `_FTgamma`, `_FXp_addh`, `_FXp_addx`, `_FXp_invx` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_FXp_ldexpx`, `_FXp_movx`, `_FXp_mulh`, `_FXp_mulx`, `_FXp_setn`, `_FXp_setw`, `_FXp_sqrtx`, `_FXp_subx`, `_Fac_tidy`, `_Fail_s` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Fgpos`, `_Fofind`, `_Fofree`, `_Fopen`, `_Foprep`, `_Frprep`, `_Fspos` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Fwprep`, `_Genld`, `_Gentime`, `_Getcloc`, `_Getctyptab`, `_Getdst`, `_Geterrno`, `_Getfld`, `_Getfloat`, `_Getgloballocale` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Getint`, `_Getpcostate`, `_Getpwcostate`, `_Getpwctrtab`, `_Getpwctytab`, `_Getstr`, `_Gettime`, `_Getzone`, `_Hypot` -- [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Isdst`, `_LAtan`, `_LDtento`, `_LErfc`, `_LHypot`, `_LLogpoly` +- [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_LAtan`, `_LDtento`, `_LErfc`, `_LHypot`, `_LLogpoly` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_LPlsw`, `_LPmsw`, `_LPow`, `_LQuad`, `_LQuadph`, `_LRint`, `_LSincos`, `_LTan` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_LTgamma`, `_LXp_addh`, `_LXp_addx`, `_LXp_getw`, `_LXp_invx`, `_LXp_ldexpx`, `_LXp_movx`, `_LXp_mulh`, `_LXp_mulx`, `_LXp_setn` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_LXp_setw`, `_LXp_sqrtx`, `_LXp_subx`, `_Ldtob`, `_Litob`, `_Lock_spin_lock`, `_Locsum`, `_Locterm`, `_Locvar`, `_Logpoly` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Makeloc`, `_Makestab`, `_Makewct`, `_Mbtowc`, `_Nnl` - [Additional imported names](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - `_Plsw`, `_Pmsw`, `_Pow`, `_Printf`, `_Putfld`, `_Putstr`, `_Puttxt`, `_Quad`, `_Quadph`, `_Readloc` +- [`_Daysto`, `_FXp_getw` and `_Isdst`](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) - follow firmware 13.42's libSceLibcInternal: read to the return, disassembly only, not measured on a console. `_Isdst` returns -1 without reading its argument. `_Daysto(year, month)` adds the leap days before `year` to the module's twelve-entry common or leap month table, as Dinkumware's `_Daysto` does; the module indexes the table with any month, and a month outside 0 to 11 throws here instead of reading past it. `_FXp_getw` follows Dinkumware's `_Xp_getw` for `float` parts, branch for branch. ### Additional PS5 3.20 imports (round 6) From 0986fdea42e1e7f0d6b6f83107595b6ae81070d0 Mon Sep 17 00:00:00 2001 From: BunnyFl <166477956+BunnyFl@users.noreply.github.com> Date: Mon, 2 Nov 2026 04:20:44 +0100 Subject: [PATCH 13/46] fix(libSceLibcInternal): compute _Daysto's leap days without signed overflow The firmware subtracts and shifts as unsigned 32-bit values, so 4 - year wraps for the five years from INT_MIN to INT_MIN + 4 instead of overflowing; do the same, and test both sides of that range and INT_MAX. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceLibcInternal/Export.cpp | 11 ++++++----- core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceLibcInternal/Export.cpp b/core/libs/prx/libSceLibcInternal/Export.cpp index 51ae83331a..f501e7a671 100644 --- a/core/libs/prx/libSceLibcInternal/Export.cpp +++ b/core/libs/prx/libSceLibcInternal/Export.cpp @@ -1133,11 +1133,12 @@ int APS5_VABI _Daysto_nid_postfix(int year, int month) { static constexpr short DaysBeforeMonth[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; static constexpr short LeapDaysBeforeMonth[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; if (month < 0 || month >= 12) throw std::out_of_range("_Daysto: the month index is outside the twelve-entry tables"); - int leapDays = 0; - if (year > 0) leapDays = (year - 1) / 4; - else if (year <= -4) leapDays = 1 + (4 - year) / 4; - const bool common = (year & 3) != 0 || year == 0; - return leapDays + (common ? DaysBeforeMonth[month] : LeapDaysBeforeMonth[month]); + const auto bits = static_cast(year); + std::uint32_t leapDays = 0; + if (year > 0) leapDays = (bits - 1u) >> 2; + else if (year <= -4) leapDays = ((4u - bits) >> 2) + 1u; + const bool common = (bits & 3u) != 0 || year == 0; + return static_cast(leapDays + static_cast(common ? DaysBeforeMonth[month] : LeapDaysBeforeMonth[month])); } int APS5_VABI _Deletegloballocale_nid_postfix() { diff --git a/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp b/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp index 43dd6300d9..c6cb0a4128 100644 --- a/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp +++ b/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include static void Require(bool value, const char* message) { @@ -35,6 +36,13 @@ static void TestDaysto() { Require(_Daysto_nid_postfix(2023, 11) == 334 + 505, "_Daysto(2023, 11)"); Require(_Daysto_nid_postfix(2024, 11) == 335 + 505, "_Daysto(2024, 11)"); Require(Throws(2024, 12) && Throws(2024, -1), "a month outside the tables throws"); + constexpr int Lowest = std::numeric_limits::min(); + constexpr int Highest = std::numeric_limits::max(); + Require(_Daysto_nid_postfix(Lowest, 0) == 536870914 && _Daysto_nid_postfix(Lowest, 11) == 536871249, "the lowest year wraps 4 - year as an unsigned 32-bit value"); + Require(_Daysto_nid_postfix(Lowest + 1, 0) == 536870913 && _Daysto_nid_postfix(Lowest + 3, 11) == 536871247, "years just above the lowest wrap 4 - year"); + Require(_Daysto_nid_postfix(Lowest + 4, 0) == 536870913 && _Daysto_nid_postfix(Lowest + 4, 11) == 536871248, "the last wrapping year"); + Require(_Daysto_nid_postfix(Lowest + 5, 0) == 536870912 && _Daysto_nid_postfix(Lowest + 5, 11) == 536871246, "the first year whose 4 - year fits in int"); + Require(_Daysto_nid_postfix(Highest, 0) == 536870911 && _Daysto_nid_postfix(Highest, 11) == 536871245, "the highest year"); } static void TestXpGetw() { From 17aebbdc1634263e34c7ff1edf22e088e1c21c13 Mon Sep 17 00:00:00 2001 From: BunnyFl <166477956+BunnyFl@users.noreply.github.com> Date: Mon, 2 Nov 2026 04:33:29 +0100 Subject: [PATCH 14/46] test(libSceLibcInternal): stop pinning the out-of-range _Daysto month to a throw Co-Authored-By: Claude Opus 5.5 --- core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp b/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp index c6cb0a4128..a07b48fb45 100644 --- a/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp +++ b/core/libs/tests/GuestLibcInternalFirmwareAnswers.cpp @@ -4,7 +4,6 @@ #include #include #include -#include static void Require(bool value, const char* message) { if (!value) { @@ -13,15 +12,6 @@ static void Require(bool value, const char* message) { } } -static bool Throws(int year, int month) { - try { - _Daysto_nid_postfix(year, month); - } catch (const std::out_of_range&) { - return true; - } - return false; -} - static void TestDaysto() { Require(_Daysto_nid_postfix(1, 0) == 0, "_Daysto(1, 0)"); Require(_Daysto_nid_postfix(1, 1) == 31, "_Daysto(1, 1)"); @@ -35,7 +25,6 @@ static void TestDaysto() { Require(_Daysto_nid_postfix(100, 11) == 359, "every fourth year uses the leap table, centuries included"); Require(_Daysto_nid_postfix(2023, 11) == 334 + 505, "_Daysto(2023, 11)"); Require(_Daysto_nid_postfix(2024, 11) == 335 + 505, "_Daysto(2024, 11)"); - Require(Throws(2024, 12) && Throws(2024, -1), "a month outside the tables throws"); constexpr int Lowest = std::numeric_limits::min(); constexpr int Highest = std::numeric_limits::max(); Require(_Daysto_nid_postfix(Lowest, 0) == 536870914 && _Daysto_nid_postfix(Lowest, 11) == 536871249, "the lowest year wraps 4 - year as an unsigned 32-bit value"); From b44e1a7f54ea690374d1fc71c52d6f49044854bf Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 05:23:18 +0200 Subject: [PATCH 15/46] feat(libSceLibreSsl3): implement X509_get0_extensions --- core/libs/prx/libSceLibreSsl3/CMakeLists.txt | 13 +++- core/libs/prx/libSceLibreSsl3/Export.cpp | 5 -- .../libSceLibreSsl3/src/X509Extensions.cpp | 47 ++++++++++++++ .../tests/GuestLibreSsl3X509Extensions.cpp | 63 +++++++++++++++++++ docs/dev/debt/libSceLibreSsl3.md | 3 +- 5 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp create mode 100644 core/libs/tests/GuestLibreSsl3X509Extensions.cpp diff --git a/core/libs/prx/libSceLibreSsl3/CMakeLists.txt b/core/libs/prx/libSceLibreSsl3/CMakeLists.txt index 41c6247a2a..23dbd3a9f0 100644 --- a/core/libs/prx/libSceLibreSsl3/CMakeLists.txt +++ b/core/libs/prx/libSceLibreSsl3/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Key.cpp) +add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Key.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509Extensions.cpp) target_include_directories(libSceLibreSsl3 PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceLibreSsl3 PRIVATE libSceLibreSsl libc) configure_windows_unwind(libSceLibreSsl3) @@ -97,4 +97,15 @@ if(BUILD_TESTING) set_tests_properties(guest_crypto_memcmp guest_libressl3_digest guest_libressl3_asn1 guest_libressl3_key_reference guest_libressl3_version PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + + add_test_executable(guest_libressl3_x509_extensions_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSsl3X509Extensions.cpp) + target_include_directories(guest_libressl3_x509_extensions_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_libressl3_x509_extensions_tests PRIVATE libSceLibreSsl3) + configure_windows_unwind(guest_libressl3_x509_extensions_tests) + add_test(NAME guest_libressl3_x509_extensions COMMAND guest_libressl3_x509_extensions_tests) + set_tests_properties(guest_libressl3_x509_extensions PROPERTIES TIMEOUT 30) + if(WIN32) + set_tests_properties(guest_libressl3_x509_extensions PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() endif() diff --git a/core/libs/prx/libSceLibreSsl3/Export.cpp b/core/libs/prx/libSceLibreSsl3/Export.cpp index d327cfb245..733aa4f896 100644 --- a/core/libs/prx/libSceLibreSsl3/Export.cpp +++ b/core/libs/prx/libSceLibreSsl3/Export.cpp @@ -321,11 +321,6 @@ int APS5_VABI X509_STORE_up_ref_nid_postfix(void) { return 0; } -int APS5_VABI X509_get0_extensions_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI X509_get0_notAfter_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp new file mode 100644 index 0000000000..f0af08e59a --- /dev/null +++ b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include "prx/libc/include/General.hpp" + +struct LibreSsl3Stack { + int num; + char** data; + int sorted; + int numAlloc; + void* comp; +}; + +struct LibreSsl3ExtensionsCertInfo { + unsigned char beforeExtensions[72]; + const LibreSsl3Stack* extensions; + unsigned char rest[24]; +}; + +struct LibreSsl3ExtensionsX509 { + const LibreSsl3ExtensionsCertInfo* certInfo; + unsigned char rest[208]; +}; + +static_assert(sizeof(LibreSsl3Stack) == 32); +static_assert(offsetof(LibreSsl3Stack, data) == 8); +static_assert(sizeof(LibreSsl3ExtensionsCertInfo) == 104); +static_assert(offsetof(LibreSsl3ExtensionsCertInfo, extensions) == 72); +static_assert(sizeof(LibreSsl3ExtensionsX509) == 216); + +namespace { + +const LibreSsl3Stack* Extensions(const LibreSsl3ExtensionsX509* certificate, const char* function) { + if (!certificate) throw std::invalid_argument(std::string(function) + ": null certificate"); + if (!certificate->certInfo) throw std::invalid_argument(std::string(function) + ": null certificate info"); + return certificate->certInfo->extensions; +} + +} + +extern "C" { + +const LibreSsl3Stack* APS5_VABI X509_get0_extensions_nid_postfix(const LibreSsl3ExtensionsX509* certificate) { + return Extensions(certificate, __func__); +} + +} diff --git a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp new file mode 100644 index 0000000000..8a9faee78e --- /dev/null +++ b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp @@ -0,0 +1,63 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include +#include + +struct TestStack { + int num; + char** data; + int sorted; + int numAlloc; + void* comp; +}; + +struct TestCertInfo { + void* before[9]; + TestStack* extensions; + void* rest[3]; +}; + +struct TestX509 { + TestCertInfo* certInfo; + void* rest[26]; +}; + +static_assert(sizeof(TestStack) == 32 && offsetof(TestStack, data) == 8); +static_assert(sizeof(TestCertInfo) == 104 && offsetof(TestCertInfo, extensions) == 72); +static_assert(sizeof(TestX509) == 216); + +extern "C" { +const TestStack* APS5_VABI X509_get0_extensions_nid_postfix(const TestX509*); +} + +static void Require(bool condition, std::source_location location = std::source_location::current()) { + if (condition) return; + std::fprintf(stderr, "LibreSSL X509 extension check failed at line %u\n", location.line()); + std::abort(); +} + +template static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) { + try { action(); } catch (const std::exception&) { return; } + Require(false, location); +} + +static void TestGet0Extensions() { + TestStack extensions {}; + TestCertInfo info {}; + TestX509 certificate {}; + RequireThrows([] { X509_get0_extensions_nid_postfix(nullptr); }); + RequireThrows([&] { X509_get0_extensions_nid_postfix(&certificate); }); + certificate.certInfo = &info; + Require(X509_get0_extensions_nid_postfix(&certificate) == nullptr); + info.before[8] = &info; + info.extensions = &extensions; + Require(X509_get0_extensions_nid_postfix(&certificate) == &extensions); +} + +int main() { + TestGet0Extensions(); + return 0; +} diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index fb73c625b7..42f6b12148 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -8,7 +8,7 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 76 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH` and `OpenSSL_version`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 73 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `OpenSSL_version` and `X509_get0_extensions`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. ### Functional @@ -23,6 +23,7 @@ - [EVP_CIPHER_key_length and EVP_CIPHER_iv_length](../../../core/libs/prx/libSceLibreSsl3/src/Cipher.cpp) (libSceLibreSsl3) - nothing is checked on the console. They return `key_len` and `iv_len` from the 80-byte `EVP_CIPHER` of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/evp) `evp_local.h` and `evp_cipher.c`. The fields are at offsets 8 and 12, as in the [2.5.4 layout](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/evp.h) of the ciphers the forwarded libSceLibreSsl exports handle. A null cipher, which LibreSSL dereferences, throws. Closes when: compared with the console. - [X509_VERIFY_PARAM_set_flags](../../../core/libs/prx/libSceLibreSsl3/src/X509VerifyParam.cpp) (libSceLibreSsl3) - nothing is checked on the console. It ORs the flags into `flags` and returns 1, as both [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_vpm.c) and [4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/x509/x509_vpm.c) `x509_vpm.c` do. They differ when `EXPLICIT_POLICY`, `INHIBIT_ANY` or `INHIBIT_MAP` is set while `POLICY_CHECK` is neither set nor requested: 2.5.4 also adds `POLICY_CHECK` and 4.0.0 does not. The console's choice is unknown, so that case throws through `NotImplemented_nid_no_patch` and leaves the parameters unchanged. It writes libSceLibreSsl's 2.5.4 `LibreSslX509VerifyParam`, whose `flags` is at offset 24 as in 4.0.0. Null parameters throw. Closes when: the console's policy behaviour is measured. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` +- [X509_get0_extensions](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach it. A null certificate or certificate info, and an extension stack with a positive count and null entries, which LibreSSL dereferences, throw ### Additional callable export inventory From 07dd9507cb0f987d5a3947cfa1c74f5b328a4335 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 05:24:19 +0200 Subject: [PATCH 16/46] feat(libSceLibreSsl3): implement X509_get_ext_count --- core/libs/prx/libSceLibreSsl3/Export.cpp | 5 ----- .../prx/libSceLibreSsl3/src/X509Extensions.cpp | 5 +++++ .../libs/tests/GuestLibreSsl3X509Extensions.cpp | 17 +++++++++++++++++ docs/dev/debt/libSceLibreSsl3.md | 4 ++-- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl3/Export.cpp b/core/libs/prx/libSceLibreSsl3/Export.cpp index 733aa4f896..17181ef745 100644 --- a/core/libs/prx/libSceLibreSsl3/Export.cpp +++ b/core/libs/prx/libSceLibreSsl3/Export.cpp @@ -341,11 +341,6 @@ int APS5_VABI X509_get_ext_nid_postfix(void) { return 0; } -int APS5_VABI X509_get_ext_count_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI X509_get_version_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp index f0af08e59a..d9e74cc04d 100644 --- a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp @@ -44,4 +44,9 @@ const LibreSsl3Stack* APS5_VABI X509_get0_extensions_nid_postfix(const LibreSsl3 return Extensions(certificate, __func__); } +int APS5_VABI X509_get_ext_count_nid_postfix(const LibreSsl3ExtensionsX509* certificate) { + const LibreSsl3Stack* extensions = Extensions(certificate, __func__); + return extensions ? extensions->num : 0; +} + } diff --git a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp index 8a9faee78e..950cc4ea5e 100644 --- a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp +++ b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp @@ -31,6 +31,7 @@ static_assert(sizeof(TestX509) == 216); extern "C" { const TestStack* APS5_VABI X509_get0_extensions_nid_postfix(const TestX509*); +int APS5_VABI X509_get_ext_count_nid_postfix(const TestX509*); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -57,7 +58,23 @@ static void TestGet0Extensions() { Require(X509_get0_extensions_nid_postfix(&certificate) == &extensions); } +static void TestGetExtCount() { + TestStack extensions {}; + TestCertInfo info {}; + TestX509 certificate {}; + RequireThrows([] { X509_get_ext_count_nid_postfix(nullptr); }); + RequireThrows([&] { X509_get_ext_count_nid_postfix(&certificate); }); + certificate.certInfo = &info; + Require(X509_get_ext_count_nid_postfix(&certificate) == 0); + info.extensions = &extensions; + Require(X509_get_ext_count_nid_postfix(&certificate) == 0); + extensions.num = 3; + extensions.numAlloc = 8; + Require(X509_get_ext_count_nid_postfix(&certificate) == 3); +} + int main() { TestGet0Extensions(); + TestGetExtCount(); return 0; } diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index 42f6b12148..aa87504881 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -8,7 +8,7 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 73 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `OpenSSL_version` and `X509_get0_extensions`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 72 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `OpenSSL_version`, `X509_get0_extensions` and `X509_get_ext_count`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. ### Functional @@ -23,7 +23,7 @@ - [EVP_CIPHER_key_length and EVP_CIPHER_iv_length](../../../core/libs/prx/libSceLibreSsl3/src/Cipher.cpp) (libSceLibreSsl3) - nothing is checked on the console. They return `key_len` and `iv_len` from the 80-byte `EVP_CIPHER` of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/evp) `evp_local.h` and `evp_cipher.c`. The fields are at offsets 8 and 12, as in the [2.5.4 layout](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/evp.h) of the ciphers the forwarded libSceLibreSsl exports handle. A null cipher, which LibreSSL dereferences, throws. Closes when: compared with the console. - [X509_VERIFY_PARAM_set_flags](../../../core/libs/prx/libSceLibreSsl3/src/X509VerifyParam.cpp) (libSceLibreSsl3) - nothing is checked on the console. It ORs the flags into `flags` and returns 1, as both [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_vpm.c) and [4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/x509/x509_vpm.c) `x509_vpm.c` do. They differ when `EXPLICIT_POLICY`, `INHIBIT_ANY` or `INHIBIT_MAP` is set while `POLICY_CHECK` is neither set nor requested: 2.5.4 also adds `POLICY_CHECK` and 4.0.0 does not. The console's choice is unknown, so that case throws through `NotImplemented_nid_no_patch` and leaves the parameters unchanged. It writes libSceLibreSsl's 2.5.4 `LibreSslX509VerifyParam`, whose `flags` is at offset 24 as in 4.0.0. Null parameters throw. Closes when: the console's policy behaviour is measured. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` -- [X509_get0_extensions](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach it. A null certificate or certificate info, and an extension stack with a positive count and null entries, which LibreSSL dereferences, throw +- [X509_get0_extensions, X509_get_ext_count](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach them. A null certificate or certificate info, and an extension stack with a positive count and null entries, which LibreSSL dereferences, throw ### Additional callable export inventory From 042ef4b83410387ba37f0d00426f084d101d6902 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 05:24:29 +0200 Subject: [PATCH 17/46] feat(libSceLibreSsl3): implement X509_get_ext --- core/libs/prx/libSceLibreSsl3/Export.cpp | 5 ---- .../libSceLibreSsl3/src/X509Extensions.cpp | 7 +++++ .../tests/GuestLibreSsl3X509Extensions.cpp | 29 +++++++++++++++++++ docs/dev/debt/libSceLibreSsl3.md | 4 +-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl3/Export.cpp b/core/libs/prx/libSceLibreSsl3/Export.cpp index 17181ef745..296813b8c3 100644 --- a/core/libs/prx/libSceLibreSsl3/Export.cpp +++ b/core/libs/prx/libSceLibreSsl3/Export.cpp @@ -336,11 +336,6 @@ int APS5_VABI X509_get0_signature_nid_postfix(void) { return 0; } -int APS5_VABI X509_get_ext_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI X509_get_version_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp index d9e74cc04d..bd0f61562e 100644 --- a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp @@ -49,4 +49,11 @@ int APS5_VABI X509_get_ext_count_nid_postfix(const LibreSsl3ExtensionsX509* cert return extensions ? extensions->num : 0; } +void* APS5_VABI X509_get_ext_nid_postfix(const LibreSsl3ExtensionsX509* certificate, int loc) { + const LibreSsl3Stack* extensions = Extensions(certificate, __func__); + if (!extensions || loc < 0 || loc >= extensions->num) return nullptr; + if (!extensions->data) throw std::invalid_argument(std::string(__func__) + ": null extension array"); + return extensions->data[loc]; +} + } diff --git a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp index 950cc4ea5e..20a26b4023 100644 --- a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp +++ b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp @@ -32,6 +32,7 @@ static_assert(sizeof(TestX509) == 216); extern "C" { const TestStack* APS5_VABI X509_get0_extensions_nid_postfix(const TestX509*); int APS5_VABI X509_get_ext_count_nid_postfix(const TestX509*); +void* APS5_VABI X509_get_ext_nid_postfix(const TestX509*, int); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -73,8 +74,36 @@ static void TestGetExtCount() { Require(X509_get_ext_count_nid_postfix(&certificate) == 3); } +static void TestGetExt() { + int first = 0; + int second = 0; + char* slots[] {reinterpret_cast(&first), reinterpret_cast(&second), nullptr}; + TestStack extensions {}; + TestCertInfo info {}; + TestX509 certificate {}; + RequireThrows([] { X509_get_ext_nid_postfix(nullptr, 0); }); + RequireThrows([&] { X509_get_ext_nid_postfix(&certificate, 0); }); + certificate.certInfo = &info; + Require(X509_get_ext_nid_postfix(&certificate, 0) == nullptr); + info.extensions = &extensions; + Require(X509_get_ext_nid_postfix(&certificate, 0) == nullptr); + extensions = {2, slots, 0, 3, nullptr}; + Require(X509_get_ext_nid_postfix(&certificate, 0) == &first); + Require(X509_get_ext_nid_postfix(&certificate, 1) == &second); + Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, -1) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, INT_MIN) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, INT_MAX) == nullptr); + extensions.num = 3; + Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); + extensions.data = nullptr; + RequireThrows([&] { X509_get_ext_nid_postfix(&certificate, 0); }); + Require(X509_get_ext_nid_postfix(&certificate, 3) == nullptr); +} + int main() { TestGet0Extensions(); TestGetExtCount(); + TestGetExt(); return 0; } diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index aa87504881..1e649b4c79 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -8,7 +8,7 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 72 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `OpenSSL_version`, `X509_get0_extensions` and `X509_get_ext_count`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 71 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `OpenSSL_version`, `X509_get0_extensions`, `X509_get_ext_count` and `X509_get_ext`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. ### Functional @@ -23,7 +23,7 @@ - [EVP_CIPHER_key_length and EVP_CIPHER_iv_length](../../../core/libs/prx/libSceLibreSsl3/src/Cipher.cpp) (libSceLibreSsl3) - nothing is checked on the console. They return `key_len` and `iv_len` from the 80-byte `EVP_CIPHER` of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/evp) `evp_local.h` and `evp_cipher.c`. The fields are at offsets 8 and 12, as in the [2.5.4 layout](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/evp.h) of the ciphers the forwarded libSceLibreSsl exports handle. A null cipher, which LibreSSL dereferences, throws. Closes when: compared with the console. - [X509_VERIFY_PARAM_set_flags](../../../core/libs/prx/libSceLibreSsl3/src/X509VerifyParam.cpp) (libSceLibreSsl3) - nothing is checked on the console. It ORs the flags into `flags` and returns 1, as both [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_vpm.c) and [4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/x509/x509_vpm.c) `x509_vpm.c` do. They differ when `EXPLICIT_POLICY`, `INHIBIT_ANY` or `INHIBIT_MAP` is set while `POLICY_CHECK` is neither set nor requested: 2.5.4 also adds `POLICY_CHECK` and 4.0.0 does not. The console's choice is unknown, so that case throws through `NotImplemented_nid_no_patch` and leaves the parameters unchanged. It writes libSceLibreSsl's 2.5.4 `LibreSslX509VerifyParam`, whose `flags` is at offset 24 as in 4.0.0. Null parameters throw. Closes when: the console's policy behaviour is measured. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` -- [X509_get0_extensions, X509_get_ext_count](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach them. A null certificate or certificate info, and an extension stack with a positive count and null entries, which LibreSSL dereferences, throw +- [X509_get0_extensions, X509_get_ext_count, X509_get_ext](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach them. A null certificate or certificate info, and an extension stack with a positive count and null entries, which LibreSSL dereferences, throw ### Additional callable export inventory From 9420289623998692db901f0bea53406231e83cfd Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sun, 25 Oct 2026 06:00:29 +0100 Subject: [PATCH 18/46] refactor(libSceLibreSsl3): declare the X509 extension exports in the public header --- core/libs/prx/libSceLibreSsl3/include/Api.hpp | 25 +++++++- .../libSceLibreSsl3/src/X509Extensions.cpp | 20 +----- .../tests/GuestLibreSsl3X509Extensions.cpp | 63 ++++++++----------- 3 files changed, 50 insertions(+), 58 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl3/include/Api.hpp b/core/libs/prx/libSceLibreSsl3/include/Api.hpp index 2eed537f89..7eecf6bbd2 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Api.hpp @@ -5,6 +5,25 @@ #include "prx/libSceLibreSsl/include/Api.hpp" #include "prx/libSceLibreSsl3/include/Cipher.hpp" +struct LibreSsl3Stack { + int num; + char** data; + int sorted; + int numAlloc; + void* comp; +}; + +struct LibreSsl3ExtensionsCertInfo { + unsigned char beforeExtensions[72]; + const LibreSsl3Stack* extensions; + unsigned char rest[24]; +}; + +struct LibreSsl3ExtensionsX509 { + const LibreSsl3ExtensionsCertInfo* certInfo; + unsigned char rest[208]; +}; + extern "C" { int APS5_VABI ASN1_INTEGER_free_nid_postfix(void); @@ -74,12 +93,12 @@ int APS5_VABI X509_STORE_CTX_free_nid_postfix(void); int APS5_VABI X509_STORE_CTX_init_nid_postfix(void); int APS5_VABI X509_STORE_CTX_new_nid_postfix(void); int APS5_VABI X509_STORE_up_ref_nid_postfix(void); -int APS5_VABI X509_get0_extensions_nid_postfix(void); +const LibreSsl3Stack* APS5_VABI X509_get0_extensions_nid_postfix(const LibreSsl3ExtensionsX509* certificate); int APS5_VABI X509_get0_notAfter_nid_postfix(void); int APS5_VABI X509_get0_notBefore_nid_postfix(void); int APS5_VABI X509_get0_signature_nid_postfix(void); -int APS5_VABI X509_get_ext_nid_postfix(void); -int APS5_VABI X509_get_ext_count_nid_postfix(void); +void* APS5_VABI X509_get_ext_nid_postfix(const LibreSsl3ExtensionsX509* certificate, int loc); +int APS5_VABI X509_get_ext_count_nid_postfix(const LibreSsl3ExtensionsX509* certificate); int APS5_VABI X509_get_version_nid_postfix(void); int APS5_VABI X509_getm_notAfter_nid_postfix(void); int APS5_VABI X509_getm_notBefore_nid_postfix(void); diff --git a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp index bd0f61562e..d9aa68ab68 100644 --- a/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp @@ -1,27 +1,9 @@ +#include "prx/libSceLibreSsl3/include/Api.hpp" #include #include #include #include "prx/libc/include/General.hpp" -struct LibreSsl3Stack { - int num; - char** data; - int sorted; - int numAlloc; - void* comp; -}; - -struct LibreSsl3ExtensionsCertInfo { - unsigned char beforeExtensions[72]; - const LibreSsl3Stack* extensions; - unsigned char rest[24]; -}; - -struct LibreSsl3ExtensionsX509 { - const LibreSsl3ExtensionsCertInfo* certInfo; - unsigned char rest[208]; -}; - static_assert(sizeof(LibreSsl3Stack) == 32); static_assert(offsetof(LibreSsl3Stack, data) == 8); static_assert(sizeof(LibreSsl3ExtensionsCertInfo) == 104); diff --git a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp index 20a26b4023..679138528e 100644 --- a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp +++ b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp @@ -1,3 +1,4 @@ +#include "prx/libSceLibreSsl3/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -6,17 +7,9 @@ #include #include -struct TestStack { - int num; - char** data; - int sorted; - int numAlloc; - void* comp; -}; - struct TestCertInfo { void* before[9]; - TestStack* extensions; + LibreSsl3Stack* extensions; void* rest[3]; }; @@ -25,14 +18,12 @@ struct TestX509 { void* rest[26]; }; -static_assert(sizeof(TestStack) == 32 && offsetof(TestStack, data) == 8); +static_assert(sizeof(LibreSsl3Stack) == 32 && offsetof(LibreSsl3Stack, data) == 8); static_assert(sizeof(TestCertInfo) == 104 && offsetof(TestCertInfo, extensions) == 72); static_assert(sizeof(TestX509) == 216); -extern "C" { -const TestStack* APS5_VABI X509_get0_extensions_nid_postfix(const TestX509*); -int APS5_VABI X509_get_ext_count_nid_postfix(const TestX509*); -void* APS5_VABI X509_get_ext_nid_postfix(const TestX509*, int); +static const LibreSsl3ExtensionsX509* Certificate(const TestX509* certificate) { + return reinterpret_cast(certificate); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -47,58 +38,58 @@ template static void RequireThrows(TAction action, std::source } static void TestGet0Extensions() { - TestStack extensions {}; + LibreSsl3Stack extensions {}; TestCertInfo info {}; TestX509 certificate {}; RequireThrows([] { X509_get0_extensions_nid_postfix(nullptr); }); - RequireThrows([&] { X509_get0_extensions_nid_postfix(&certificate); }); + RequireThrows([&] { X509_get0_extensions_nid_postfix(Certificate(&certificate)); }); certificate.certInfo = &info; - Require(X509_get0_extensions_nid_postfix(&certificate) == nullptr); + Require(X509_get0_extensions_nid_postfix(Certificate(&certificate)) == nullptr); info.before[8] = &info; info.extensions = &extensions; - Require(X509_get0_extensions_nid_postfix(&certificate) == &extensions); + Require(X509_get0_extensions_nid_postfix(Certificate(&certificate)) == &extensions); } static void TestGetExtCount() { - TestStack extensions {}; + LibreSsl3Stack extensions {}; TestCertInfo info {}; TestX509 certificate {}; RequireThrows([] { X509_get_ext_count_nid_postfix(nullptr); }); - RequireThrows([&] { X509_get_ext_count_nid_postfix(&certificate); }); + RequireThrows([&] { X509_get_ext_count_nid_postfix(Certificate(&certificate)); }); certificate.certInfo = &info; - Require(X509_get_ext_count_nid_postfix(&certificate) == 0); + Require(X509_get_ext_count_nid_postfix(Certificate(&certificate)) == 0); info.extensions = &extensions; - Require(X509_get_ext_count_nid_postfix(&certificate) == 0); + Require(X509_get_ext_count_nid_postfix(Certificate(&certificate)) == 0); extensions.num = 3; extensions.numAlloc = 8; - Require(X509_get_ext_count_nid_postfix(&certificate) == 3); + Require(X509_get_ext_count_nid_postfix(Certificate(&certificate)) == 3); } static void TestGetExt() { int first = 0; int second = 0; char* slots[] {reinterpret_cast(&first), reinterpret_cast(&second), nullptr}; - TestStack extensions {}; + LibreSsl3Stack extensions {}; TestCertInfo info {}; TestX509 certificate {}; RequireThrows([] { X509_get_ext_nid_postfix(nullptr, 0); }); - RequireThrows([&] { X509_get_ext_nid_postfix(&certificate, 0); }); + RequireThrows([&] { X509_get_ext_nid_postfix(Certificate(&certificate), 0); }); certificate.certInfo = &info; - Require(X509_get_ext_nid_postfix(&certificate, 0) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 0) == nullptr); info.extensions = &extensions; - Require(X509_get_ext_nid_postfix(&certificate, 0) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 0) == nullptr); extensions = {2, slots, 0, 3, nullptr}; - Require(X509_get_ext_nid_postfix(&certificate, 0) == &first); - Require(X509_get_ext_nid_postfix(&certificate, 1) == &second); - Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); - Require(X509_get_ext_nid_postfix(&certificate, -1) == nullptr); - Require(X509_get_ext_nid_postfix(&certificate, INT_MIN) == nullptr); - Require(X509_get_ext_nid_postfix(&certificate, INT_MAX) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 0) == &first); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 1) == &second); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 2) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), -1) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), INT_MIN) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), INT_MAX) == nullptr); extensions.num = 3; - Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 2) == nullptr); extensions.data = nullptr; - RequireThrows([&] { X509_get_ext_nid_postfix(&certificate, 0); }); - Require(X509_get_ext_nid_postfix(&certificate, 3) == nullptr); + RequireThrows([&] { X509_get_ext_nid_postfix(Certificate(&certificate), 0); }); + Require(X509_get_ext_nid_postfix(Certificate(&certificate), 3) == nullptr); } int main() { From fc9b469048b79fe5886b8663aaa75ed2a7c6a811 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sun, 25 Oct 2026 17:57:22 +0100 Subject: [PATCH 19/46] test(libSceLibreSsl3): use the public X509 structs and drop the unsupported-input throws in the extension tests --- .../tests/GuestLibreSsl3X509Extensions.cpp | 80 ++++++------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp index 679138528e..0bb11f3422 100644 --- a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp +++ b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp @@ -1,30 +1,10 @@ #include "prx/libSceLibreSsl3/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include -#include #include #include +#include #include -#include - -struct TestCertInfo { - void* before[9]; - LibreSsl3Stack* extensions; - void* rest[3]; -}; - -struct TestX509 { - TestCertInfo* certInfo; - void* rest[26]; -}; - -static_assert(sizeof(LibreSsl3Stack) == 32 && offsetof(LibreSsl3Stack, data) == 8); -static_assert(sizeof(TestCertInfo) == 104 && offsetof(TestCertInfo, extensions) == 72); -static_assert(sizeof(TestX509) == 216); - -static const LibreSsl3ExtensionsX509* Certificate(const TestX509* certificate) { - return reinterpret_cast(certificate); -} static void Require(bool condition, std::source_location location = std::source_location::current()) { if (condition) return; @@ -32,37 +12,29 @@ static void Require(bool condition, std::source_location location = std::source_ std::abort(); } -template static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) { - try { action(); } catch (const std::exception&) { return; } - Require(false, location); -} - static void TestGet0Extensions() { LibreSsl3Stack extensions {}; - TestCertInfo info {}; - TestX509 certificate {}; - RequireThrows([] { X509_get0_extensions_nid_postfix(nullptr); }); - RequireThrows([&] { X509_get0_extensions_nid_postfix(Certificate(&certificate)); }); + LibreSsl3ExtensionsCertInfo info {}; + LibreSsl3ExtensionsX509 certificate {}; certificate.certInfo = &info; - Require(X509_get0_extensions_nid_postfix(Certificate(&certificate)) == nullptr); - info.before[8] = &info; + Require(X509_get0_extensions_nid_postfix(&certificate) == nullptr); + std::memset(info.beforeExtensions, 0xFF, sizeof(info.beforeExtensions)); + std::memset(info.rest, 0xFF, sizeof(info.rest)); info.extensions = &extensions; - Require(X509_get0_extensions_nid_postfix(Certificate(&certificate)) == &extensions); + Require(X509_get0_extensions_nid_postfix(&certificate) == &extensions); } static void TestGetExtCount() { LibreSsl3Stack extensions {}; - TestCertInfo info {}; - TestX509 certificate {}; - RequireThrows([] { X509_get_ext_count_nid_postfix(nullptr); }); - RequireThrows([&] { X509_get_ext_count_nid_postfix(Certificate(&certificate)); }); + LibreSsl3ExtensionsCertInfo info {}; + LibreSsl3ExtensionsX509 certificate {}; certificate.certInfo = &info; - Require(X509_get_ext_count_nid_postfix(Certificate(&certificate)) == 0); + Require(X509_get_ext_count_nid_postfix(&certificate) == 0); info.extensions = &extensions; - Require(X509_get_ext_count_nid_postfix(Certificate(&certificate)) == 0); + Require(X509_get_ext_count_nid_postfix(&certificate) == 0); extensions.num = 3; extensions.numAlloc = 8; - Require(X509_get_ext_count_nid_postfix(Certificate(&certificate)) == 3); + Require(X509_get_ext_count_nid_postfix(&certificate) == 3); } static void TestGetExt() { @@ -70,26 +42,22 @@ static void TestGetExt() { int second = 0; char* slots[] {reinterpret_cast(&first), reinterpret_cast(&second), nullptr}; LibreSsl3Stack extensions {}; - TestCertInfo info {}; - TestX509 certificate {}; - RequireThrows([] { X509_get_ext_nid_postfix(nullptr, 0); }); - RequireThrows([&] { X509_get_ext_nid_postfix(Certificate(&certificate), 0); }); + LibreSsl3ExtensionsCertInfo info {}; + LibreSsl3ExtensionsX509 certificate {}; certificate.certInfo = &info; - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 0) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, 0) == nullptr); info.extensions = &extensions; - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 0) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, 0) == nullptr); extensions = {2, slots, 0, 3, nullptr}; - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 0) == &first); - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 1) == &second); - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 2) == nullptr); - Require(X509_get_ext_nid_postfix(Certificate(&certificate), -1) == nullptr); - Require(X509_get_ext_nid_postfix(Certificate(&certificate), INT_MIN) == nullptr); - Require(X509_get_ext_nid_postfix(Certificate(&certificate), INT_MAX) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, 0) == &first); + Require(X509_get_ext_nid_postfix(&certificate, 1) == &second); + Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, -1) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, INT_MIN) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, INT_MAX) == nullptr); extensions.num = 3; - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 2) == nullptr); - extensions.data = nullptr; - RequireThrows([&] { X509_get_ext_nid_postfix(Certificate(&certificate), 0); }); - Require(X509_get_ext_nid_postfix(Certificate(&certificate), 3) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, 3) == nullptr); } int main() { From c21ce49e513d7f40a0866efdbc6b22f2d67daa2a Mon Sep 17 00:00:00 2001 From: Alvaro Date: Mon, 26 Oct 2026 07:16:33 +0100 Subject: [PATCH 20/46] test(libSceLibreSsl3): pin the extension count bound with a stale entry past it --- core/libs/tests/GuestLibreSsl3X509Extensions.cpp | 5 +++-- docs/dev/debt/libSceLibreSsl3.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp index 0bb11f3422..a89f8c328e 100644 --- a/core/libs/tests/GuestLibreSsl3X509Extensions.cpp +++ b/core/libs/tests/GuestLibreSsl3X509Extensions.cpp @@ -40,7 +40,8 @@ static void TestGetExtCount() { static void TestGetExt() { int first = 0; int second = 0; - char* slots[] {reinterpret_cast(&first), reinterpret_cast(&second), nullptr}; + int third = 0; + char* slots[] {reinterpret_cast(&first), reinterpret_cast(&second), reinterpret_cast(&third)}; LibreSsl3Stack extensions {}; LibreSsl3ExtensionsCertInfo info {}; LibreSsl3ExtensionsX509 certificate {}; @@ -56,7 +57,7 @@ static void TestGetExt() { Require(X509_get_ext_nid_postfix(&certificate, INT_MIN) == nullptr); Require(X509_get_ext_nid_postfix(&certificate, INT_MAX) == nullptr); extensions.num = 3; - Require(X509_get_ext_nid_postfix(&certificate, 2) == nullptr); + Require(X509_get_ext_nid_postfix(&certificate, 2) == &third); Require(X509_get_ext_nid_postfix(&certificate, 3) == nullptr); } diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index 1e649b4c79..41f5d16e8e 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -23,7 +23,7 @@ - [EVP_CIPHER_key_length and EVP_CIPHER_iv_length](../../../core/libs/prx/libSceLibreSsl3/src/Cipher.cpp) (libSceLibreSsl3) - nothing is checked on the console. They return `key_len` and `iv_len` from the 80-byte `EVP_CIPHER` of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/evp) `evp_local.h` and `evp_cipher.c`. The fields are at offsets 8 and 12, as in the [2.5.4 layout](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/evp.h) of the ciphers the forwarded libSceLibreSsl exports handle. A null cipher, which LibreSSL dereferences, throws. Closes when: compared with the console. - [X509_VERIFY_PARAM_set_flags](../../../core/libs/prx/libSceLibreSsl3/src/X509VerifyParam.cpp) (libSceLibreSsl3) - nothing is checked on the console. It ORs the flags into `flags` and returns 1, as both [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_vpm.c) and [4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/x509/x509_vpm.c) `x509_vpm.c` do. They differ when `EXPLICIT_POLICY`, `INHIBIT_ANY` or `INHIBIT_MAP` is set while `POLICY_CHECK` is neither set nor requested: 2.5.4 also adds `POLICY_CHECK` and 4.0.0 does not. The console's choice is unknown, so that case throws through `NotImplemented_nid_no_patch` and leaves the parameters unchanged. It writes libSceLibreSsl's 2.5.4 `LibreSslX509VerifyParam`, whose `flags` is at offset 24 as in 4.0.0. Null parameters throw. Closes when: the console's policy behaviour is measured. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` -- [X509_get0_extensions, X509_get_ext_count, X509_get_ext](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach them. A null certificate or certificate info, and an extension stack with a positive count and null entries, which LibreSSL dereferences, throw +- [X509_get0_extensions, X509_get_ext_count, X509_get_ext](../../../core/libs/prx/libSceLibreSsl3/src/X509Extensions.cpp) (libSceLibreSsl3) - nothing is checked on the console. The `X509`, `X509_CINF` and stack layouts and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `x509_local.h`, `stack_local.h`, `x509_set.c`, `x509_ext.c`, `x509_v3.c` and `stack.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a certificate yet, so only certificates the caller builds reach them. A null certificate or certificate info, and an extension stack with a positive count and a null entry array, which LibreSSL dereferences, throw ### Additional callable export inventory From 79d6d1af7036d17c6f51ad7158fb7d9ea2113565 Mon Sep 17 00:00:00 2001 From: Eduardo Ruiz Date: Mon, 2 Nov 2026 23:15:24 +0100 Subject: [PATCH 21/46] fix(libSceContentSearch): keep the unmeasured index query throwing --- core/libs/prx/libSceContentSearch/Export.cpp | 1 + core/libs/tests/GuestContentSearch.cpp | 2 +- docs/dev/debt/libSceContentSearch.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceContentSearch/Export.cpp b/core/libs/prx/libSceContentSearch/Export.cpp index 72a644817c..9e0c985a9f 100644 --- a/core/libs/prx/libSceContentSearch/Export.cpp +++ b/core/libs/prx/libSceContentSearch/Export.cpp @@ -200,6 +200,7 @@ int APS5_VABI sceContentSearchGetMyApplicationIndex(std::int32_t* index) { if (!g_initialized.load()) return ErrorNotInitialized; if (index == nullptr) return ErrorInvalidArgument; *index = 0; + NotImplemented_nid_no_patch(__func__); return 0; } int APS5_VABI sceContentSearchGetNumOfApplication(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, const ContentSearchContentOrderByCondition* orderByConditions, std::uint32_t orderByConditionsLength, std::int64_t* numOfApplication, std::int64_t* lastUpdateId) { diff --git a/core/libs/tests/GuestContentSearch.cpp b/core/libs/tests/GuestContentSearch.cpp index 76c6660d85..064029844e 100644 --- a/core/libs/tests/GuestContentSearch.cpp +++ b/core/libs/tests/GuestContentSearch.cpp @@ -99,7 +99,7 @@ int main() { updateId = -1; Require(sceContentSearchGetApplicationLastUpdateId(&updateId) == 0 && updateId == 0); applicationIndex = -1; - Require(sceContentSearchGetMyApplicationIndex(&applicationIndex) == 0 && applicationIndex == 0); + Require(ThrowsRuntimeError([&] { sceContentSearchGetMyApplicationIndex(&applicationIndex); }) && applicationIndex == 0); count = -1; updateId = -1; Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, &count, &updateId) == 0); diff --git a/docs/dev/debt/libSceContentSearch.md b/docs/dev/debt/libSceContentSearch.md index 7ee5185575..627bdcd5d1 100644 --- a/docs/dev/debt/libSceContentSearch.md +++ b/docs/dev/debt/libSceContentSearch.md @@ -16,7 +16,7 @@ ### Functional - [Content queries](../../../core/libs/prx/libSceContentSearch/Export.cpp) use an empty host library and always report update id 0; the console's initial update id is unverified. No content rows are produced. Every title is assumed to have search permission. Nested filter/sort validation is incomplete: empty nested column sets, null filter values and unknown enum values throw. Guest heap allocation and the platform-dependent minimum heap size are unimplemented. Initialization and outer pointer/count/limit validation follow the supplied module. -- [sceContentSearchGetNumOfApplication, sceContentSearchGetApplicationLastUpdateId and sceContentSearchGetMyApplicationIndex](../../../core/libs/prx/libSceContentSearch/Export.cpp) (libSceContentSearch) - signatures from the 11.00 module wrapper: `GetNumOfApplication` takes the column-set and order-by pairs plus the count and update-id outputs; `GetApplicationLastUpdateId` writes a 64-bit update id; `GetMyApplicationIndex` writes a 32-bit index, defaulting it to 0 before delegating. The host reports zero applications and update id 0, and index 0, because the host library has no application records; the console values are unverified. All three check initialization and null outputs first, and `GetNumOfApplication` validates the column-set and order-by contents as the content queries do. `sceContentSearchSearchApplication` still throws because its eleven-argument contract is not mapped. +- [sceContentSearchGetNumOfApplication, sceContentSearchGetApplicationLastUpdateId and sceContentSearchGetMyApplicationIndex](../../../core/libs/prx/libSceContentSearch/Export.cpp) (libSceContentSearch) - signatures from the 11.00 module wrappers at `0x18e70`, `0x18f70` and `0x191a0` in `libSceContentSearch.sprx` (SHA-256 `af44252b7b9a0c2d83d01197a4d38119375fb14b82c7e2a822187a49432b42e4`): `GetNumOfApplication` takes the column-set and order-by pairs plus the count and update-id outputs; `GetApplicationLastUpdateId` writes a 64-bit update id. The host reports zero applications and update id 0 because the host library has no application records; the console values are unverified. `GetMyApplicationIndex` writes a 32-bit index, defaults it to 0 before delegating, and returns the delegate's result, which is not established when the title is absent from the library, so the host writes 0 and then throws through `NotImplemented_nid_no_patch`. All three check initialization and null outputs first, and `GetNumOfApplication` validates the column-set and order-by contents as the content queries do. `sceContentSearchSearchApplication` still throws because its eleven-argument contract is not mapped. - [Metadata access](../../../core/libs/prx/libSceContentSearch/Export.cpp) cannot open metadata by path or content id because missing-content errors remain unresolved: past their argument checks, both opening calls go through `NotImplemented_nid_no_patch`. No metadata handles are issued. The field-info and value queries reject invalid outer pointers, then go through `NotImplemented_nid_no_patch`, as no metadata id is ever opened and the console's error for one depends on the requested field; closing an unopened handle returns the confirmed invalid-argument error. ### Additional callable exports From ef74cb216e42d1e46cb87814f1a48f458158a8fb Mon Sep 17 00:00:00 2001 From: JJenkx Date: Mon, 2 Nov 2026 20:32:49 -0500 Subject: [PATCH 22/46] docs(libc): keep the source notice of fmod's special-value filter fmod's special-value filter (the bit tests and (x * y) / (x * y)) comes from FreeBSD msun's lib/msun/src/e_fmod.c at release/11.4.0. Name that source above fmod_nid_postfix, in the form the exp port uses, followed by the file's Sun copyright and permission notice copied verbatim from that revision. fmodf's source, e_fmodf.c, is already named in this file. No code changes. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libc/src/Math.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index b8fdb1f785..7e18ec51e8 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -2113,6 +2113,17 @@ float APS5_VABI ldexpf_nid_postfix(float x, int exp) { errno = saved_errno; return result; } +/* Adapted from FreeBSD release/11.4.0 lib/msun/src/e_fmod.c: https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/e_fmod.c */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ double APS5_VABI fmod_nid_postfix(double x, double y) { const std::uint64_t ix = std::bit_cast(x) & 0x7fffffffffffffffull; const std::uint64_t iy = std::bit_cast(y) & 0x7fffffffffffffffull; From 9406c8585e0eca130d0f1014c80f9544d09850bc Mon Sep 17 00:00:00 2001 From: Milos-Opletal <87488948+Milos-Opletal@users.noreply.github.com> Date: Thu, 5 Nov 2026 10:07:16 +0100 Subject: [PATCH 23/46] feat(libSceAppContent): implement sceAppContentDownloadDataGetAvailableSpaceKb and sceAppContentDownload1Expand --- core/libs/prx/libSceAppContent/Export.cpp | 14 +++++++++---- .../libs/prx/libSceAppContent/include/Api.hpp | 2 +- core/libs/tests/GuestAppContent.cpp | 21 +++++++++++++++++++ docs/dev/debt/libSceAppContent.md | 6 +++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index 7f4947a6c1..ce58e1546e 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -62,6 +62,7 @@ bool IsTemporaryMountPoint(const AppContentMountPoint* mountPoint) { return std::strncmp(mountPoint->data, TEMPORARY_MOUNT_POINT, sizeof(mountPoint->data)) == 0; } +std::atomic download1QuotaBytes{0}; std::atomic download2QuotaBytes{0}; bool IsDownloadMountPoint(const AppContentMountPoint* mountPoint) { @@ -173,10 +174,10 @@ int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMount const std::uint64_t quotaMiB = GetAppDownloadDataSizeMiB_nid_postfix(); if (quotaMiB > std::numeric_limits::max() / 1024u) throw std::overflow_error("AppContent download quota exceeds KiB range"); quotaKb = quotaMiB * 1024u; + } else if (std::strncmp(mountPoint->data, "/download1", sizeof(mountPoint->data)) == 0) { + quotaKb = download1QuotaBytes.load() / 1024u; } else if (std::strncmp(mountPoint->data, "/download2", sizeof(mountPoint->data)) == 0) { quotaKb = download2QuotaBytes.load() / 1024u; - } else { - NotImplemented_nid_no_patch(__func__); } if (quotaKb == 0) { *availableSpaceKb = 0; @@ -322,8 +323,13 @@ int APS5_VABI sceAppContentDownload0Shrink(void) { return 0; } -int APS5_VABI sceAppContentDownload1Expand(void) { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceAppContentDownload1Expand(size_t size) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; + if (size == 0) return SCE_APP_CONTENT_ERROR_PARAMETER; + const auto directory = ResolvePath_nid_no_patch("/download1"); + if (std::filesystem::create_directories(directory)) RecordWrittenPath_nid_no_patch(directory); + size_t current = download1QuotaBytes.load(); + while (size > current && !download1QuotaBytes.compare_exchange_weak(current, size)) {} return 0; } diff --git a/core/libs/prx/libSceAppContent/include/Api.hpp b/core/libs/prx/libSceAppContent/include/Api.hpp index e6c6f7021a..ec4fc67145 100644 --- a/core/libs/prx/libSceAppContent/include/Api.hpp +++ b/core/libs/prx/libSceAppContent/include/Api.hpp @@ -36,7 +36,7 @@ int APS5_VABI sceAppContentCheckBundleLicenseOnDisc(uint32_t serviceLabel, const int APS5_VABI sceAppContentAppParamGetString(void); int APS5_VABI sceAppContentDownload0Expand(void); int APS5_VABI sceAppContentDownload0Shrink(void); -int APS5_VABI sceAppContentDownload1Expand(void); +int APS5_VABI sceAppContentDownload1Expand(size_t size); int APS5_VABI sceAppContentDownload1Shrink(void); int APS5_VABI sceAppContentDownload2Expand(size_t size); int APS5_VABI sceAppContentDownload2Shrink(void); diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp index a57e43df2c..0086125a1d 100644 --- a/core/libs/tests/GuestAppContent.cpp +++ b/core/libs/tests/GuestAppContent.cpp @@ -38,6 +38,7 @@ int main() { Require(sceAppContentTemporaryDataGetAvailableSpaceKb(nullptr, nullptr) == notInitialized); Require(sceAppContentDownloadDataFormat(nullptr) == notInitialized); Require(sceAppContentDownloadDataGetAvailableSpaceKb(nullptr, nullptr) == notInitialized); + Require(sceAppContentDownload1Expand(1024) == notInitialized); Require(sceAppContentDownload2Expand(1024) == notInitialized); Require(sceAppContentAddcontDelete(0, nullptr) == notInitialized); Require(sceAppContentAddcontEnqueueDownload(0, nullptr) == notInitialized); @@ -94,6 +95,7 @@ int main() { Require(sceAppContentDownloadDataGetAvailableSpaceKb(nullptr, nullptr) == ErrorParameter); AppContentMountPoint invalidMount{}; Require(sceAppContentDownloadDataGetAvailableSpaceKb(&invalidMount, &availableKb) == ErrorNotMounted); + Require(availableKb == 12345); const auto downloadData = ResolvePath_nid_no_patch("/download0"); Require(sceAppContentDownloadDataFormat(&download) == 0); Require(std::filesystem::is_directory(downloadData) && std::filesystem::is_empty(downloadData)); @@ -123,6 +125,25 @@ int main() { Require(sceAppContentDownload2Expand(1024 * 1024) == 0); Require(sceAppContentDownloadDataGetAvailableSpaceKb(&download2Mount, &download2Space) == 0); Require(download2Space == 2048); + AppContentMountPoint download1Mount{}; + std::memcpy(download1Mount.data, "/download1", 11); + const auto download1Data = ResolvePath_nid_no_patch("/download1"); + std::filesystem::create_directories(download1Data); + { std::ofstream file(download1Data / "old.bin"); file << "download1"; } + Require(sceAppContentDownloadDataFormat(&download1Mount) == 0); + Require(std::filesystem::is_directory(download1Data) && std::filesystem::is_empty(download1Data)); + Require(sceAppContentDownload1Expand(0) == ErrorParameter); + Require(sceAppContentDownload1Expand(2 * 1024 * 1024) == 0); + Require(std::filesystem::is_directory(download1Data)); + size_t download1Space = 12345; + Require(sceAppContentDownloadDataGetAvailableSpaceKb(&download1Mount, &download1Space) == 0); + Require(download1Space == 2048); + Require(sceAppContentDownload1Expand(1024 * 1024) == 0); + Require(sceAppContentDownloadDataGetAvailableSpaceKb(&download1Mount, &download1Space) == 0); + Require(download1Space == 2048); + { std::ofstream file(download1Data / "used.bin", std::ios::binary); file << std::string(1024, 'x'); } + Require(sceAppContentDownloadDataGetAvailableSpaceKb(&download1Mount, &download1Space) == 0); + Require(download1Space == 2047); const int32_t expectedParams[] = {3, 23566, -7, 0, 2147483647}; for (uint32_t id = 0; id < 5; ++id) { diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index faac56021c..160869a9a8 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -8,15 +8,15 @@ | --- | --- | | `sceAppContentDownloadDataGetBlockSize` | `9ht66cHTj7g` | -- [sceAppContentDownloadDataGetAvailableSpaceKb](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). KytyPS5 does not special-case it, and shadPS4 stubs 1048576 KB. One disassembled title (PPSA15246) calls it with the `/download0` mount point and a zeroed 64-bit output, ignores the return value and multiplies the output by 1024, so for that title an error that leaves the output untouched and an OK with 0 KB look the same. `/download1` queries remain unimplemented because their quota model is unverified; `/download2` reflects the expanded quota. +- [sceAppContentDownloadDataGetAvailableSpaceKb](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). KytyPS5 does not special-case it, and shadPS4 stubs 1048576 KB. One disassembled title (PPSA15246) calls it with the `/download0` mount point and a zeroed 64-bit output, ignores the return value and multiplies the output by 1024, so for that title an error that leaves the output untouched and an OK with 0 KB look the same. `/download1` and `/download2` queries reflect their configured quota. - [sceAppContentDownloadDataFormat](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - empties the `/download0`, `/download1`, or `/download2` directory, creating it if needed. Imported by PPSA01317. A null mount point returns `PARAMETER` and another mount point returns `NOT_MOUNTED` as in `sceAppContentTemporaryDataFormat` and `sceAppContentDownloadDataGetAvailableSpaceKb`; the console's error codes for both and its behaviour with files still open are unverified -- [sceAppContentDownload0Expand, sceAppContentDownload0Shrink, sceAppContentDownload1Expand, sceAppContentDownload1Shrink and sceAppContentDownload2Shrink](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - partition expansion and shrink behaviour on hardware is unverified. `sceAppContentDownload2Expand` expands `/download2` and updates its reported quota; the other partition expansion and shrink functions throw through `NotImplemented_nid_no_patch`. +- [sceAppContentDownload0Expand, sceAppContentDownload0Shrink, sceAppContentDownload1Shrink and sceAppContentDownload2Shrink](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - partition expansion and shrink behaviour on hardware is unverified. `sceAppContentDownload1Expand` and `sceAppContentDownload2Expand` expand `/download1` and `/download2` and update their reported quotas; the other partition expansion and shrink functions throw through `NotImplemented_nid_no_patch`. - [sceAppContentUnknown_SWVxsi_ZBlw](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent, NID SWVxsi-ZBlw) - master data identifier query (UnknownMdid in [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.cpp#L2258-L2262)). Reconstructed signature and return values on hardware are unverified; all calls throw through `NotImplemented_nid_no_patch`. - [libSceAppContent pending exports](../../../core/libs/prx/libSceAppContent/Export.cpp) - 8 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppContent.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. ### Functional -- [Download partition expansion](../../../core/libs/prx/libSceAppContent/Export.cpp) - `/download2` expansion tracks size monotonically and reports available space in `sceAppContentDownloadDataGetAvailableSpaceKb`. The quota units and hardware shrink behavior remain unverified. +- [Download partition expansion](../../../core/libs/prx/libSceAppContent/Export.cpp) - `/download1` and `/download2` expansion tracks size monotonically and reports available space in `sceAppContentDownloadDataGetAvailableSpaceKb`. The quota units and hardware shrink behavior remain unverified. - [Download-data quota conversion](../../../core/libs/prx/libSceAppContent/Export.cpp) - console handling of `downloadDataSize` values too large to represent in 64-bit KiB is unverified. The host model throws `std::overflow_error` before writing the output or creating storage; no console error code is assigned. From aa14f404798c5e313e0a840bbb6a5dba71ac1e0c Mon Sep 17 00:00:00 2001 From: Evrim Cicek Date: Mon, 26 Oct 2026 16:32:26 +0300 Subject: [PATCH 24/46] test(libkernel): end a blocked receive when its socket is shut for reading --- core/libs/tests/GuestTcp.cpp | 55 ++++++++++++++++++++++++++++++++++++ core/libs/tests/GuestUdp.cpp | 47 ++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/core/libs/tests/GuestTcp.cpp b/core/libs/tests/GuestTcp.cpp index 2538d521a1..ba32a68be0 100644 --- a/core/libs/tests/GuestTcp.cpp +++ b/core/libs/tests/GuestTcp.cpp @@ -3,6 +3,7 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include #include #include #include @@ -275,6 +276,59 @@ static void SocketNameFaults() { for (const int descriptor : {unconnected, accepted, client, listener}) Require(close_nid_postfix(descriptor) == 0); } +static void BlockedReceiveEndsAtShutRead() { + enum Call { Receive, ReceiveFrom, ReceiveMessage, Read, ThroughDuplicate, WithTimeout, Calls }; + for (int call = Receive; call < Calls; ++call) { + const int listener = socket_nid_postfix(2, 1, 0); + Require(listener >= 0); + std::array address{16, 2, 0, 0, 127, 0, 0, 1}; + Require(bind_nid_postfix(listener, address.data(), address.size()) == 0 && listen_nid_postfix(listener, 4) == 0); + std::uint32_t size = address.size(); + Require(getsockname_nid_postfix(listener, address.data(), &size) == 0); + const int client = socket_nid_postfix(2, 1, 0); + Require(client >= 0 && connect_nid_postfix(client, address.data(), address.size()) == 0); + const int accepted = accept_nid_postfix(listener, nullptr, nullptr); + Require(accepted >= 0); + const int watched = call == ThroughDuplicate ? dup_nid_postfix(accepted) : accepted; + Require(watched >= 0); + if (call == WithTimeout) { + const KernelTimeval timeout{3, 0}; + Require(setsockopt_nid_postfix(accepted, 0xffff, 0x1006, &timeout, sizeof(timeout)) == 0); + } + char buffer[8]; + std::array sender{}; + std::uint32_t senderSize = sender.size(); + KernelIovec vector{buffer, sizeof(buffer)}; + GuestMsghdr message{sender.data(), static_cast(sender.size()), &vector, 1, nullptr, 0, -1}; + std::atomic entered{false}; + std::atomic returned{false}; + std::int64_t received = -2; + std::chrono::steady_clock::duration waited{}; + std::thread receiver([&] { + const auto start = std::chrono::steady_clock::now(); + entered = true; + if (call == ReceiveFrom) received = recvfrom_nid_postfix(watched, buffer, sizeof(buffer), 0, sender.data(), &senderSize); + else if (call == ReceiveMessage) received = recvmsg_nid_postfix(watched, &message, 0); + else if (call == Read) received = read_nid_postfix(watched, buffer, sizeof(buffer)); + else received = recv_nid_postfix(watched, buffer, sizeof(buffer), 0); + waited = std::chrono::steady_clock::now() - start; + returned = true; + }); + while (!entered) std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + Require(!returned); + Require(shutdown_nid_postfix(accepted, 0) == 0); + for (int poll = 0; poll < 100 && !returned; ++poll) std::this_thread::sleep_for(std::chrono::milliseconds(20)); + if (!returned) Require(shutdown_nid_postfix(client, 2) == 0); + receiver.join(); + Require(received == 0 && waited >= std::chrono::milliseconds(40) && waited < std::chrono::seconds(2)); + if (call == ReceiveFrom) Require(senderSize == 0); + if (call == ReceiveMessage) Require(message.nameLength == 0 && message.flags == 0); + if (call == ThroughDuplicate) Require(close_nid_postfix(watched) == 0); + for (const int descriptor : {client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0); + } +} + int main() { StreamReceiveReportsNoSender(); SocketNameFaults(); @@ -282,6 +336,7 @@ int main() { AcceptedSocketsFollowListenerBlocking(); WaitAllReceivesTheWholeRequest(); ReceiveAnsweredWithoutTheHost(); + BlockedReceiveEndsAtShutRead(); UnboundSocketNameIsZero(); const int listener = socket_nid_postfix(2, 1, 6); Require(listener >= 0); diff --git a/core/libs/tests/GuestUdp.cpp b/core/libs/tests/GuestUdp.cpp index e177213445..99b6c7a997 100644 --- a/core/libs/tests/GuestUdp.cpp +++ b/core/libs/tests/GuestUdp.cpp @@ -4,6 +4,7 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include #include #include #include @@ -221,6 +222,49 @@ static void CheckShutReadUnconnected() { Require(close_nid_postfix(receiver) == 0 && close_nid_postfix(sender) == 0); } +#ifndef __APPLE__ +static void CheckShutReadEndsBlockedReceive() { + for (const bool stamped : {false, true}) { + const int receiver = socket_nid_postfix(2, 2, 0); + const int sender = socket_nid_postfix(2, 2, 0); + Require(receiver >= 0 && sender >= 0); + std::array address{16, 2, 0, 0, 127, 0, 0, 1}; + Require(bind_nid_postfix(receiver, address.data(), address.size()) == 0); + std::uint32_t size = address.size(); + Require(getsockname_nid_postfix(receiver, address.data(), &size) == 0); + const int enabled = 1; + if (stamped) Require(setsockopt_nid_postfix(receiver, 0xffff, 0x400, &enabled, sizeof(enabled)) == 0); + std::atomic entered{false}; + std::atomic returned{false}; + std::int64_t received = -2; + std::array control{}; + std::uint32_t controlLength = 0; + std::chrono::steady_clock::duration waited{}; + std::thread waiter([&] { + char buffer[4]; + KernelIovec vector{buffer, sizeof(buffer)}; + GuestMsghdr message{nullptr, 0, &vector, 1, control.data(), static_cast(control.size()), -1}; + const auto start = std::chrono::steady_clock::now(); + entered = true; + received = stamped ? recvmsg_nid_postfix(receiver, &message, 0) : recv_nid_postfix(receiver, buffer, sizeof(buffer), 0); + waited = std::chrono::steady_clock::now() - start; + controlLength = message.controlLength; + returned = true; + }); + while (!entered) std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + Require(!returned); + Require(shutdown_nid_postfix(receiver, 0) == -1 && *__error_nid_postfix() == 57); + for (int poll = 0; poll < 100 && !returned; ++poll) std::this_thread::sleep_for(std::chrono::milliseconds(20)); + if (!returned) Require(sendto_nid_postfix(sender, "x", 1, 0, address.data(), address.size()) == 1); + waiter.join(); + Require(received == 0 && waited >= std::chrono::milliseconds(40) && waited < std::chrono::seconds(2)); + if (stamped) Require(controlLength == 0); + Require(close_nid_postfix(receiver) == 0 && close_nid_postfix(sender) == 0); + } +} +#endif + static void CheckDontWait() { const int receiver = socket_nid_postfix(2, 2, 0); const int sender = socket_nid_postfix(2, 2, 0); @@ -501,6 +545,9 @@ int main() { CheckDontWait(); CheckSendToPortZero(); CheckShutReadUnconnected(); +#ifndef __APPLE__ + CheckShutReadEndsBlockedReceive(); +#endif const int receiver = socket_nid_postfix(2, 2, 0); const int sender = socket_nid_postfix(2, 2, 17); Require(receiver >= 0 && sender >= 0 && receiver != sender); From 11f2ffcc4083b9acada3d22c014ee232363859d2 Mon Sep 17 00:00:00 2001 From: Evrim Cicek Date: Mon, 26 Oct 2026 16:32:26 +0300 Subject: [PATCH 25/46] fix(libkernel): end a blocked receive on Windows when its socket is shut for reading --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 68 ++++++++++++++++------ docs/dev/debt/libkernel.md | 3 +- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 11d3088b20..8e2ed3ec73 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -944,7 +944,15 @@ int SocketSendFlags(Socket& socket, int flags) { std::lock_guard lock(socket.modeMutex); return NativeSendFlags(socket.options.noSignal ? flags | GuestNoSignal : flags); } -int WaitForTransfer(Socket& socket, bool send, int flags) { +#ifdef _WIN32 +bool ReceiveWouldBlock(const Socket& socket) { + if (socket.type == 1) return PeerConnected(socket); + sockaddr_storage name{}; + socklen_t size = sizeof(name); + return ::getsockname(socket.value, reinterpret_cast(&name), &size) == 0; +} +#endif +int WaitForTransfer(Socket& socket, bool send, int flags, int receiver = -1) { if ((flags & GuestDontWait) != 0) { #ifdef _WIN32 NativePollDescriptor entry{}; @@ -961,22 +969,38 @@ int WaitForTransfer(Socket& socket, bool send, int flags) { if (socket.options.nonblocking) return 0; timeout = send ? socket.options.sendTimeout : socket.options.receiveTimeout; } - if (timeout == 0) return 0; +#ifdef _WIN32 + const bool watched = receiver >= 0 && ReceiveWouldBlock(socket); +#else + static_cast(receiver); + constexpr bool watched = false; +#endif + if (timeout == 0 && !watched) return 0; + const ReadWatch watch(watched ? std::vector{receiver} : std::vector{}); const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout >> 32) + std::chrono::nanoseconds((static_cast(static_cast(timeout)) * 1000000000) >> 32); for (;;) { - const auto left = deadline - std::chrono::steady_clock::now(); - if (left <= std::chrono::steady_clock::duration::zero()) return 35; - const int milliseconds = static_cast(std::min(std::chrono::ceil(left).count(), INT_MAX)); - NativePollDescriptor entry{}; - entry.fd = socket.value; + if (watched && ReceiveShut(socket)) return 0; + int milliseconds = -1; + if (timeout != 0) { + const auto left = deadline - std::chrono::steady_clock::now(); + if (left <= std::chrono::steady_clock::duration::zero()) return 35; + milliseconds = static_cast(std::min(std::chrono::ceil(left).count(), INT_MAX)); + } + NativePollDescriptor entries[2]{}; + entries[0].fd = socket.value; #ifdef _WIN32 - entry.events = static_cast(send ? POLLWRNORM : POLLRDNORM); - const int ready = WSAPoll(&entry, 1, milliseconds); - if (ready != 0) return 0; + entries[0].events = static_cast(send ? POLLWRNORM : POLLRDNORM); + if (watched) { + entries[1].fd = ThreadWaker().value; + entries[1].events = POLLRDNORM; + } + const int ready = WSAPoll(entries, watched ? 2 : 1, milliseconds); + if (ready < 0 || entries[0].revents != 0) return 0; + if (entries[1].revents != 0) DrainWaker(entries[1].fd); #else - entry.events = static_cast(send ? POLLOUT : POLLIN); - const int ready = ::poll(&entry, 1, milliseconds); + entries[0].events = static_cast(send ? POLLOUT : POLLIN); + const int ready = ::poll(entries, 1, milliseconds); if (ready > 0 || (ready < 0 && errno != EINTR)) return 0; #endif } @@ -1621,7 +1645,8 @@ std::int64_t APS5_VABI recv_nid_postfix(int descriptor, void* buffer, std::uint6 const GuestArena::HostWrite destination(length == 0 ? nullptr : buffer, static_cast(length)); if (!destination.Open()) return Fail(14); if (const int answered = ReceiveAnsweredHere(*socket, length); answered >= 0) return answered == 0 ? 0 : Fail(answered); - if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); + if (const int waited = WaitForTransfer(*socket, false, flags, descriptor)) return Fail(waited); + if (ReceiveShut(*socket)) return 0; const auto result = ::recv(socket->value, static_cast(buffer), static_cast(length), NativeReceiveFlags(flags, socket->type)); #ifdef _WIN32 @@ -1678,9 +1703,9 @@ int APS5_VABI shutdown_nid_postfix(int descriptor, int how) { sockaddr_storage peer{}; socklen_t size = sizeof(peer); const bool unconnected = socket->type == 2 && ::getpeername(socket->value, reinterpret_cast(&peer), &size) != 0; - if (::shutdown(socket->value, how) && !unconnected && !AlreadyShut(socket->value, how)) return Fail(NativeError()); { std::lock_guard lock(socket->modeMutex); + if (::shutdown(socket->value, how) && !unconnected && !AlreadyShut(socket->value, how)) return Fail(NativeError()); if (how != 1) socket->receiveShut = true; if (how != 0) socket->sendShut = true; } @@ -1861,7 +1886,11 @@ std::int64_t APS5_VABI recvfrom_nid_postfix(int descriptor, void* buffer, std::u } sockaddr_storage native{}; socklen_t size = sizeof(native); - if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); + if (const int waited = WaitForTransfer(*socket, false, flags, descriptor)) return Fail(waited); + if (ReceiveShut(*socket)) { + if (addressLength && !ReportAddress(address, addressLength, nullptr, address ? 0 : capacity)) return Fail(14); + return 0; + } std::uint64_t limit = length; #ifdef _WIN32 if (length != 0 && (limit = GuestPrefix(buffer, length, WritableProtection)) < length) { @@ -2187,11 +2216,12 @@ std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message, ReceivedRights rights; bool controlTruncated = false; #endif - const bool shut = ReceiveShut(*socket); + bool shut = ReceiveShut(*socket); std::vector stamps; if (!shut && (total != 0 || socket->type == 1 || ReadableNow(*socket))) { size = sizeof(native); - if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); + if (const int waited = WaitForTransfer(*socket, false, flags, descriptor)) return Fail(waited); + shut = ReceiveShut(*socket); #ifndef _WIN32 if (passing) { std::vector control(CMSG_SPACE(MostPassedDescriptors * sizeof(int))); @@ -2227,7 +2257,7 @@ std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message, if (const int error = rights.Install((flags & GuestControlCloseOnExec) != 0)) return Fail(error); } else #endif - received = ::recvfrom(socket->value, buffer.data(), static_cast(buffer.size()), + if (!shut) received = ::recvfrom(socket->value, buffer.data(), static_cast(buffer.size()), NativeReceiveFlags(flags, socket->type), reinterpret_cast(&native), &size); if (received < 0) { #ifdef _WIN32 @@ -2237,7 +2267,7 @@ std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message, return Fail(NativeError()); #endif } - if (header.control) { + if (header.control && !(received == 0 && ReceiveShut(*socket))) { int keptFlags = 0; { std::lock_guard lock(socket->modeMutex); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 08a150589a..ebbd688c3c 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -281,10 +281,11 @@ - Guest sockets on macOS ([the equeue wait, poll, kevent, fstat and ioctl](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp), [gethostbyname](../../../core/libs/prx/libScePosixForWebKit/Resolver.cpp)): macOS has no `ppoll`, so the wait uses `poll` with the timeout rounded up to a millisecond; it has no `POLLRDHUP`, so a stream that the peer closed is found by peeking one byte when it is readable; XNU's `poll` reports `POLLHUP`, with `POLLPRI` and without `POLLOUT`, once either direction of a stream has ended, so a guest stream socket it reports hung up is asked about each direction alone and keeps `POLLHUP` only once both have ended, as FreeBSD does; a call that asks for no event gets nothing, where FreeBSD reports `POLLHUP`; `FIONREAD` on a datagram socket, and the byte count that `fstat` and `kevent` report for it, is `SO_NREAD`, the size of the next datagram, because macOS adds the source address to `FIONREAD`; and `gethostbyname` replaces `gethostbyname_r`, which macOS lacks. - [recvmsg](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) loses received stream data on an output copy fault: outputs are checked after the host receive. FreeBSD 11.4 leaves unread stream data queued, though it also drops a datagram on a copy fault ([`soreceive_dgram`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L2308-L2312), [`soreceive_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1758-L1774)). A Linux `process_vm_readv` or `process_vm_writev` failure other than `EFAULT` throws - [MSG_DONTWAIT](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) in `send`, `sendto`, `sendmsg`, `recv`, `recvfrom` and `recvmsg` makes that one call non-blocking through the host's `MSG_DONTWAIT`, as FreeBSD 11.4's `soreceive_generic` returns `EWOULDBLOCK` for it (`sys/kern/uipc_socket.c`). Winsock has no per-call non-blocking flag, so on Windows the call first asks `WSAPoll` with a zero timeout whether the socket is ready and fails with `EAGAIN` when it is not. The check and the transfer are two host calls: a second thread that takes the data in between can leave a `recv` blocked, and a `send` of more bytes than the socket buffer holds can still block until it has sent them, where FreeBSD's `sosend_generic` returns a partial count -- [MSG_WAITALL](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) in `recv`, `recvfrom` and `recvmsg` on a stream socket is passed to the host, which returns when the buffer is full, the peer closes or an error occurs, as FreeBSD 11.4's [`soreceive_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1855-L1860) does; a datagram socket ignores it. It is not measured on a console, a nonblocking socket and a call that also passes `MSG_PEEK` follow the host's rules, and `SO_RCVTIMEO` bounds only the wait for the first data. Any other flag still fails with `EOPNOTSUPP`. +- [MSG_WAITALL](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) in `recv`, `recvfrom` and `recvmsg` on a stream socket is passed to the host, which returns when the buffer is full, the peer closes or an error occurs, as FreeBSD 11.4's [`soreceive_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1855-L1860) does; a datagram socket ignores it. It is not measured on a console, a nonblocking socket and a call that also passes `MSG_PEEK` follow the host's rules, and `SO_RCVTIMEO` bounds only the wait for the first data. On Windows, `shutdown(SHUT_RD)` from another thread does not end a wait that already holds part of the data, where that loop stops at `SBS_CANTRCVMORE` and returns it. Any other flag still fails with `EOPNOTSUPP`. - [recv and recvfrom](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answer some cases without the host. A zero-length receive on a stream socket returns 0, or `ENOTCONN` when it is not connected ([L1566-L1578](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1566-L1578)), as on a PS5 (firmware 13.60). On an empty datagram socket it returns 0 at once; the console was measured only with `MSG_DONTWAIT`, and the blocking case follows FreeBSD 11.4's `soreceive_dgram`, which returns 0 before waiting. `recvmsg` answers the empty datagram socket the same way but asks the host in the stream cases. A datagram another thread takes after the check makes a zero-length receive wait - [`shutdown(SHUT_RD)`](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) on a Windows TCP socket resets the connection when data is unread or arrives later, so the peer's next send fails with `EPIPE`, where FreeBSD's [`sorflush`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L2387-L2432) discards the data and the peer's sends succeed. The reset also makes `poll` report `POLLHUP` and `POLLERR` beside `POLLIN` for the shut socket, where FreeBSD's [`sopoll_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L3060-L3103) reports only `POLLIN` and `POLLRDNORM`. On a Unix stream socket FreeBSD keeps appending the peer's later data ([`uipc_send`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_usrreq.c#L936-L1001) does not check the receiver's `SBS_CANTRCVMORE`) and receives return it; here receives return 0 and the peer's send fails with `EPIPE` on Linux. - [sendmsg and recvmsg](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) pass descriptors in `SCM_RIGHTS` messages on Unix-domain sockets through the host's `SCM_RIGHTS` (Linux only), in FreeBSD's guest layout: a 12-byte `cmsghdr`, data at offset 16, `CMSG_SPACE` records. Several `SCM_RIGHTS` messages in one `sendmsg` arrive as one record because the host merges them (FreeBSD keeps one per message), so a buffer sized for the first can't receive it alone. A received socket gets default emulated socket options. Sending a guest kqueue returns `EOPNOTSUPP`, as FreeBSD's `unp_internalize` does; a random device, directory or JIT shared memory descriptor (libkernel's state) throws, as does a zero-byte stream send with descriptors (Linux carries no record without data). Windows throws (socket pairs are loopback TCP), as do other control message types and control data on IP sockets. `MSG_PEEK` with a control buffer reads only the data and leaves passed descriptors queued. +- [Blocking receives](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) - `shutdown(SHUT_RD)` from another thread does not end every `recv`, `recvfrom` or `recvmsg` blocked on the socket, where FreeBSD's [`sorflush`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L2403-L2409) wakes them all. On Windows it misses a receive whose data a third thread took between its `WSAPoll` wait and the host receive, and one whose descriptor was closed or replaced while it waited when the shutdown goes through another descriptor of the socket. On macOS it misses a receive on an unconnected datagram socket: XNU's [`soshutdown`](https://github.com/apple-oss-distributions/xnu/blob/xnu-12377.121.6/bsd/kern/uipc_socket.c#L4351-L4356) fails with `ENOTCONN` without flushing. A `recvmsg` that takes a zero-length datagram as `SHUT_RD` lands returns it without its `SO_TIMESTAMP` or `SO_BINTIME` record. - [send, sendto](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) - send and sendto without an address on an unconnected datagram socket map Windows `WSAENOTCONN` to FreeBSD's `EDESTADDRREQ` (39). - [bind, connect, sendto](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) - a connected stream socket's `sendto` ignores the address once its length passes, as FreeBSD 11.4's `tcp_usr_send` does, which was not measured. Otherwise they check a socket address as a payload process on a PS5 (firmware 13.60) did on datagram and stream sockets: a length below 2 fails with `EINVAL` and above 255 with `ENAMETOOLONG`; then an `AF_INET` socket needs exactly 16 bytes and an `AF_INET6` socket 28, or `EINVAL`, before the family is looked at (an `AF_INET6` address of 28 or 10 bytes on an `AF_INET` socket fails with `EINVAL`); then another family, `AF_UNSPEC` with `INADDR_ANY` included, fails with `EAFNOSUPPORT`; and `connect` on a listening stream socket fails with `EOPNOTSUPP` - [bind, connect](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) on an `AF_INET6` datagram socket with `IPV6_V6ONLY` cleared accept an IPv4-mapped address longer than 28 bytes and use its first 28, as FreeBSD 11.4's `udp6_bind` and `udp6_connect` hand it to `in_pcbbind` and `in_pcbconnect` as a `sockaddr_in` without the length test of `in6_pcbbind`; `sendto` still fails it with `EINVAL`, as `udp6_send` tests the length first. The console was measured only with `::1` and `IPV6_V6ONLY` at its default, so this case is not measured From fba8413cfaf608681999375b2155ad4bed94d31a Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Fri, 6 Nov 2026 07:07:10 +1300 Subject: [PATCH 26/46] docs(debt): drop a reference to an outside project Remove the reference to an outside project from the debt entry; nothing else changes. Co-authored-by: Milos-Opletal <87488948+Milos-Opletal@users.noreply.github.com> --- docs/dev/debt/libSceAppContent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index 160869a9a8..5c413cad89 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -8,7 +8,7 @@ | --- | --- | | `sceAppContentDownloadDataGetBlockSize` | `9ht66cHTj7g` | -- [sceAppContentDownloadDataGetAvailableSpaceKb](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). KytyPS5 does not special-case it, and shadPS4 stubs 1048576 KB. One disassembled title (PPSA15246) calls it with the `/download0` mount point and a zeroed 64-bit output, ignores the return value and multiplies the output by 1024, so for that title an error that leaves the output untouched and an OK with 0 KB look the same. `/download1` and `/download2` queries reflect their configured quota. +- [sceAppContentDownloadDataGetAvailableSpaceKb](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). shadPS4 stubs 1048576 KB. One disassembled title (PPSA15246) calls it with the `/download0` mount point and a zeroed 64-bit output, ignores the return value and multiplies the output by 1024, so for that title an error that leaves the output untouched and an OK with 0 KB look the same. `/download1` and `/download2` queries reflect their configured quota. - [sceAppContentDownloadDataFormat](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - empties the `/download0`, `/download1`, or `/download2` directory, creating it if needed. Imported by PPSA01317. A null mount point returns `PARAMETER` and another mount point returns `NOT_MOUNTED` as in `sceAppContentTemporaryDataFormat` and `sceAppContentDownloadDataGetAvailableSpaceKb`; the console's error codes for both and its behaviour with files still open are unverified - [sceAppContentDownload0Expand, sceAppContentDownload0Shrink, sceAppContentDownload1Shrink and sceAppContentDownload2Shrink](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - partition expansion and shrink behaviour on hardware is unverified. `sceAppContentDownload1Expand` and `sceAppContentDownload2Expand` expand `/download1` and `/download2` and update their reported quotas; the other partition expansion and shrink functions throw through `NotImplemented_nid_no_patch`. From ba4d5848271ef34e2a01d23faf70000333fb5a38 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 04:57:12 +0200 Subject: [PATCH 27/46] feat(libSceLibreSsl): implement SSL_get_session --- core/libs/prx/libSceLibreSsl/CMakeLists.txt | 13 +++- core/libs/prx/libSceLibreSsl/Export.cpp | 4 -- .../prx/libSceLibreSsl/src/SslSession.cpp | 62 +++++++++++++++++ core/libs/tests/GuestLibreSslSslSession.cpp | 67 +++++++++++++++++++ docs/dev/debt/libSceLibreSsl.md | 1 + 5 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 core/libs/prx/libSceLibreSsl/src/SslSession.cpp create mode 100644 core/libs/tests/GuestLibreSslSslSession.cpp diff --git a/core/libs/prx/libSceLibreSsl/CMakeLists.txt b/core/libs/prx/libSceLibreSsl/CMakeLists.txt index fb18e93d52..8d04a71532 100644 --- a/core/libs/prx/libSceLibreSsl/CMakeLists.txt +++ b/core/libs/prx/libSceLibreSsl/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Bignum.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509StoreContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipherCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Evp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/OcspResponse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpAes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509Name.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ssl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SslCtx.cpp) +add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Bignum.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509StoreContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipherCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Evp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/OcspResponse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpAes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509Name.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ssl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SslCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SslSession.cpp) target_include_directories(libSceLibreSsl PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceLibreSsl PRIVATE mbedcrypto) configure_windows_unwind(libSceLibreSsl) @@ -271,6 +271,17 @@ if(BUILD_TESTING) set_tests_properties(guest_libressl_ocsp_response PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + + add_test_executable(guest_libressl_ssl_session_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslSslSession.cpp) + target_include_directories(guest_libressl_ssl_session_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_libressl_ssl_session_tests PRIVATE libSceLibreSsl) + configure_windows_unwind(guest_libressl_ssl_session_tests) + add_test(NAME guest_libressl_ssl_session COMMAND guest_libressl_ssl_session_tests) + set_tests_properties(guest_libressl_ssl_session PROPERTIES TIMEOUT 30) + if(WIN32) + set_tests_properties(guest_libressl_ssl_session PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() endif() target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Cipher.cpp) diff --git a/core/libs/prx/libSceLibreSsl/Export.cpp b/core/libs/prx/libSceLibreSsl/Export.cpp index 5b0d8bee9c..872cabf408 100644 --- a/core/libs/prx/libSceLibreSsl/Export.cpp +++ b/core/libs/prx/libSceLibreSsl/Export.cpp @@ -688,10 +688,6 @@ int APS5_VABI SSL_get_servername_nid_postfix(void) { return 0; } -int APS5_VABI SSL_get_session_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} int APS5_VABI SSL_library_init_nid_postfix(void) { return 1; diff --git a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp new file mode 100644 index 0000000000..8827d85d87 --- /dev/null +++ b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include "prx/libc/include/General.hpp" + +struct LibreSslCertKey { + void* x509; + void* privatekey; + const void* digest; +}; + +struct LibreSslCert { + LibreSslCertKey* key; + unsigned char rest[248]; +}; + +struct LibreSslSession { + unsigned char beforeCipher[168]; + const void* cipher; + unsigned char rest[56]; +}; + +struct LibreSslSessionConnection { + unsigned char beforeCert[80]; + LibreSslCert* cert; + unsigned char beforeSession[40]; + LibreSslSession* session; + unsigned char rest[96]; +}; + +static_assert(sizeof(LibreSslCertKey) == 24); +static_assert(offsetof(LibreSslCertKey, privatekey) == 8); +static_assert(sizeof(LibreSslCert) == 256); +static_assert(sizeof(LibreSslSession) == 232); +static_assert(offsetof(LibreSslSession, cipher) == 168); +static_assert(sizeof(LibreSslSessionConnection) == 232); +static_assert(offsetof(LibreSslSessionConnection, cert) == 80); +static_assert(offsetof(LibreSslSessionConnection, session) == 128); + +namespace { + +const LibreSslSessionConnection& Connection(const LibreSslSessionConnection* ssl, const char* function) { + if (!ssl) throw std::invalid_argument(std::string(function) + ": null connection"); + return *ssl; +} + +const LibreSslCertKey* CurrentKey(const LibreSslSessionConnection* ssl, const char* function) { + const LibreSslCert* cert = Connection(ssl, function).cert; + if (!cert) return nullptr; + if (!cert->key) throw std::invalid_argument(std::string(function) + ": null current key"); + return cert->key; +} + +} + +extern "C" { + +LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSessionConnection* ssl) { + return Connection(ssl, __func__).session; +} + +} diff --git a/core/libs/tests/GuestLibreSslSslSession.cpp b/core/libs/tests/GuestLibreSslSslSession.cpp new file mode 100644 index 0000000000..41b22a4bb8 --- /dev/null +++ b/core/libs/tests/GuestLibreSslSslSession.cpp @@ -0,0 +1,67 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include + +struct TestCertKey { + void* x509; + void* privatekey; + const void* digest; +}; + +struct TestCert { + TestCertKey* key; + TestCertKey pkeys[8]; + unsigned char rest[56]; +}; + +struct TestSession { + unsigned char beforeCipher[168]; + const void* cipher; + unsigned char rest[56]; +}; + +struct TestSsl { + unsigned char beforeCert[80]; + TestCert* cert; + unsigned char beforeSession[40]; + TestSession* session; + unsigned char rest[96]; +}; + +static_assert(sizeof(TestCertKey) == 24 && sizeof(TestCert) == 256); +static_assert(sizeof(TestSession) == 232 && offsetof(TestSession, cipher) == 168); +static_assert(sizeof(TestSsl) == 232 && offsetof(TestSsl, cert) == 80 && offsetof(TestSsl, session) == 128); + +extern "C" { +TestSession* APS5_VABI SSL_get_session_nid_postfix(const TestSsl*); +} + +static void Require(bool condition, std::source_location location = std::source_location::current()) { + if (condition) return; + std::fprintf(stderr, "LibreSSL SSL session check failed at line %u\n", location.line()); + std::abort(); +} + +template static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) { + try { action(); } catch (const std::exception&) { return; } + Require(false, location); +} + +static void TestGetSession() { + TestSession session {}; + TestCert cert {}; + TestSsl ssl {}; + RequireThrows([] { SSL_get_session_nid_postfix(nullptr); }); + Require(SSL_get_session_nid_postfix(&ssl) == nullptr); + ssl.cert = &cert; + ssl.session = &session; + Require(SSL_get_session_nid_postfix(&ssl) == &session); +} + +int main() { + TestGetSession(); + return 0; +} diff --git a/docs/dev/debt/libSceLibreSsl.md b/docs/dev/debt/libSceLibreSsl.md index 2f7ae12703..c3c7c10d06 100644 --- a/docs/dev/debt/libSceLibreSsl.md +++ b/docs/dev/debt/libSceLibreSsl.md @@ -45,6 +45,7 @@ - [EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm](../../../core/libs/prx/libSceLibreSsl/src/EvpAes.cpp) (libSceLibreSsl) - nothing is checked on the console. The descriptors hold the values of [LibreSSL 2.5.4's e_aes.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/e_aes.c) (NID, 12-byte IV, flags, the 680-byte LP64 `ctx_size`) with host `init`, `do_cipher`, `cleanup` and `ctrl`, and the cipher data has the port's own layout, so a title reading it sees different bytes. `aes_gcm_ctrl`, the TLS record path and [gcm128.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/modes/gcm128.c) are followed; `EVP_CTRL_GCM_SET_IV_FIXED` fills the IV from host entropy. A key length the AES setup rejects throws where LibreSSL keeps a stale schedule, as do missing cipher data, null buffers, `EVP_CTRL_GCM_IV_GEN` with an IV under 8 bytes and an invocation field longer than the IV - [EVP_cleanup](../../../core/libs/prx/libSceLibreSsl/src/EvpCipherCtx.cpp) (libSceLibreSsl) - nothing is checked on the console. [LibreSSL 2.5.4's names.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/names.c) empties the cipher and digest name tables, the PBE table, a deferred `OBJ_cleanup` and the signature-ID tables. Nothing in this module can fill them yet, because `OPENSSL_add_all_algorithms_noconf`, `CONF_modules_load_file` and `OPENSSL_load_builtin_modules` are not implemented, `SSL_library_init` returns 1 without adding a cipher or digest, `PKCS12_PBE_add` is empty as in LibreSSL 2.5.4, and no cipher or digest can be registered under an `OBJ_create` NID to defer `OBJ_cleanup`, so `EVP_cleanup` returns without doing anything; it must clear those tables once one of them fills them. The static cipher descriptors stay valid afterwards, as in LibreSSL - [OCSP_response_status, OCSP_single_get0_status](../../../core/libs/prx/libSceLibreSsl/src/OcspResponse.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto) `ocsp.h`, `ocsp_cl.c` and `a_enum.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module decodes a response yet, so only structures the caller builds reach them. Enumerated values follow `ASN1_ENUMERATED_get` (0 for a null value or null data, -1 for another type or more than 8 bytes), truncated to `int`. A null response, a null certificate status, and a null revocation record when its time or reason is requested, which LibreSSL dereferences, throw +- [SSL_get_session](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach it. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw - [OCSP_resp_count and OCSP_resp_get0](../../../core/libs/prx/libSceLibreSsl/src/OcspBasicResponse.cpp) - not verified on the console. The signatures, the guest `OCSP_BASICRESP`, `OCSP_RESPDATA` and stack layouts and the results follow LibreSSL 2.5.4's [ocsp.h and ocsp_cl.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ocsp) and [stack.h and stack.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/stack), the version in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware layouts remain unverified. A null response data pointer, and a null entry array with an index in range, throw where that release dereferences them. From 6b1511aba2748a0dd9deb130e2971f04a49a143e Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 04:58:14 +0200 Subject: [PATCH 28/46] feat(libSceLibreSsl): implement SSL_get_current_cipher --- core/libs/prx/libSceLibreSsl/Export.cpp | 5 ----- core/libs/prx/libSceLibreSsl/src/SslSession.cpp | 5 +++++ core/libs/tests/GuestLibreSslSslSession.cpp | 14 ++++++++++++++ docs/dev/debt/libSceLibreSsl.md | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl/Export.cpp b/core/libs/prx/libSceLibreSsl/Export.cpp index 872cabf408..4e6a6d7dae 100644 --- a/core/libs/prx/libSceLibreSsl/Export.cpp +++ b/core/libs/prx/libSceLibreSsl/Export.cpp @@ -648,11 +648,6 @@ int APS5_VABI SSL_get_certificate_nid_postfix(void) { return 0; } -int APS5_VABI SSL_get_current_cipher_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI SSL_get_error_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp index 8827d85d87..5436730d69 100644 --- a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp +++ b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp @@ -59,4 +59,9 @@ LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSessionConn return Connection(ssl, __func__).session; } +const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSessionConnection* ssl) { + const LibreSslSession* session = Connection(ssl, __func__).session; + return session ? session->cipher : nullptr; +} + } diff --git a/core/libs/tests/GuestLibreSslSslSession.cpp b/core/libs/tests/GuestLibreSslSslSession.cpp index 41b22a4bb8..73420dba3d 100644 --- a/core/libs/tests/GuestLibreSslSslSession.cpp +++ b/core/libs/tests/GuestLibreSslSslSession.cpp @@ -37,6 +37,7 @@ static_assert(sizeof(TestSsl) == 232 && offsetof(TestSsl, cert) == 80 && offseto extern "C" { TestSession* APS5_VABI SSL_get_session_nid_postfix(const TestSsl*); +const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const TestSsl*); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -61,7 +62,20 @@ static void TestGetSession() { Require(SSL_get_session_nid_postfix(&ssl) == &session); } +static void TestCurrentCipher() { + int cipher = 0; + TestSession session {}; + TestSsl ssl {}; + RequireThrows([] { SSL_get_current_cipher_nid_postfix(nullptr); }); + Require(SSL_get_current_cipher_nid_postfix(&ssl) == nullptr); + ssl.session = &session; + Require(SSL_get_current_cipher_nid_postfix(&ssl) == nullptr); + session.cipher = &cipher; + Require(SSL_get_current_cipher_nid_postfix(&ssl) == &cipher); +} + int main() { TestGetSession(); + TestCurrentCipher(); return 0; } diff --git a/docs/dev/debt/libSceLibreSsl.md b/docs/dev/debt/libSceLibreSsl.md index c3c7c10d06..9ac9029f89 100644 --- a/docs/dev/debt/libSceLibreSsl.md +++ b/docs/dev/debt/libSceLibreSsl.md @@ -45,7 +45,7 @@ - [EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm](../../../core/libs/prx/libSceLibreSsl/src/EvpAes.cpp) (libSceLibreSsl) - nothing is checked on the console. The descriptors hold the values of [LibreSSL 2.5.4's e_aes.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/e_aes.c) (NID, 12-byte IV, flags, the 680-byte LP64 `ctx_size`) with host `init`, `do_cipher`, `cleanup` and `ctrl`, and the cipher data has the port's own layout, so a title reading it sees different bytes. `aes_gcm_ctrl`, the TLS record path and [gcm128.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/modes/gcm128.c) are followed; `EVP_CTRL_GCM_SET_IV_FIXED` fills the IV from host entropy. A key length the AES setup rejects throws where LibreSSL keeps a stale schedule, as do missing cipher data, null buffers, `EVP_CTRL_GCM_IV_GEN` with an IV under 8 bytes and an invocation field longer than the IV - [EVP_cleanup](../../../core/libs/prx/libSceLibreSsl/src/EvpCipherCtx.cpp) (libSceLibreSsl) - nothing is checked on the console. [LibreSSL 2.5.4's names.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/names.c) empties the cipher and digest name tables, the PBE table, a deferred `OBJ_cleanup` and the signature-ID tables. Nothing in this module can fill them yet, because `OPENSSL_add_all_algorithms_noconf`, `CONF_modules_load_file` and `OPENSSL_load_builtin_modules` are not implemented, `SSL_library_init` returns 1 without adding a cipher or digest, `PKCS12_PBE_add` is empty as in LibreSSL 2.5.4, and no cipher or digest can be registered under an `OBJ_create` NID to defer `OBJ_cleanup`, so `EVP_cleanup` returns without doing anything; it must clear those tables once one of them fills them. The static cipher descriptors stay valid afterwards, as in LibreSSL - [OCSP_response_status, OCSP_single_get0_status](../../../core/libs/prx/libSceLibreSsl/src/OcspResponse.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto) `ocsp.h`, `ocsp_cl.c` and `a_enum.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module decodes a response yet, so only structures the caller builds reach them. Enumerated values follow `ASN1_ENUMERATED_get` (0 for a null value or null data, -1 for another type or more than 8 bytes), truncated to `int`. A null response, a null certificate status, and a null revocation record when its time or reason is requested, which LibreSSL dereferences, throw -- [SSL_get_session](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach it. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw +- [SSL_get_session, SSL_get_current_cipher](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach them. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw - [OCSP_resp_count and OCSP_resp_get0](../../../core/libs/prx/libSceLibreSsl/src/OcspBasicResponse.cpp) - not verified on the console. The signatures, the guest `OCSP_BASICRESP`, `OCSP_RESPDATA` and stack layouts and the results follow LibreSSL 2.5.4's [ocsp.h and ocsp_cl.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ocsp) and [stack.h and stack.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/stack), the version in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware layouts remain unverified. A null response data pointer, and a null entry array with an index in range, throw where that release dereferences them. From 4cbd76f64f175d350d2abd6a1dc93600f38fa762 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 04:58:24 +0200 Subject: [PATCH 29/46] feat(libSceLibreSsl): implement SSL_get_certificate --- core/libs/prx/libSceLibreSsl/Export.cpp | 4 ---- .../prx/libSceLibreSsl/src/SslSession.cpp | 5 +++++ core/libs/tests/GuestLibreSslSslSession.cpp | 21 +++++++++++++++++++ docs/dev/debt/libSceLibreSsl.md | 2 +- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl/Export.cpp b/core/libs/prx/libSceLibreSsl/Export.cpp index 4e6a6d7dae..c1bc1fe882 100644 --- a/core/libs/prx/libSceLibreSsl/Export.cpp +++ b/core/libs/prx/libSceLibreSsl/Export.cpp @@ -643,10 +643,6 @@ int APS5_VABI SSL_get1_session_nid_postfix(void) { return 0; } -int APS5_VABI SSL_get_certificate_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} int APS5_VABI SSL_get_error_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); diff --git a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp index 5436730d69..a953a52ba0 100644 --- a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp +++ b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp @@ -64,4 +64,9 @@ const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSessionCo return session ? session->cipher : nullptr; } +void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSessionConnection* ssl) { + const LibreSslCertKey* key = CurrentKey(ssl, __func__); + return key ? key->x509 : nullptr; +} + } diff --git a/core/libs/tests/GuestLibreSslSslSession.cpp b/core/libs/tests/GuestLibreSslSslSession.cpp index 73420dba3d..c89a2ea8e1 100644 --- a/core/libs/tests/GuestLibreSslSslSession.cpp +++ b/core/libs/tests/GuestLibreSslSslSession.cpp @@ -38,6 +38,7 @@ static_assert(sizeof(TestSsl) == 232 && offsetof(TestSsl, cert) == 80 && offseto extern "C" { TestSession* APS5_VABI SSL_get_session_nid_postfix(const TestSsl*); const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const TestSsl*); +void* APS5_VABI SSL_get_certificate_nid_postfix(const TestSsl*); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -74,8 +75,28 @@ static void TestCurrentCipher() { Require(SSL_get_current_cipher_nid_postfix(&ssl) == &cipher); } +static void TestGetCertificate() { + int x509 = 0; + int other = 0; + int key = 0; + TestCert cert {}; + TestSsl ssl {}; + RequireThrows([] { SSL_get_certificate_nid_postfix(nullptr); }); + Require(SSL_get_certificate_nid_postfix(&ssl) == nullptr); + ssl.cert = &cert; + RequireThrows([&] { SSL_get_certificate_nid_postfix(&ssl); }); + cert.pkeys[0].x509 = &other; + cert.pkeys[2].x509 = &x509; + cert.pkeys[2].privatekey = &key; + cert.key = &cert.pkeys[1]; + Require(SSL_get_certificate_nid_postfix(&ssl) == nullptr); + cert.key = &cert.pkeys[2]; + Require(SSL_get_certificate_nid_postfix(&ssl) == &x509); +} + int main() { TestGetSession(); TestCurrentCipher(); + TestGetCertificate(); return 0; } diff --git a/docs/dev/debt/libSceLibreSsl.md b/docs/dev/debt/libSceLibreSsl.md index 9ac9029f89..2dd6c9ef6b 100644 --- a/docs/dev/debt/libSceLibreSsl.md +++ b/docs/dev/debt/libSceLibreSsl.md @@ -45,7 +45,7 @@ - [EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm](../../../core/libs/prx/libSceLibreSsl/src/EvpAes.cpp) (libSceLibreSsl) - nothing is checked on the console. The descriptors hold the values of [LibreSSL 2.5.4's e_aes.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/e_aes.c) (NID, 12-byte IV, flags, the 680-byte LP64 `ctx_size`) with host `init`, `do_cipher`, `cleanup` and `ctrl`, and the cipher data has the port's own layout, so a title reading it sees different bytes. `aes_gcm_ctrl`, the TLS record path and [gcm128.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/modes/gcm128.c) are followed; `EVP_CTRL_GCM_SET_IV_FIXED` fills the IV from host entropy. A key length the AES setup rejects throws where LibreSSL keeps a stale schedule, as do missing cipher data, null buffers, `EVP_CTRL_GCM_IV_GEN` with an IV under 8 bytes and an invocation field longer than the IV - [EVP_cleanup](../../../core/libs/prx/libSceLibreSsl/src/EvpCipherCtx.cpp) (libSceLibreSsl) - nothing is checked on the console. [LibreSSL 2.5.4's names.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/names.c) empties the cipher and digest name tables, the PBE table, a deferred `OBJ_cleanup` and the signature-ID tables. Nothing in this module can fill them yet, because `OPENSSL_add_all_algorithms_noconf`, `CONF_modules_load_file` and `OPENSSL_load_builtin_modules` are not implemented, `SSL_library_init` returns 1 without adding a cipher or digest, `PKCS12_PBE_add` is empty as in LibreSSL 2.5.4, and no cipher or digest can be registered under an `OBJ_create` NID to defer `OBJ_cleanup`, so `EVP_cleanup` returns without doing anything; it must clear those tables once one of them fills them. The static cipher descriptors stay valid afterwards, as in LibreSSL - [OCSP_response_status, OCSP_single_get0_status](../../../core/libs/prx/libSceLibreSsl/src/OcspResponse.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto) `ocsp.h`, `ocsp_cl.c` and `a_enum.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module decodes a response yet, so only structures the caller builds reach them. Enumerated values follow `ASN1_ENUMERATED_get` (0 for a null value or null data, -1 for another type or more than 8 bytes), truncated to `int`. A null response, a null certificate status, and a null revocation record when its time or reason is requested, which LibreSSL dereferences, throw -- [SSL_get_session, SSL_get_current_cipher](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach them. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw +- [SSL_get_session, SSL_get_current_cipher, SSL_get_certificate](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach them. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw - [OCSP_resp_count and OCSP_resp_get0](../../../core/libs/prx/libSceLibreSsl/src/OcspBasicResponse.cpp) - not verified on the console. The signatures, the guest `OCSP_BASICRESP`, `OCSP_RESPDATA` and stack layouts and the results follow LibreSSL 2.5.4's [ocsp.h and ocsp_cl.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ocsp) and [stack.h and stack.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/stack), the version in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware layouts remain unverified. A null response data pointer, and a null entry array with an index in range, throw where that release dereferences them. From d855f060cd8b5b835885b09c599fbfb33b6f8d86 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 04:58:34 +0200 Subject: [PATCH 30/46] feat(libSceLibreSsl): implement SSL_get_privatekey --- core/libs/prx/libSceLibreSsl/Export.cpp | 4 ---- .../prx/libSceLibreSsl/src/SslSession.cpp | 5 +++++ core/libs/tests/GuestLibreSslSslSession.cpp | 21 +++++++++++++++++++ docs/dev/debt/libSceLibreSsl.md | 2 +- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl/Export.cpp b/core/libs/prx/libSceLibreSsl/Export.cpp index c1bc1fe882..9550157b8c 100644 --- a/core/libs/prx/libSceLibreSsl/Export.cpp +++ b/core/libs/prx/libSceLibreSsl/Export.cpp @@ -669,10 +669,6 @@ int APS5_VABI SSL_get_peer_certificate_nid_postfix(void) { return 0; } -int APS5_VABI SSL_get_privatekey_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} int APS5_VABI SSL_get_servername_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); diff --git a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp index a953a52ba0..e231deea71 100644 --- a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp +++ b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp @@ -69,4 +69,9 @@ void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSessionConnection* return key ? key->x509 : nullptr; } +void* APS5_VABI SSL_get_privatekey_nid_postfix(const LibreSslSessionConnection* ssl) { + const LibreSslCertKey* key = CurrentKey(ssl, __func__); + return key ? key->privatekey : nullptr; +} + } diff --git a/core/libs/tests/GuestLibreSslSslSession.cpp b/core/libs/tests/GuestLibreSslSslSession.cpp index c89a2ea8e1..874babcccd 100644 --- a/core/libs/tests/GuestLibreSslSslSession.cpp +++ b/core/libs/tests/GuestLibreSslSslSession.cpp @@ -39,6 +39,7 @@ extern "C" { TestSession* APS5_VABI SSL_get_session_nid_postfix(const TestSsl*); const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const TestSsl*); void* APS5_VABI SSL_get_certificate_nid_postfix(const TestSsl*); +void* APS5_VABI SSL_get_privatekey_nid_postfix(const TestSsl*); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -94,9 +95,29 @@ static void TestGetCertificate() { Require(SSL_get_certificate_nid_postfix(&ssl) == &x509); } +static void TestGetPrivateKey() { + int x509 = 0; + int key = 0; + int other = 0; + TestCert cert {}; + TestSsl ssl {}; + RequireThrows([] { SSL_get_privatekey_nid_postfix(nullptr); }); + Require(SSL_get_privatekey_nid_postfix(&ssl) == nullptr); + ssl.cert = &cert; + RequireThrows([&] { SSL_get_privatekey_nid_postfix(&ssl); }); + cert.pkeys[0].privatekey = &other; + cert.pkeys[3].x509 = &x509; + cert.pkeys[3].privatekey = &key; + cert.key = &cert.pkeys[1]; + Require(SSL_get_privatekey_nid_postfix(&ssl) == nullptr); + cert.key = &cert.pkeys[3]; + Require(SSL_get_privatekey_nid_postfix(&ssl) == &key); +} + int main() { TestGetSession(); TestCurrentCipher(); TestGetCertificate(); + TestGetPrivateKey(); return 0; } diff --git a/docs/dev/debt/libSceLibreSsl.md b/docs/dev/debt/libSceLibreSsl.md index 2dd6c9ef6b..2e5707d3f8 100644 --- a/docs/dev/debt/libSceLibreSsl.md +++ b/docs/dev/debt/libSceLibreSsl.md @@ -45,7 +45,7 @@ - [EVP_aes_128_gcm, EVP_aes_192_gcm, EVP_aes_256_gcm](../../../core/libs/prx/libSceLibreSsl/src/EvpAes.cpp) (libSceLibreSsl) - nothing is checked on the console. The descriptors hold the values of [LibreSSL 2.5.4's e_aes.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/e_aes.c) (NID, 12-byte IV, flags, the 680-byte LP64 `ctx_size`) with host `init`, `do_cipher`, `cleanup` and `ctrl`, and the cipher data has the port's own layout, so a title reading it sees different bytes. `aes_gcm_ctrl`, the TLS record path and [gcm128.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/modes/gcm128.c) are followed; `EVP_CTRL_GCM_SET_IV_FIXED` fills the IV from host entropy. A key length the AES setup rejects throws where LibreSSL keeps a stale schedule, as do missing cipher data, null buffers, `EVP_CTRL_GCM_IV_GEN` with an IV under 8 bytes and an invocation field longer than the IV - [EVP_cleanup](../../../core/libs/prx/libSceLibreSsl/src/EvpCipherCtx.cpp) (libSceLibreSsl) - nothing is checked on the console. [LibreSSL 2.5.4's names.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/names.c) empties the cipher and digest name tables, the PBE table, a deferred `OBJ_cleanup` and the signature-ID tables. Nothing in this module can fill them yet, because `OPENSSL_add_all_algorithms_noconf`, `CONF_modules_load_file` and `OPENSSL_load_builtin_modules` are not implemented, `SSL_library_init` returns 1 without adding a cipher or digest, `PKCS12_PBE_add` is empty as in LibreSSL 2.5.4, and no cipher or digest can be registered under an `OBJ_create` NID to defer `OBJ_cleanup`, so `EVP_cleanup` returns without doing anything; it must clear those tables once one of them fills them. The static cipher descriptors stay valid afterwards, as in LibreSSL - [OCSP_response_status, OCSP_single_get0_status](../../../core/libs/prx/libSceLibreSsl/src/OcspResponse.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto) `ocsp.h`, `ocsp_cl.c` and `a_enum.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module decodes a response yet, so only structures the caller builds reach them. Enumerated values follow `ASN1_ENUMERATED_get` (0 for a null value or null data, -1 for another type or more than 8 bytes), truncated to `int`. A null response, a null certificate status, and a null revocation record when its time or reason is requested, which LibreSSL dereferences, throw -- [SSL_get_session, SSL_get_current_cipher, SSL_get_certificate](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach them. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw +- [SSL_get_session, SSL_get_current_cipher, SSL_get_certificate, SSL_get_privatekey](../../../core/libs/prx/libSceLibreSsl/src/SslSession.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h`, `ssl_lib.c` and `ssl_sess.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach them. A null connection, and a certificate set without a current key, which LibreSSL dereferences, throw - [OCSP_resp_count and OCSP_resp_get0](../../../core/libs/prx/libSceLibreSsl/src/OcspBasicResponse.cpp) - not verified on the console. The signatures, the guest `OCSP_BASICRESP`, `OCSP_RESPDATA` and stack layouts and the results follow LibreSSL 2.5.4's [ocsp.h and ocsp_cl.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ocsp) and [stack.h and stack.c](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/stack), the version in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware layouts remain unverified. A null response data pointer, and a null entry array with an index in range, throw where that release dereferences them. From a8d285fbcc7378730bf653508543a3ba10948bba Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sun, 25 Oct 2026 05:58:50 +0100 Subject: [PATCH 31/46] refactor(libSceLibreSsl): declare the SSL session exports in the public header --- core/libs/prx/libSceLibreSsl/include/Api.hpp | 33 +++++++++++++++--- .../prx/libSceLibreSsl/src/SslSession.cpp | 26 +------------- core/libs/tests/GuestLibreSslSslSession.cpp | 34 +++++++++---------- 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl/include/Api.hpp b/core/libs/prx/libSceLibreSsl/include/Api.hpp index b3491f0b15..bcd1c6bf0e 100644 --- a/core/libs/prx/libSceLibreSsl/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl/include/Api.hpp @@ -523,6 +523,31 @@ struct LibreSslX509Name { int canonEnclen; }; +struct LibreSslCertKey { + void* x509; + void* privatekey; + const void* digest; +}; + +struct LibreSslCert { + LibreSslCertKey* key; + unsigned char rest[248]; +}; + +struct LibreSslSession { + unsigned char beforeCipher[168]; + const void* cipher; + unsigned char rest[56]; +}; + +struct LibreSslSessionConnection { + unsigned char beforeCert[80]; + LibreSslCert* cert; + unsigned char beforeSession[40]; + LibreSslSession* session; + unsigned char rest[96]; +}; + extern "C" { std::int64_t APS5_VABI ASN1_INTEGER_get_nid_postfix(const Asn1String* value); @@ -775,8 +800,8 @@ void APS5_VABI SSL_get0_alpn_selected_nid_postfix(const LibreSslSsl* ssl, const int APS5_VABI SSL_get0_param_nid_postfix(void); int APS5_VABI SSL_get1_session_nid_postfix(void); void* APS5_VABI SSL_get_SSL_CTX_nid_postfix(const LibreSslSsl* ssl); -int APS5_VABI SSL_get_certificate_nid_postfix(void); -int APS5_VABI SSL_get_current_cipher_nid_postfix(void); +void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSessionConnection* ssl); +const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSessionConnection* ssl); int APS5_VABI SSL_get_error_nid_postfix(void); int APS5_VABI SSL_get_ex_data_nid_postfix(void); int APS5_VABI SSL_get_ex_data_X509_STORE_CTX_idx_nid_postfix(void); @@ -784,13 +809,13 @@ int APS5_VABI SSL_get_ex_new_index_nid_postfix(std::int64_t argl, void* argp, Li int APS5_VABI SSL_get_fd_nid_postfix(void); int APS5_VABI SSL_get_peer_cert_chain_nid_postfix(void); int APS5_VABI SSL_get_peer_certificate_nid_postfix(void); -int APS5_VABI SSL_get_privatekey_nid_postfix(void); void* APS5_VABI SSL_get_rbio_nid_postfix(const LibreSslSsl* ssl); +void* APS5_VABI SSL_get_privatekey_nid_postfix(const LibreSslSessionConnection* ssl); int APS5_VABI SSL_get_servername_nid_postfix(void); -int APS5_VABI SSL_get_session_nid_postfix(void); int APS5_VABI SSL_get_shutdown_nid_postfix(const LibreSslSsl* ssl); std::int64_t APS5_VABI SSL_get_verify_result_nid_postfix(const LibreSslSsl* ssl); void* APS5_VABI SSL_get_wbio_nid_postfix(const LibreSslSsl* ssl); +LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSessionConnection* ssl); int APS5_VABI SSL_library_init_nid_postfix(void); void APS5_VABI SSL_load_error_strings_nid_postfix(void); int APS5_VABI SSL_new_nid_postfix(void); diff --git a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp index e231deea71..7afe4ec6bf 100644 --- a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp +++ b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp @@ -1,33 +1,9 @@ +#include "prx/libSceLibreSsl/include/Api.hpp" #include #include #include #include "prx/libc/include/General.hpp" -struct LibreSslCertKey { - void* x509; - void* privatekey; - const void* digest; -}; - -struct LibreSslCert { - LibreSslCertKey* key; - unsigned char rest[248]; -}; - -struct LibreSslSession { - unsigned char beforeCipher[168]; - const void* cipher; - unsigned char rest[56]; -}; - -struct LibreSslSessionConnection { - unsigned char beforeCert[80]; - LibreSslCert* cert; - unsigned char beforeSession[40]; - LibreSslSession* session; - unsigned char rest[96]; -}; - static_assert(sizeof(LibreSslCertKey) == 24); static_assert(offsetof(LibreSslCertKey, privatekey) == 8); static_assert(sizeof(LibreSslCert) == 256); diff --git a/core/libs/tests/GuestLibreSslSslSession.cpp b/core/libs/tests/GuestLibreSslSslSession.cpp index 874babcccd..020b234edb 100644 --- a/core/libs/tests/GuestLibreSslSslSession.cpp +++ b/core/libs/tests/GuestLibreSslSslSession.cpp @@ -1,3 +1,4 @@ +#include "prx/libSceLibreSsl/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -35,11 +36,8 @@ static_assert(sizeof(TestCertKey) == 24 && sizeof(TestCert) == 256); static_assert(sizeof(TestSession) == 232 && offsetof(TestSession, cipher) == 168); static_assert(sizeof(TestSsl) == 232 && offsetof(TestSsl, cert) == 80 && offsetof(TestSsl, session) == 128); -extern "C" { -TestSession* APS5_VABI SSL_get_session_nid_postfix(const TestSsl*); -const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const TestSsl*); -void* APS5_VABI SSL_get_certificate_nid_postfix(const TestSsl*); -void* APS5_VABI SSL_get_privatekey_nid_postfix(const TestSsl*); +static const LibreSslSessionConnection* Connection(const TestSsl* ssl) { + return reinterpret_cast(ssl); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -58,10 +56,10 @@ static void TestGetSession() { TestCert cert {}; TestSsl ssl {}; RequireThrows([] { SSL_get_session_nid_postfix(nullptr); }); - Require(SSL_get_session_nid_postfix(&ssl) == nullptr); + Require(SSL_get_session_nid_postfix(Connection(&ssl)) == nullptr); ssl.cert = &cert; ssl.session = &session; - Require(SSL_get_session_nid_postfix(&ssl) == &session); + Require(reinterpret_cast(SSL_get_session_nid_postfix(Connection(&ssl))) == &session); } static void TestCurrentCipher() { @@ -69,11 +67,11 @@ static void TestCurrentCipher() { TestSession session {}; TestSsl ssl {}; RequireThrows([] { SSL_get_current_cipher_nid_postfix(nullptr); }); - Require(SSL_get_current_cipher_nid_postfix(&ssl) == nullptr); + Require(SSL_get_current_cipher_nid_postfix(Connection(&ssl)) == nullptr); ssl.session = &session; - Require(SSL_get_current_cipher_nid_postfix(&ssl) == nullptr); + Require(SSL_get_current_cipher_nid_postfix(Connection(&ssl)) == nullptr); session.cipher = &cipher; - Require(SSL_get_current_cipher_nid_postfix(&ssl) == &cipher); + Require(SSL_get_current_cipher_nid_postfix(Connection(&ssl)) == &cipher); } static void TestGetCertificate() { @@ -83,16 +81,16 @@ static void TestGetCertificate() { TestCert cert {}; TestSsl ssl {}; RequireThrows([] { SSL_get_certificate_nid_postfix(nullptr); }); - Require(SSL_get_certificate_nid_postfix(&ssl) == nullptr); + Require(SSL_get_certificate_nid_postfix(Connection(&ssl)) == nullptr); ssl.cert = &cert; - RequireThrows([&] { SSL_get_certificate_nid_postfix(&ssl); }); + RequireThrows([&] { SSL_get_certificate_nid_postfix(Connection(&ssl)); }); cert.pkeys[0].x509 = &other; cert.pkeys[2].x509 = &x509; cert.pkeys[2].privatekey = &key; cert.key = &cert.pkeys[1]; - Require(SSL_get_certificate_nid_postfix(&ssl) == nullptr); + Require(SSL_get_certificate_nid_postfix(Connection(&ssl)) == nullptr); cert.key = &cert.pkeys[2]; - Require(SSL_get_certificate_nid_postfix(&ssl) == &x509); + Require(SSL_get_certificate_nid_postfix(Connection(&ssl)) == &x509); } static void TestGetPrivateKey() { @@ -102,16 +100,16 @@ static void TestGetPrivateKey() { TestCert cert {}; TestSsl ssl {}; RequireThrows([] { SSL_get_privatekey_nid_postfix(nullptr); }); - Require(SSL_get_privatekey_nid_postfix(&ssl) == nullptr); + Require(SSL_get_privatekey_nid_postfix(Connection(&ssl)) == nullptr); ssl.cert = &cert; - RequireThrows([&] { SSL_get_privatekey_nid_postfix(&ssl); }); + RequireThrows([&] { SSL_get_privatekey_nid_postfix(Connection(&ssl)); }); cert.pkeys[0].privatekey = &other; cert.pkeys[3].x509 = &x509; cert.pkeys[3].privatekey = &key; cert.key = &cert.pkeys[1]; - Require(SSL_get_privatekey_nid_postfix(&ssl) == nullptr); + Require(SSL_get_privatekey_nid_postfix(Connection(&ssl)) == nullptr); cert.key = &cert.pkeys[3]; - Require(SSL_get_privatekey_nid_postfix(&ssl) == &key); + Require(SSL_get_privatekey_nid_postfix(Connection(&ssl)) == &key); } int main() { From 503b810afd2335ab6b849d7bb778fff099a10bd5 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Thu, 5 Nov 2026 19:15:39 +0100 Subject: [PATCH 32/46] docs(libSceLibreSsl): count 166 pending exports after the rebase --- docs/dev/debt/libSceLibreSsl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceLibreSsl.md b/docs/dev/debt/libSceLibreSsl.md index 2e5707d3f8..0b53b22ba4 100644 --- a/docs/dev/debt/libSceLibreSsl.md +++ b/docs/dev/debt/libSceLibreSsl.md @@ -10,7 +10,7 @@ - [SSL_get_SSL_CTX, SSL_get_rbio, SSL_get_wbio, SSL_version, SSL_get_verify_result, SSL_get_shutdown, SSL_want](../../../core/libs/prx/libSceLibreSsl/src/Ssl.cpp) (libSceLibreSsl) - nothing is checked on the console. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h` and `ssl_lib.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a connection yet, so only connections the caller builds reach them. A null connection, and for the fields kept in `ssl_internal_st` a null internal state, which LibreSSL dereferences, throw - [SSL_set_verify, SSL_get0_alpn_selected, SSL_set_connect_state, SSL_ctrl](../../../core/libs/prx/libSceLibreSsl/src/Ssl.cpp) (libSceLibreSsl) - nothing is checked on the console. `SSL_ctrl` throws for the commands LibreSSL passes to `ssl3_ctrl` or `dtls1_ctrl`, including `SSL_set_tlsext_host_name`. `SSL_set_connect_state` throws when a cipher or hash context is set, which LibreSSL frees; they are null before a handshake. Layouts and results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/tree/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl) `ssl.h`, `ssl_locl.h` and `ssl_lib.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export creates a connection or `SSL_METHOD` yet, so only structures the caller builds reach them. Null pointers that LibreSSL dereferences throw -- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 168 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 164 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c9cd71c1f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. - [MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 Init/Update/Final and SHA256](../../../core/libs/prx/libSceLibreSsl/src/Digest.cpp) (libSceLibreSsl) - signatures and context layouts follow the [LibreSSL headers](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/sha/sha.h), and buffering and `Final` follow its [sources](https://github.com/libressl/openbsd/tree/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/sha); nothing is checked on the console. Null contexts, null input with a nonzero length, null digest buffers in the 32-bit `Final` functions and a buffered byte count that is not below the block size, which LibreSSL does not check, throw. SHA-384 and SHA-512 initialization retains the block buffer, following [LibreSSL 2.5.4 `sha512.c`](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/sha/sha512.c) - [DES_set_key, DES_ecb_encrypt, DES_set_odd_parity](../../../core/libs/prx/libSceLibreSsl/Export.cpp) (libSceLibreSsl) - the 128-byte `DES_key_schedule` holds the 8-byte key followed by zeros instead of LibreSSL's precomputed subkeys, so a title reading its contents sees different bytes; `DES_ecb_encrypt` derives the subkeys through mbedtls on each call. Not compared with the console. Results follow LibreSSL [des_key.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/des/des_key.c) and [des.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/des/des.c): `DES_set_key` skips the parity and weak-key checks and returns 0, as it does while `DES_check_key` is 0 (that variable is not in the PS5 export inventory), a nonzero `enc` encrypts, and `DES_set_odd_parity` sets each byte's low bit for odd parity. Null pointers throw where LibreSSL faults - [X509_verify_cert_error_string](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - no console call is verified. The 64-bit argument, low-32-bit lookup, messages and shared numeric fallback follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_txt.c), the version identified in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware versions remain unverified. Unknown-code results share an overwritten static buffer and are not safe for concurrent use, as in that release; code 1 and codes above 54 use the numeric fallback. From 6aa47502d3d5437ec89b566139b4d5f3254ba574 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Thu, 5 Nov 2026 19:16:36 +0100 Subject: [PATCH 33/46] refactor(libSceLibreSsl): read the SSL session fields through LibreSslSsl --- core/libs/prx/libSceLibreSsl/include/Api.hpp | 16 +++--------- .../prx/libSceLibreSsl/src/SslSession.cpp | 25 ++++++++++--------- core/libs/tests/GuestLibreSslSslSession.cpp | 4 +-- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl/include/Api.hpp b/core/libs/prx/libSceLibreSsl/include/Api.hpp index bcd1c6bf0e..62434a2278 100644 --- a/core/libs/prx/libSceLibreSsl/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl/include/Api.hpp @@ -540,14 +540,6 @@ struct LibreSslSession { unsigned char rest[56]; }; -struct LibreSslSessionConnection { - unsigned char beforeCert[80]; - LibreSslCert* cert; - unsigned char beforeSession[40]; - LibreSslSession* session; - unsigned char rest[96]; -}; - extern "C" { std::int64_t APS5_VABI ASN1_INTEGER_get_nid_postfix(const Asn1String* value); @@ -800,8 +792,8 @@ void APS5_VABI SSL_get0_alpn_selected_nid_postfix(const LibreSslSsl* ssl, const int APS5_VABI SSL_get0_param_nid_postfix(void); int APS5_VABI SSL_get1_session_nid_postfix(void); void* APS5_VABI SSL_get_SSL_CTX_nid_postfix(const LibreSslSsl* ssl); -void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSessionConnection* ssl); -const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSessionConnection* ssl); +void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSsl* ssl); +const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSsl* ssl); int APS5_VABI SSL_get_error_nid_postfix(void); int APS5_VABI SSL_get_ex_data_nid_postfix(void); int APS5_VABI SSL_get_ex_data_X509_STORE_CTX_idx_nid_postfix(void); @@ -810,12 +802,12 @@ int APS5_VABI SSL_get_fd_nid_postfix(void); int APS5_VABI SSL_get_peer_cert_chain_nid_postfix(void); int APS5_VABI SSL_get_peer_certificate_nid_postfix(void); void* APS5_VABI SSL_get_rbio_nid_postfix(const LibreSslSsl* ssl); -void* APS5_VABI SSL_get_privatekey_nid_postfix(const LibreSslSessionConnection* ssl); +void* APS5_VABI SSL_get_privatekey_nid_postfix(const LibreSslSsl* ssl); int APS5_VABI SSL_get_servername_nid_postfix(void); int APS5_VABI SSL_get_shutdown_nid_postfix(const LibreSslSsl* ssl); std::int64_t APS5_VABI SSL_get_verify_result_nid_postfix(const LibreSslSsl* ssl); void* APS5_VABI SSL_get_wbio_nid_postfix(const LibreSslSsl* ssl); -LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSessionConnection* ssl); +LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSsl* ssl); int APS5_VABI SSL_library_init_nid_postfix(void); void APS5_VABI SSL_load_error_strings_nid_postfix(void); int APS5_VABI SSL_new_nid_postfix(void); diff --git a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp index 7afe4ec6bf..800b903fd4 100644 --- a/core/libs/prx/libSceLibreSsl/src/SslSession.cpp +++ b/core/libs/prx/libSceLibreSsl/src/SslSession.cpp @@ -9,19 +9,20 @@ static_assert(offsetof(LibreSslCertKey, privatekey) == 8); static_assert(sizeof(LibreSslCert) == 256); static_assert(sizeof(LibreSslSession) == 232); static_assert(offsetof(LibreSslSession, cipher) == 168); -static_assert(sizeof(LibreSslSessionConnection) == 232); -static_assert(offsetof(LibreSslSessionConnection, cert) == 80); -static_assert(offsetof(LibreSslSessionConnection, session) == 128); namespace { -const LibreSslSessionConnection& Connection(const LibreSslSessionConnection* ssl, const char* function) { +const LibreSslSsl& Connection(const LibreSslSsl* ssl, const char* function) { if (!ssl) throw std::invalid_argument(std::string(function) + ": null connection"); return *ssl; } -const LibreSslCertKey* CurrentKey(const LibreSslSessionConnection* ssl, const char* function) { - const LibreSslCert* cert = Connection(ssl, function).cert; +LibreSslSession* Session(const LibreSslSsl* ssl, const char* function) { + return static_cast(Connection(ssl, function).session); +} + +const LibreSslCertKey* CurrentKey(const LibreSslSsl* ssl, const char* function) { + const LibreSslCert* cert = static_cast(Connection(ssl, function).cert); if (!cert) return nullptr; if (!cert->key) throw std::invalid_argument(std::string(function) + ": null current key"); return cert->key; @@ -31,21 +32,21 @@ const LibreSslCertKey* CurrentKey(const LibreSslSessionConnection* ssl, const ch extern "C" { -LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSessionConnection* ssl) { - return Connection(ssl, __func__).session; +LibreSslSession* APS5_VABI SSL_get_session_nid_postfix(const LibreSslSsl* ssl) { + return Session(ssl, __func__); } -const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSessionConnection* ssl) { - const LibreSslSession* session = Connection(ssl, __func__).session; +const void* APS5_VABI SSL_get_current_cipher_nid_postfix(const LibreSslSsl* ssl) { + const LibreSslSession* session = Session(ssl, __func__); return session ? session->cipher : nullptr; } -void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSessionConnection* ssl) { +void* APS5_VABI SSL_get_certificate_nid_postfix(const LibreSslSsl* ssl) { const LibreSslCertKey* key = CurrentKey(ssl, __func__); return key ? key->x509 : nullptr; } -void* APS5_VABI SSL_get_privatekey_nid_postfix(const LibreSslSessionConnection* ssl) { +void* APS5_VABI SSL_get_privatekey_nid_postfix(const LibreSslSsl* ssl) { const LibreSslCertKey* key = CurrentKey(ssl, __func__); return key ? key->privatekey : nullptr; } diff --git a/core/libs/tests/GuestLibreSslSslSession.cpp b/core/libs/tests/GuestLibreSslSslSession.cpp index 020b234edb..55a3e23e08 100644 --- a/core/libs/tests/GuestLibreSslSslSession.cpp +++ b/core/libs/tests/GuestLibreSslSslSession.cpp @@ -36,8 +36,8 @@ static_assert(sizeof(TestCertKey) == 24 && sizeof(TestCert) == 256); static_assert(sizeof(TestSession) == 232 && offsetof(TestSession, cipher) == 168); static_assert(sizeof(TestSsl) == 232 && offsetof(TestSsl, cert) == 80 && offsetof(TestSsl, session) == 128); -static const LibreSslSessionConnection* Connection(const TestSsl* ssl) { - return reinterpret_cast(ssl); +static const LibreSslSsl* Connection(const TestSsl* ssl) { + return reinterpret_cast(ssl); } static void Require(bool condition, std::source_location location = std::source_location::current()) { From a367f6235fd2a2c84806c6b8a53b743af7bf9aec Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 03:05:31 +0200 Subject: [PATCH 34/46] feat(libSceLibreSsl3): implement ASN1_STRING_get0_data --- core/libs/prx/libSceLibreSsl3/CMakeLists.txt | 13 +++++- core/libs/prx/libSceLibreSsl3/Export.cpp | 5 --- core/libs/prx/libSceLibreSsl3/src/Asn1.cpp | 26 +++++++++++ core/libs/tests/GuestLibreSsl3Asn1.cpp | 47 ++++++++++++++++++++ docs/dev/debt/libSceLibreSsl3.md | 3 +- 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 core/libs/prx/libSceLibreSsl3/src/Asn1.cpp create mode 100644 core/libs/tests/GuestLibreSsl3Asn1.cpp diff --git a/core/libs/prx/libSceLibreSsl3/CMakeLists.txt b/core/libs/prx/libSceLibreSsl3/CMakeLists.txt index 496fb2a9b9..8ecce8813c 100644 --- a/core/libs/prx/libSceLibreSsl3/CMakeLists.txt +++ b/core/libs/prx/libSceLibreSsl3/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Key.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpDigest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509PubkeyParam.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ObjCmp.cpp) +add_library(libSceLibreSsl3 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Key.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpDigest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509PubkeyParam.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ObjCmp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Asn1.cpp) target_include_directories(libSceLibreSsl3 PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(libSceLibreSsl3 PRIVATE libSceLibreSsl libc) configure_windows_unwind(libSceLibreSsl3) @@ -130,6 +130,17 @@ if(BUILD_TESTING) set_tests_properties(guest_crypto_memcmp guest_libressl3_digest guest_libressl3_asn1 guest_libressl3_key_reference guest_libressl3_evp guest_libressl3_version guest_libressl3_x509_pubkey_param PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + + add_test_executable(guest_libressl3_asn1_string_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSsl3Asn1.cpp) + target_include_directories(guest_libressl3_asn1_string_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_libressl3_asn1_string_tests PRIVATE libSceLibreSsl3 libc) + configure_windows_unwind(guest_libressl3_asn1_string_tests) + add_test(NAME guest_libressl3_asn1_string COMMAND guest_libressl3_asn1_string_tests) + set_tests_properties(guest_libressl3_asn1_string PROPERTIES TIMEOUT 30) + if(WIN32) + set_tests_properties(guest_libressl3_asn1_string PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() endif() if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND EXISTS "${LIBS_INCLUDE_DIR}/prx/libSceLibreSsl/src/Evp.cpp") diff --git a/core/libs/prx/libSceLibreSsl3/Export.cpp b/core/libs/prx/libSceLibreSsl3/Export.cpp index 6ba83e997a..caae5e95a9 100644 --- a/core/libs/prx/libSceLibreSsl3/Export.cpp +++ b/core/libs/prx/libSceLibreSsl3/Export.cpp @@ -18,11 +18,6 @@ int APS5_VABI ASN1_OBJECT_free_nid_postfix(void) { return 0; } -int APS5_VABI ASN1_STRING_get0_data_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI BIO_up_ref_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp new file mode 100644 index 0000000000..23d2c6dcc5 --- /dev/null +++ b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include "prx/libc/include/General.hpp" + +struct LibreSslAsn1String { + int length; + int type; + unsigned char* data; + std::int64_t flags; +}; + +static_assert(sizeof(LibreSslAsn1String) == 24); +static_assert(offsetof(LibreSslAsn1String, type) == 4); +static_assert(offsetof(LibreSslAsn1String, data) == 8); +static_assert(offsetof(LibreSslAsn1String, flags) == 16); + +extern "C" { + +const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const LibreSslAsn1String* string) { + if (!string) throw std::invalid_argument(std::string(__func__) + ": null string"); + return string->data; +} + +} diff --git a/core/libs/tests/GuestLibreSsl3Asn1.cpp b/core/libs/tests/GuestLibreSsl3Asn1.cpp new file mode 100644 index 0000000000..8e1283f3dd --- /dev/null +++ b/core/libs/tests/GuestLibreSsl3Asn1.cpp @@ -0,0 +1,47 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include +#include + +struct TestAsn1String { + int length; + int type; + unsigned char* data; + std::int64_t flags; +}; + +static_assert(sizeof(TestAsn1String) == 24 && offsetof(TestAsn1String, data) == 8 && offsetof(TestAsn1String, flags) == 16); + +extern "C" { +const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const TestAsn1String*); +} + +static void Require(bool condition, std::source_location location = std::source_location::current()) { + if (condition) return; + std::fprintf(stderr, "LibreSSL 3 ASN.1 check failed at line %u\n", location.line()); + std::abort(); +} + +template static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) { + try { action(); } catch (const std::exception&) { return; } + Require(false, location); +} + +static void TestGet0Data() { + unsigned char bytes[] {1, 2, 3}; + TestAsn1String string {3, 2, bytes, 0}; + Require(ASN1_STRING_get0_data_nid_postfix(&string) == bytes); + string = {0, 4, nullptr, 0}; + Require(ASN1_STRING_get0_data_nid_postfix(&string) == nullptr); + string = {-1, 0x102, bytes, 0x010}; + Require(ASN1_STRING_get0_data_nid_postfix(&string) == bytes); + RequireThrows([] { ASN1_STRING_get0_data_nid_postfix(nullptr); }); +} + +int main() { + TestGet0Data(); + std::puts("libSceLibreSsl3 ASN.1 tests passed"); +} diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index bafab7de86..a27eb85e58 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -10,7 +10,7 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 67 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `EVP_MD_CTX_new`, `EVP_MD_CTX_free`, `EVP_DigestInit`, `EVP_sha1`, `EVP_sha224`, `ERR_load_crypto_strings`, `ERR_peek_last_error`, `OpenSSL_version`, `X509_PUBKEY_get0_param` and `OBJ_cmp`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 67 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `EVP_MD_CTX_new`, `EVP_MD_CTX_free`, `EVP_DigestInit`, `EVP_sha1`, `EVP_sha224`, `ERR_load_crypto_strings`, `ERR_peek_last_error`, `X509_PUBKEY_get0_param` and `OBJ_cmp`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. 73 `OpenSSL_version` `ASN1_STRING_get0_data`, ### Functional @@ -28,6 +28,7 @@ - [EVP_CIPHER_key_length and EVP_CIPHER_iv_length](../../../core/libs/prx/libSceLibreSsl3/src/Cipher.cpp) (libSceLibreSsl3) - nothing is checked on the console. They return `key_len` and `iv_len` from the 80-byte `EVP_CIPHER` of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/evp) `evp_local.h` and `evp_cipher.c`. The fields are at offsets 8 and 12, as in the [2.5.4 layout](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/evp.h) of the ciphers the forwarded libSceLibreSsl exports handle. A null cipher, which LibreSSL dereferences, throws. Closes when: compared with the console. - [X509_VERIFY_PARAM_set_flags](../../../core/libs/prx/libSceLibreSsl3/src/X509VerifyParam.cpp) (libSceLibreSsl3) - nothing is checked on the console. It ORs the flags into `flags` and returns 1, as both [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_vpm.c) and [4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/x509/x509_vpm.c) `x509_vpm.c` do. They differ when `EXPLICIT_POLICY`, `INHIBIT_ANY` or `INHIBIT_MAP` is set while `POLICY_CHECK` is neither set nor requested: 2.5.4 also adds `POLICY_CHECK` and 4.0.0 does not. The console's choice is unknown, so that case throws through `NotImplemented_nid_no_patch` and leaves the parameters unchanged. It writes libSceLibreSsl's 2.5.4 `LibreSslX509VerifyParam`, whose `flags` is at offset 24 as in 4.0.0. Null parameters throw. Closes when: the console's policy behaviour is measured. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` +- [ASN1_STRING_get0_data](../../../core/libs/prx/libSceLibreSsl3/src/Asn1.cpp) (libSceLibreSsl3) - nothing is checked on the console. The 24-byte `ASN1_STRING` layout and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1) `asn1.h` and `a_string.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a string yet, so only strings the caller builds reach them. A null string, which `ASN1_STRING_get0_data` dereferences, throws - [ASN1_TIME_diff](../../../core/libs/prx/libSceLibreSsl3/src/Time.cpp) has no console ABI or behavior verification. Its signature and date rules follow LibreSSL 4.0.0 [a_time.c](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1/a_time.c), [a_time_tm.c](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1/a_time_tm.c) and [a_time_posix.c](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1/a_time_posix.c), using the existing 24-byte string layout. Null inputs use the host wall clock; the console clock relationship is unverified. Null output pointers throw after valid input parsing, where the reference dereferences them. Closes when: measured on a PS5. From f3f057f0f7b57c96a6cbb0a2e78371db2331cee7 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sat, 24 Oct 2026 03:05:34 +0200 Subject: [PATCH 35/46] feat(libSceLibreSsl3): implement ASN1_INTEGER_free --- core/libs/prx/libSceLibreSsl3/Export.cpp | 5 --- core/libs/prx/libSceLibreSsl3/src/Asn1.cpp | 18 ++++++++ core/libs/tests/GuestLibreSsl3Asn1.cpp | 52 ++++++++++++++++++++++ docs/dev/debt/libSceLibreSsl3.md | 4 +- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl3/Export.cpp b/core/libs/prx/libSceLibreSsl3/Export.cpp index caae5e95a9..4c5a04a69b 100644 --- a/core/libs/prx/libSceLibreSsl3/Export.cpp +++ b/core/libs/prx/libSceLibreSsl3/Export.cpp @@ -8,11 +8,6 @@ extern "C" { APS5_DUMMY_FUN -int APS5_VABI ASN1_INTEGER_free_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI ASN1_OBJECT_free_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp index 23d2c6dcc5..7a128a9135 100644 --- a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include "prx/libc/include/General.hpp" @@ -16,11 +17,28 @@ static_assert(offsetof(LibreSslAsn1String, type) == 4); static_assert(offsetof(LibreSslAsn1String, data) == 8); static_assert(offsetof(LibreSslAsn1String, flags) == 16); +namespace { + +constexpr std::int64_t Asn1StringFlagNdef = 0x010; + +} + extern "C" { +void APS5_VABI free_nid_postfix(void* pointer); const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const LibreSslAsn1String* string) { if (!string) throw std::invalid_argument(std::string(__func__) + ": null string"); return string->data; } +void APS5_VABI ASN1_INTEGER_free_nid_postfix(LibreSslAsn1String* integer) { + if (!integer) return; + if (!(integer->flags & Asn1StringFlagNdef) && integer->data) { + if (integer->length < 0) throw std::invalid_argument(std::string(__func__) + ": negative length"); + std::memset(integer->data, 0, static_cast(integer->length)); + free_nid_postfix(integer->data); + } + free_nid_postfix(integer); +} + } diff --git a/core/libs/tests/GuestLibreSsl3Asn1.cpp b/core/libs/tests/GuestLibreSsl3Asn1.cpp index 8e1283f3dd..2e4d1eb921 100644 --- a/core/libs/tests/GuestLibreSsl3Asn1.cpp +++ b/core/libs/tests/GuestLibreSsl3Asn1.cpp @@ -1,8 +1,10 @@ +#include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include +#include #include #include @@ -17,6 +19,9 @@ static_assert(sizeof(TestAsn1String) == 24 && offsetof(TestAsn1String, data) == extern "C" { const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const TestAsn1String*); +void APS5_VABI ASN1_INTEGER_free_nid_postfix(TestAsn1String*); +void* APS5_VABI malloc_nid_postfix(std::size_t); +void APS5_VABI free_nid_postfix(void*); } static void Require(bool condition, std::source_location location = std::source_location::current()) { @@ -41,7 +46,54 @@ static void TestGet0Data() { RequireThrows([] { ASN1_STRING_get0_data_nid_postfix(nullptr); }); } +static void InitializeHeap() { + void* const api[10] {}; + ApplicationHeapRegister_nid_no_patch(api); +} + +static TestAsn1String* NewInteger(int length, int type, std::int64_t flags) { + auto* integer = static_cast(malloc_nid_postfix(sizeof(TestAsn1String))); + Require(integer != nullptr); + integer->length = length; + integer->type = type; + integer->flags = flags; + integer->data = nullptr; + if (length > 0) { + integer->data = static_cast(malloc_nid_postfix(static_cast(length))); + Require(integer->data != nullptr); + std::memset(integer->data, 0x5A, static_cast(length)); + } + return integer; +} + +static void TestIntegerFree() { + ASN1_INTEGER_free_nid_postfix(nullptr); + ASN1_INTEGER_free_nid_postfix(NewInteger(4, 2, 0)); + ASN1_INTEGER_free_nid_postfix(NewInteger(16, 0x102, 0)); + ASN1_INTEGER_free_nid_postfix(NewInteger(0, 2, 0)); + ASN1_INTEGER_free_nid_postfix(NewInteger(4, 6, 0)); + + TestAsn1String* borrowed = NewInteger(4, 2, 0x010); + unsigned char* const data = borrowed->data; + ASN1_INTEGER_free_nid_postfix(borrowed); + Require(data[0] == 0x5A && data[3] == 0x5A); + free_nid_postfix(data); + + TestAsn1String* negative = NewInteger(4, 2, 0); + negative->length = -1; + RequireThrows([&] { ASN1_INTEGER_free_nid_postfix(negative); }); + Require(negative->data != nullptr && negative->data[0] == 0x5A); + negative->length = 4; + ASN1_INTEGER_free_nid_postfix(negative); + + TestAsn1String* empty = NewInteger(0, 2, 0); + empty->length = -1; + ASN1_INTEGER_free_nid_postfix(empty); +} + int main() { + InitializeHeap(); TestGet0Data(); + TestIntegerFree(); std::puts("libSceLibreSsl3 ASN.1 tests passed"); } diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index a27eb85e58..dbc2020eae 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -10,7 +10,7 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 67 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `EVP_MD_CTX_new`, `EVP_MD_CTX_free`, `EVP_DigestInit`, `EVP_sha1`, `EVP_sha224`, `ERR_load_crypto_strings`, `ERR_peek_last_error`, `X509_PUBKEY_get0_param` and `OBJ_cmp`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. 73 `OpenSSL_version` `ASN1_STRING_get0_data`, +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 67 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `EVP_MD_CTX_new`, `EVP_MD_CTX_free`, `EVP_DigestInit`, `EVP_sha1`, `EVP_sha224`, `ERR_load_crypto_strings`, `ERR_peek_last_error`, `X509_PUBKEY_get0_param` and `OBJ_cmp`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. 72 `OpenSSL_version`, `ASN1_STRING_get0_data` `ASN1_INTEGER_free`, ### Functional @@ -28,7 +28,7 @@ - [EVP_CIPHER_key_length and EVP_CIPHER_iv_length](../../../core/libs/prx/libSceLibreSsl3/src/Cipher.cpp) (libSceLibreSsl3) - nothing is checked on the console. They return `key_len` and `iv_len` from the 80-byte `EVP_CIPHER` of [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/evp) `evp_local.h` and `evp_cipher.c`. The fields are at offsets 8 and 12, as in the [2.5.4 layout](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/evp.h) of the ciphers the forwarded libSceLibreSsl exports handle. A null cipher, which LibreSSL dereferences, throws. Closes when: compared with the console. - [X509_VERIFY_PARAM_set_flags](../../../core/libs/prx/libSceLibreSsl3/src/X509VerifyParam.cpp) (libSceLibreSsl3) - nothing is checked on the console. It ORs the flags into `flags` and returns 1, as both [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_vpm.c) and [4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/x509/x509_vpm.c) `x509_vpm.c` do. They differ when `EXPLICIT_POLICY`, `INHIBIT_ANY` or `INHIBIT_MAP` is set while `POLICY_CHECK` is neither set nor requested: 2.5.4 also adds `POLICY_CHECK` and 4.0.0 does not. The console's choice is unknown, so that case throws through `NotImplemented_nid_no_patch` and leaves the parameters unchanged. It writes libSceLibreSsl's 2.5.4 `LibreSslX509VerifyParam`, whose `flags` is at offset 24 as in 4.0.0. Null parameters throw. Closes when: the console's policy behaviour is measured. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` -- [ASN1_STRING_get0_data](../../../core/libs/prx/libSceLibreSsl3/src/Asn1.cpp) (libSceLibreSsl3) - nothing is checked on the console. The 24-byte `ASN1_STRING` layout and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1) `asn1.h` and `a_string.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a string yet, so only strings the caller builds reach them. A null string, which `ASN1_STRING_get0_data` dereferences, throws +- [ASN1_STRING_get0_data, ASN1_INTEGER_free](../../../core/libs/prx/libSceLibreSsl3/src/Asn1.cpp) (libSceLibreSsl3) - nothing is checked on the console. The 24-byte `ASN1_STRING` layout and the results follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1) `asn1.h`, `a_string.c`, `a_int.c` and `tasn_fre.c`, the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). No export of this module creates a string yet, so only strings the caller builds reach them. `ASN1_INTEGER_free` zeroes and frees the data unless `ASN1_STRING_FLAG_NDEF` is set, then frees the string, through the guest `free` as the module would through libc. A null string in `ASN1_STRING_get0_data`, which LibreSSL dereferences, and a negative length with data in `ASN1_INTEGER_free`, which `freezero` would take as a huge size, throw - [ASN1_TIME_diff](../../../core/libs/prx/libSceLibreSsl3/src/Time.cpp) has no console ABI or behavior verification. Its signature and date rules follow LibreSSL 4.0.0 [a_time.c](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1/a_time.c), [a_time_tm.c](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1/a_time_tm.c) and [a_time_posix.c](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1/a_time_posix.c), using the existing 24-byte string layout. Null inputs use the host wall clock; the console clock relationship is unverified. Null output pointers throw after valid input parsing, where the reference dereferences them. Closes when: measured on a PS5. From 206fd2c9e230f176e861b7973dbcf22361a78172 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Sun, 25 Oct 2026 05:58:38 +0100 Subject: [PATCH 36/46] refactor(libSceLibreSsl3): declare the ASN.1 string exports in the public header --- core/libs/prx/libSceLibreSsl3/include/Api.hpp | 11 +++++-- core/libs/prx/libSceLibreSsl3/src/Asn1.cpp | 11 ++----- core/libs/tests/GuestLibreSsl3Asn1.cpp | 30 ++++++------------- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl3/include/Api.hpp b/core/libs/prx/libSceLibreSsl3/include/Api.hpp index d55f888615..9ac23feb18 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Api.hpp @@ -30,11 +30,18 @@ static_assert(sizeof(LibreSsl3X509Algor) == 16); static_assert(sizeof(LibreSsl3X509Pubkey) == 24); static_assert(offsetof(LibreSsl3X509Pubkey, publicKey) == 8); +struct LibreSslAsn1String { + int length; + int type; + unsigned char* data; + std::int64_t flags; +}; + extern "C" { -int APS5_VABI ASN1_INTEGER_free_nid_postfix(void); +void APS5_VABI ASN1_INTEGER_free_nid_postfix(LibreSslAsn1String* integer); int APS5_VABI ASN1_OBJECT_free_nid_postfix(void); -int APS5_VABI ASN1_STRING_get0_data_nid_postfix(void); +const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const LibreSslAsn1String* string); int APS5_VABI BIO_up_ref_nid_postfix(void); int APS5_VABI BIO_vfree_nid_postfix(void); int APS5_VABI CRYPTO_memcmp_nid_postfix(const void* first, const void* second, std::size_t length); diff --git a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp index 7a128a9135..fcf640c275 100644 --- a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp @@ -1,3 +1,5 @@ +#include "prx/libSceLibreSsl3/include/Api.hpp" +#include "prx/libc/include/Api.hpp" #include #include #include @@ -5,13 +7,6 @@ #include #include "prx/libc/include/General.hpp" -struct LibreSslAsn1String { - int length; - int type; - unsigned char* data; - std::int64_t flags; -}; - static_assert(sizeof(LibreSslAsn1String) == 24); static_assert(offsetof(LibreSslAsn1String, type) == 4); static_assert(offsetof(LibreSslAsn1String, data) == 8); @@ -24,8 +19,6 @@ constexpr std::int64_t Asn1StringFlagNdef = 0x010; } extern "C" { -void APS5_VABI free_nid_postfix(void* pointer); - const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const LibreSslAsn1String* string) { if (!string) throw std::invalid_argument(std::string(__func__) + ": null string"); return string->data; diff --git a/core/libs/tests/GuestLibreSsl3Asn1.cpp b/core/libs/tests/GuestLibreSsl3Asn1.cpp index 2e4d1eb921..84879be9fa 100644 --- a/core/libs/tests/GuestLibreSsl3Asn1.cpp +++ b/core/libs/tests/GuestLibreSsl3Asn1.cpp @@ -1,3 +1,5 @@ +#include "prx/libSceLibreSsl3/include/Api.hpp" +#include "prx/libc/include/Api.hpp" #include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include @@ -8,21 +10,7 @@ #include #include -struct TestAsn1String { - int length; - int type; - unsigned char* data; - std::int64_t flags; -}; - -static_assert(sizeof(TestAsn1String) == 24 && offsetof(TestAsn1String, data) == 8 && offsetof(TestAsn1String, flags) == 16); - -extern "C" { -const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const TestAsn1String*); -void APS5_VABI ASN1_INTEGER_free_nid_postfix(TestAsn1String*); -void* APS5_VABI malloc_nid_postfix(std::size_t); -void APS5_VABI free_nid_postfix(void*); -} +static_assert(sizeof(LibreSslAsn1String) == 24 && offsetof(LibreSslAsn1String, data) == 8 && offsetof(LibreSslAsn1String, flags) == 16); static void Require(bool condition, std::source_location location = std::source_location::current()) { if (condition) return; @@ -37,7 +25,7 @@ template static void RequireThrows(TAction action, std::source static void TestGet0Data() { unsigned char bytes[] {1, 2, 3}; - TestAsn1String string {3, 2, bytes, 0}; + LibreSslAsn1String string {3, 2, bytes, 0}; Require(ASN1_STRING_get0_data_nid_postfix(&string) == bytes); string = {0, 4, nullptr, 0}; Require(ASN1_STRING_get0_data_nid_postfix(&string) == nullptr); @@ -51,8 +39,8 @@ static void InitializeHeap() { ApplicationHeapRegister_nid_no_patch(api); } -static TestAsn1String* NewInteger(int length, int type, std::int64_t flags) { - auto* integer = static_cast(malloc_nid_postfix(sizeof(TestAsn1String))); +static LibreSslAsn1String* NewInteger(int length, int type, std::int64_t flags) { + auto* integer = static_cast(malloc_nid_postfix(sizeof(LibreSslAsn1String))); Require(integer != nullptr); integer->length = length; integer->type = type; @@ -73,20 +61,20 @@ static void TestIntegerFree() { ASN1_INTEGER_free_nid_postfix(NewInteger(0, 2, 0)); ASN1_INTEGER_free_nid_postfix(NewInteger(4, 6, 0)); - TestAsn1String* borrowed = NewInteger(4, 2, 0x010); + LibreSslAsn1String* borrowed = NewInteger(4, 2, 0x010); unsigned char* const data = borrowed->data; ASN1_INTEGER_free_nid_postfix(borrowed); Require(data[0] == 0x5A && data[3] == 0x5A); free_nid_postfix(data); - TestAsn1String* negative = NewInteger(4, 2, 0); + LibreSslAsn1String* negative = NewInteger(4, 2, 0); negative->length = -1; RequireThrows([&] { ASN1_INTEGER_free_nid_postfix(negative); }); Require(negative->data != nullptr && negative->data[0] == 0x5A); negative->length = 4; ASN1_INTEGER_free_nid_postfix(negative); - TestAsn1String* empty = NewInteger(0, 2, 0); + LibreSslAsn1String* empty = NewInteger(0, 2, 0); empty->length = -1; ASN1_INTEGER_free_nid_postfix(empty); } From 980a76976b42979046da2669ca1338ffe2c04345 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Mon, 26 Oct 2026 23:12:45 +0100 Subject: [PATCH 37/46] refactor(libSceLibreSsl3): use Asn1String for the ASN.1 string exports --- core/libs/prx/libSceLibreSsl3/include/Api.hpp | 11 ++--------- core/libs/prx/libSceLibreSsl3/src/Asn1.cpp | 9 ++------- core/libs/tests/GuestLibreSsl3Asn1.cpp | 14 +++++++------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceLibreSsl3/include/Api.hpp b/core/libs/prx/libSceLibreSsl3/include/Api.hpp index 9ac23feb18..6f51accb4c 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Api.hpp @@ -30,18 +30,11 @@ static_assert(sizeof(LibreSsl3X509Algor) == 16); static_assert(sizeof(LibreSsl3X509Pubkey) == 24); static_assert(offsetof(LibreSsl3X509Pubkey, publicKey) == 8); -struct LibreSslAsn1String { - int length; - int type; - unsigned char* data; - std::int64_t flags; -}; - extern "C" { -void APS5_VABI ASN1_INTEGER_free_nid_postfix(LibreSslAsn1String* integer); +void APS5_VABI ASN1_INTEGER_free_nid_postfix(Asn1String* integer); int APS5_VABI ASN1_OBJECT_free_nid_postfix(void); -const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const LibreSslAsn1String* string); +const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const Asn1String* string); int APS5_VABI BIO_up_ref_nid_postfix(void); int APS5_VABI BIO_vfree_nid_postfix(void); int APS5_VABI CRYPTO_memcmp_nid_postfix(const void* first, const void* second, std::size_t length); diff --git a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp index fcf640c275..8948cdf2f6 100644 --- a/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/Asn1.cpp @@ -7,11 +7,6 @@ #include #include "prx/libc/include/General.hpp" -static_assert(sizeof(LibreSslAsn1String) == 24); -static_assert(offsetof(LibreSslAsn1String, type) == 4); -static_assert(offsetof(LibreSslAsn1String, data) == 8); -static_assert(offsetof(LibreSslAsn1String, flags) == 16); - namespace { constexpr std::int64_t Asn1StringFlagNdef = 0x010; @@ -19,12 +14,12 @@ constexpr std::int64_t Asn1StringFlagNdef = 0x010; } extern "C" { -const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const LibreSslAsn1String* string) { +const unsigned char* APS5_VABI ASN1_STRING_get0_data_nid_postfix(const Asn1String* string) { if (!string) throw std::invalid_argument(std::string(__func__) + ": null string"); return string->data; } -void APS5_VABI ASN1_INTEGER_free_nid_postfix(LibreSslAsn1String* integer) { +void APS5_VABI ASN1_INTEGER_free_nid_postfix(Asn1String* integer) { if (!integer) return; if (!(integer->flags & Asn1StringFlagNdef) && integer->data) { if (integer->length < 0) throw std::invalid_argument(std::string(__func__) + ": negative length"); diff --git a/core/libs/tests/GuestLibreSsl3Asn1.cpp b/core/libs/tests/GuestLibreSsl3Asn1.cpp index 84879be9fa..303e7104d7 100644 --- a/core/libs/tests/GuestLibreSsl3Asn1.cpp +++ b/core/libs/tests/GuestLibreSsl3Asn1.cpp @@ -10,7 +10,7 @@ #include #include -static_assert(sizeof(LibreSslAsn1String) == 24 && offsetof(LibreSslAsn1String, data) == 8 && offsetof(LibreSslAsn1String, flags) == 16); +static_assert(sizeof(Asn1String) == 24 && offsetof(Asn1String, data) == 8 && offsetof(Asn1String, flags) == 16); static void Require(bool condition, std::source_location location = std::source_location::current()) { if (condition) return; @@ -25,7 +25,7 @@ template static void RequireThrows(TAction action, std::source static void TestGet0Data() { unsigned char bytes[] {1, 2, 3}; - LibreSslAsn1String string {3, 2, bytes, 0}; + Asn1String string {3, 2, bytes, 0}; Require(ASN1_STRING_get0_data_nid_postfix(&string) == bytes); string = {0, 4, nullptr, 0}; Require(ASN1_STRING_get0_data_nid_postfix(&string) == nullptr); @@ -39,8 +39,8 @@ static void InitializeHeap() { ApplicationHeapRegister_nid_no_patch(api); } -static LibreSslAsn1String* NewInteger(int length, int type, std::int64_t flags) { - auto* integer = static_cast(malloc_nid_postfix(sizeof(LibreSslAsn1String))); +static Asn1String* NewInteger(int length, int type, std::int64_t flags) { + auto* integer = static_cast(malloc_nid_postfix(sizeof(Asn1String))); Require(integer != nullptr); integer->length = length; integer->type = type; @@ -61,20 +61,20 @@ static void TestIntegerFree() { ASN1_INTEGER_free_nid_postfix(NewInteger(0, 2, 0)); ASN1_INTEGER_free_nid_postfix(NewInteger(4, 6, 0)); - LibreSslAsn1String* borrowed = NewInteger(4, 2, 0x010); + Asn1String* borrowed = NewInteger(4, 2, 0x010); unsigned char* const data = borrowed->data; ASN1_INTEGER_free_nid_postfix(borrowed); Require(data[0] == 0x5A && data[3] == 0x5A); free_nid_postfix(data); - LibreSslAsn1String* negative = NewInteger(4, 2, 0); + Asn1String* negative = NewInteger(4, 2, 0); negative->length = -1; RequireThrows([&] { ASN1_INTEGER_free_nid_postfix(negative); }); Require(negative->data != nullptr && negative->data[0] == 0x5A); negative->length = 4; ASN1_INTEGER_free_nid_postfix(negative); - LibreSslAsn1String* empty = NewInteger(0, 2, 0); + Asn1String* empty = NewInteger(0, 2, 0); empty->length = -1; ASN1_INTEGER_free_nid_postfix(empty); } From 57847c8140574e10d6f5f6e2bf117bcaf73ce7f1 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Thu, 5 Nov 2026 19:31:47 +0100 Subject: [PATCH 38/46] docs(libSceLibreSsl3): count 65 pending exports after the rebase --- docs/dev/debt/libSceLibreSsl3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index dbc2020eae..14d97492f7 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -10,7 +10,7 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 67 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `EVP_MD_CTX_new`, `EVP_MD_CTX_free`, `EVP_DigestInit`, `EVP_sha1`, `EVP_sha224`, `ERR_load_crypto_strings`, `ERR_peek_last_error`, `X509_PUBKEY_get0_param` and `OBJ_cmp`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. 72 `OpenSSL_version`, `ASN1_STRING_get0_data` `ASN1_INTEGER_free`, +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 65 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref`, `RSA_up_ref`, `EVP_PKEY_get0_RSA`, `EVP_PKEY_get0_DSA`, `EVP_PKEY_get0_DH`, `EVP_MD_CTX_new`, `EVP_MD_CTX_free`, `EVP_DigestInit`, `EVP_sha1`, `EVP_sha224`, `ERR_load_crypto_strings`, `ERR_peek_last_error`, `X509_PUBKEY_get0_param` and `OBJ_cmp`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. 72 `OpenSSL_version`, `ASN1_STRING_get0_data` `ASN1_INTEGER_free`, ### Functional From 528be04fe5db23fcc06396fa6465e6677d4a068c Mon Sep 17 00:00:00 2001 From: simnJS Date: Wed, 28 Oct 2026 14:43:34 +0100 Subject: [PATCH 39/46] feat(libSceNpCppWebApi): answer abort, getId and getResponse of never-started transactions as the module does --- core/libs/prx/libSceNpCppWebApi/Export.cpp | 30 +++++++------------ .../prx/libSceNpCppWebApi/include/Api.hpp | 8 ++--- core/libs/tests/GuestNpCppWebApiSignedOut.cpp | 18 +++++++++++ docs/dev/debt/libSceNpCppWebApi.md | 1 + 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceNpCppWebApi/Export.cpp b/core/libs/prx/libSceNpCppWebApi/Export.cpp index d658955335..8aac44d4f1 100644 --- a/core/libs/prx/libSceNpCppWebApi/Export.cpp +++ b/core/libs/prx/libSceNpCppWebApi/Export.cpp @@ -2560,8 +2560,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6 int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERSB_(const void* self, void* response) { (void)self; (void)response; - NotImplemented_nid_no_patch(__func__); - return 0; + return TransactionNotStarted; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) { @@ -2575,8 +2574,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_1 int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_(const void* self, void* response) { (void)self; (void)response; - NotImplemented_nid_no_patch(__func__); - return 0; + return TransactionNotStarted; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self) { @@ -4867,16 +4865,14 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceC1EPNS1_6Common } int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() { - NotImplemented_nid_no_patch(__func__); - return 0; + return TransactionNotStarted; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V114PersonalDetailEED1Ev(IntrusivePtr* self) { ReleaseIntrusivePtr(self, 0x00, __func__); } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { - NotImplemented_nid_no_patch(__func__); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { return 0; } @@ -5068,8 +5064,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V125GetPublicProfilesResponse11g return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { - NotImplemented_nid_no_patch(__func__); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { return 0; } @@ -5083,8 +5078,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFr } int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() { - NotImplemented_nid_no_patch(__func__); - return 0; + return TransactionNotStarted; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEC1Ev(IntrusivePtr* self) { @@ -5113,8 +5107,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_30CommunicationRest CopyIntrusivePtr(self, source, 0x00); } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { - NotImplemented_nid_no_patch(__func__); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { return 0; } @@ -5137,16 +5130,14 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_ } int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() { - NotImplemented_nid_no_patch(__func__); - return 0; + return TransactionNotStarted; } int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBlockingUsers8setlimitEi() { return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { - NotImplemented_nid_no_patch(__func__); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { return 0; } @@ -5168,8 +5159,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence15setOnlineStat } int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv() { - NotImplemented_nid_no_patch(__func__); - return 0; + return TransactionNotStarted; } int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEdeEv() { diff --git a/core/libs/prx/libSceNpCppWebApi/include/Api.hpp b/core/libs/prx/libSceNpCppWebApi/include/Api.hpp index 76abf99f39..b8fd95d243 100644 --- a/core/libs/prx/libSceNpCppWebApi/include/Api.hpp +++ b/core/libs/prx/libSceNpCppWebApi/include/Api.hpp @@ -847,7 +847,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V124G void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEC1ERKS7_(IntrusivePtr* self, const IntrusivePtr* source); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriends8setlimitEi(); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEE3getEv(const IntrusivePtr* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEEaSERKSB_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V112BasicProfileEED1Ev(IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceD1Ev(); @@ -958,7 +958,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile23setIsOfficiall int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEED2Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi28ParameterToGetPublicProfiles10initializeEPNS1_6Common10LibContextEPKc(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEC1ERS7_(IntrusivePtr* self, const IntrusivePtr* source); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEC1Ev(IntrusivePtr* self); int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile23getIsOfficiallyVerifiedEv(); @@ -1002,7 +1002,7 @@ void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEC1Ev(IntrusivePtr* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEC1Ev(IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEE5beginEv(); int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V114PersonalDetail14getDisplayNameEv(); @@ -1011,7 +1011,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi28ParameterTo int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEED1Ev(IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); +std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEptEv(const IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBlockingUsers8setlimitEi(); diff --git a/core/libs/tests/GuestNpCppWebApiSignedOut.cpp b/core/libs/tests/GuestNpCppWebApiSignedOut.cpp index 5d7ea3cf4f..6fac1aec91 100644 --- a/core/libs/tests/GuestNpCppWebApiSignedOut.cpp +++ b/core/libs/tests/GuestNpCppWebApiSignedOut.cpp @@ -144,11 +144,13 @@ static const RequestFlow flows[] = { using Lifecycle = void (APS5_VABI*)(void*); using Finish = int (APS5_VABI*)(void*); using Identifier = std::int64_t (APS5_VABI*)(); +using Response = int (APS5_VABI*)(const void*, void*); struct TransactionLifecycle { Lifecycle construct; Lifecycle destroy; Finish finish; + Response getResponse; }; static const TransactionLifecycle lifecycles[] = { @@ -156,11 +158,13 @@ static const TransactionLifecycle lifecycles[] = { _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev, _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev, _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv, + _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERSB_, }, { _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev, _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev, _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv, + _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_, }, }; @@ -171,6 +175,10 @@ static const Call aborts[] = { _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, + _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, + _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, + _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, + _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv, }; static const Identifier identifiers[] = { @@ -179,6 +187,10 @@ static const Identifier identifiers[] = { _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, + _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, + _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, + _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, + _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv, }; static void TestNeverStartedTransactions() { @@ -189,6 +201,12 @@ static void TestNeverStartedTransactions() { for (std::size_t index = 0; index < sizeof(transaction); ++index) Require(transaction[index] == (index < 0x10 ? 0 : 0x5a)); Require(lifecycle.finish(transaction) == TransactionNotStarted); + alignas(16) unsigned char response[0x18]; + std::memset(response, 0x5a, sizeof(response)); + Require(lifecycle.getResponse(transaction, response) == TransactionNotStarted); + for (const unsigned char byte : response) Require(byte == 0x5a); + for (std::size_t index = 0; index < sizeof(transaction); ++index) + Require(transaction[index] == (index < 0x10 ? 0 : 0x5a)); lifecycle.destroy(transaction); for (std::size_t index = 0; index < sizeof(transaction); ++index) Require(transaction[index] == (index < 0x10 ? 0 : 0x5a)); diff --git a/docs/dev/debt/libSceNpCppWebApi.md b/docs/dev/debt/libSceNpCppWebApi.md index 1186c51691..3143ab1248 100644 --- a/docs/dev/debt/libSceNpCppWebApi.md +++ b/docs/dev/debt/libSceNpCppWebApi.md @@ -27,6 +27,7 @@ - [libSceNpCppWebApi](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - unimplemented stubs have their parameter lists, const qualifiers and return types aligned with the demangled symbols confirmed by NID matching in [#8143](https://github.com/boykopovar/AnyPS5/issues/8143). They cover response-body accessors, iterators, and uninstantiated transactions in Leaderboards::V1, SessionManager::V1, InGameCatalog::V5, TitleCloudStorage::V1, and Common templates: each mangled name gives the method and parameter types, but the runtime layout of response objects and errors are unverified on console, so each call continues to throw NotImplemented_nid_no_patch. No call site is recorded for them - [sce::Np::CppWebApi::SessionManager2V1 and sce::Np::CppWebApi::InGameCatalog2V5](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - 198 further exports imported by PPSA20397, named from aerolib. API calls, factories and transaction `start`/`getResponse*` fail `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter bookkeeping succeeds without state, except the `PlayerSessionsApi` parameters, which track initialization per the SessionManager::V1 entry; `IntrusivePtr`/`Vector` helpers follow the layouts above, with `Vector::empty`/`size`/`operator[]` reading host bodies and throwing on title-provided ones. Response accessors, iterators, `Vector::begin`/`end`/`copyFrom` and transaction `operator=` throw: a failed request produces no response object. As `start` never succeeds here, `TransactionBase::finish` and `abort` return 0x80553508 and `getId` returns 0, as the module bundled with PPSA21564 does while the two words its `Transaction` constructor zeroes are unset - [sce::Np::CppWebApi::UserProfile::V1, sce::Np::CppWebApi::CommunicationRestrictionStatus::V3 and the remaining sce::Np::CppWebApi::Common instantiations](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - 87 further exports imported by PPSA20397, plus the previously-throwing `ContainerApi::ParameterToGetContainer` lifecycle and `getContainer`. API verbs and transaction `start`/`getResponse` fail `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter and model bookkeeping (`initialize`, `terminate`, `set*`, constructors, destructors) succeeds without state. `BasicProfile`/`BasicPresence`/`PersonalDetail` response accessors, `*IsSet` queries, `Get*Response` getters, vector `begin`/`end`, iterator dereference/compare and transaction `abort`/`getId`/`operator=` throw: no response object exists offline and the model layouts are unverified +- [sce::Np::CppWebApi::Common::TransactionBase and Transaction](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - not read in a module for the four `UserProfile::V1` transactions: their `abort` and `getId` return 0x80553508 and 0, as the `TransactionBase` code read in the PPSA21564 module does for six and five other instantiations ([#11235](https://github.com/boykopovar/AnyPS5/pull/11235)). The `InGameCatalog::V5` `Vector>` and `TitleCloudStorage::V1` `GetMultiDataStatusesResponseBody` transactions still throw from `start` and `DataApi::getMultiDataStatusesByUser`: past the `LibContext` checks (0x80553503, 0x80553502), the module bundled with PPSA21567 returns what `libSceNpWebApi2` and the service answer, which is not measured. Their `getResponse` returns 0x80553508 and writes nothing, as [that module](https://github.com/boykopovar/AnyPS5/pull/10832#issuecomment-6416121806) does while the words at +0 and +8 are null - [sce::Np::CppWebApi title-imported exports](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - exports imported by the `sce_module` copies shipped with PPSA20397 and PPSA03530 that main did not declare. NIDs and signatures come only from the titles' import tables; const and return types are inferred from the demangled name. Layouts and offline behaviour follow the landed same-template implementations sourced from the module bundled with PPSA21564. Response-body accessors, `IsSet` queries, `Vector` begin/end, `Iterator` dereference/compare and transaction `operator=` throw: a failed request produces no response object and the layouts are unverified. `SessionManager::V1` `GameSessionsApi`, `Matches::V1`, `Matchmaking::V1`, `UserProfile::V1`, `TitleCloudStorage::V1` and `InGameCatalog::V5` accessors have no other source, so behaviour is only what the signatures admit. From d0778d6b83f4a22f4f33b7c36d25f607f715ce0b Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Mon, 2 Nov 2026 13:10:48 +0000 Subject: [PATCH 40/46] feat(libSceNpCppWebApi): fill audited Common/vector/model exports from bundled module Ports the title-imported unclaimed stubs decoded from the libSceNpCppWebApi module bundled with PPSA13197: Common iterators and Vector begin/end/ size/empty/copyFrom over the audited IntrusivePtr and unsigned-long families, TransactionBase assignment and start guards, Binary accessors, BasicPresence/BasicProfile model lifecycle and field accessors, Sku, Product, Container and response getters, and the restriction flag. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- core/libs/prx/libSceNpCppWebApi/Export.cpp | 516 ++++++++++-------- .../prx/libSceNpCppWebApi/include/Api.hpp | 144 ++--- .../tests/GuestNpCppWebApiIntrusivePtr.cpp | 33 +- docs/dev/debt/libSceNpCppWebApi.md | 6 +- 4 files changed, 385 insertions(+), 314 deletions(-) diff --git a/core/libs/prx/libSceNpCppWebApi/Export.cpp b/core/libs/prx/libSceNpCppWebApi/Export.cpp index 8aac44d4f1..a8ce8ed1b5 100644 --- a/core/libs/prx/libSceNpCppWebApi/Export.cpp +++ b/core/libs/prx/libSceNpCppWebApi/Export.cpp @@ -347,6 +347,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi12downloadDataEi static constexpr int ParameterNotInitialized = static_cast(0x80553502); static constexpr int ParameterInvalidArgument = static_cast(0x80553503); +static constexpr int ParameterAllocationFailed = static_cast(0x80553504); static constexpr int ParameterAlreadyInitialized = static_cast(0x8055350f); static constexpr int ParameterTerminatedUninitialized = static_cast(0x80553510); static constexpr int ParameterBodyMissing = static_cast(0x80553512); @@ -1859,6 +1860,98 @@ static CommonString* StringVectorElements(const IntrusivePtr* self, const bool e auto* elements = static_cast(body->elements); return end && body->size != 0 ? elements + body->size : elements; } + +static const VectorBody* HostVectorBody(const void* self, const std::int32_t elementKind, const char* function) { + const auto* body = static_cast(static_cast(self)->object); + if (body == nullptr) return nullptr; + if (body->magic != VectorBodyMagic || body->elementKind != elementKind) NotImplemented_nid_no_patch(function); + return body; +} + +static void* HostVectorBegin(void* out, const void* self, const std::int32_t elementKind, const char* function) { + const VectorBody* body = HostVectorBody(self, elementKind, function); + auto* it = static_cast(out); + it->referenceCount = 0; + it->element = body != nullptr ? body->elements : nullptr; + return out; +} + +static void* HostVectorEnd(void* out, const void* self, const std::int32_t elementKind, const std::size_t elementSize, const char* function) { + const VectorBody* body = HostVectorBody(self, elementKind, function); + auto* it = static_cast(out); + it->referenceCount = 0; + it->element = body != nullptr ? static_cast(body->elements) + body->size * elementSize : nullptr; + return out; +} + +static void* HostIteratorAdvance(void* self, const std::size_t elementSize) { + auto* it = static_cast(self); + it->element = static_cast(it->element) + elementSize; + return self; +} + +static bool HostIteratorNotEqual(const void* self, const void* other) { + return static_cast(self)->element != static_cast(other)->element; +} + +static bool VectorReserve(VectorBody* body, const std::size_t elementSize, const std::uint64_t count) { + if (body->capacity >= count) return true; + std::uint64_t capacity = body->capacity != 0 ? body->capacity : 4; + while (capacity < count) capacity *= 2; + void* elements = std::realloc(body->elements, static_cast(capacity) * elementSize); + if (elements == nullptr) return false; + body->elements = elements; + body->capacity = capacity; + return true; +} + +static int HostVectorCopyFrom(IntrusivePtr* self, const IntrusivePtr* source, const char* function) { + VectorBody* body = VectorBodyFor(self, 1, function); + if (body == nullptr) return ParameterAllocationFailed; + if (self->libContext == nullptr) self->libContext = source->libContext; + const auto* sourceBody = static_cast(source->object); + if (sourceBody != nullptr && (sourceBody->magic != VectorBodyMagic || sourceBody->elementKind != 1)) { + NotImplemented_nid_no_patch(function); + return ParameterAllocationFailed; + } + auto* elements = static_cast(body->elements); + for (std::uint64_t index = 0; index < body->size; ++index) { + ReleaseIntrusivePtr(&elements[index], ReferenceCountOffset, function); + } + body->size = 0; + const std::uint64_t count = sourceBody != nullptr ? sourceBody->size : 0; + if (count == 0) return 0; + if (!VectorReserve(body, sizeof(IntrusivePtr), count)) return ParameterAllocationFailed; + elements = static_cast(body->elements); + const auto* sourceElements = static_cast(sourceBody->elements); + for (std::uint64_t index = 0; index < count; ++index) { + elements[index] = {}; + CopyIntrusivePtr(&elements[index], &sourceElements[index], ReferenceCountOffset); + } + body->size = count; + return 0; +} + +static void* CopyMemberIntrusivePtr(void* out, const void* self, const std::size_t memberOffset, const std::size_t referenceCountOffset) { + CopyIntrusivePtr(static_cast(out), + reinterpret_cast(static_cast(self) + memberOffset), + referenceCountOffset); + return out; +} + +static void InitHostString(void* field, void* context) { + *static_cast(field) = {}; + static_cast(field)->libContext = context; +} + +static void DestroyHostString(void* field) { + auto* str = static_cast(field); + if (str->buffer != nullptr) { + std::free(str->buffer); + str->buffer = nullptr; + str->bufferSize = 0; + } +} // -------------------------------------------------------------------------- // SessionManager2V1 and extra Common symbols, from a title dump whose modules lack // libSceNpCppWebApi.prx. @@ -2081,10 +2174,8 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 CopyIntrusivePtr(self, source, 0x00); } -void* APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V130PostPlayerSessionsResponseBody17getPlayerSessionsEv(void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; +void* APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V130PostPlayerSessionsResponseBody17getPlayerSessionsEv(void* out, void* self) { + return CopyMemberIntrusivePtr(out, self, 0x30, VectorReferenceCountOffset); } int APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V117PlayerSessionsApi18leavePlayerSessionEiRKNS4_29ParameterToLeavePlayerSessionERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS8_18ResponseHeaderBaseEEEEE() { @@ -2391,9 +2482,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 } const char* APS5_VABI _ZNK3sce2Np9CppWebApi14SessionManager2V113PlayerSession12getSessionIdEv(const void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return static_cast(self) + 0x10; } int APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V158PostPlayerSessionsSessionIdMemberPlayersRequestBodyFactory6createEPNS1_6Common10LibContextERKNS5_6VectorINS5_12IntrusivePtrINS3_26RequestPlayerSessionPlayerEEEEEPNS9_INS3_51PostPlayerSessionsSessionIdMemberPlayersRequestBodyEEE() { @@ -2516,22 +2605,15 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_17TitleCloudStorage } bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEEneERKS9_(const void* self, const void* other) { - (void)self; - (void)other; - NotImplemented_nid_no_patch(__func__); - return false; + return HostIteratorNotEqual(self, other); } void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEEdeEv(const void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return static_cast(self)->element; } void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEEppEv(void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; + return HostIteratorAdvance(self, sizeof(IntrusivePtr)); } bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_17TitleCloudStorage2V110DataStatusEEEEneERKS9_(const void* self, const void* other) { @@ -2588,9 +2670,9 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(void* self, void* context) { (void)self; - (void)context; - NotImplemented_nid_no_patch(__func__); - return 0; + if (context == nullptr) return ParameterInvalidArgument; + if (*static_cast(context) == 0) return ParameterNotInitialized; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; } int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv(void* self) { @@ -2613,16 +2695,12 @@ std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1 return HostVectorSize(self, __func__); } -void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5beginEv(void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5beginEv(void* out, void* self) { + return HostVectorBegin(out, self, 1, __func__); } -void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE3endEv(void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE3endEv(void* out, void* self) { + return HostVectorEnd(out, self, 1, sizeof(IntrusivePtr), __func__); } std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE4sizeEv(const void* self) { @@ -2641,10 +2719,8 @@ CommonIterator* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrI return iterator; } -void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V59Container11getChildrenEv(void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; +void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V59Container11getChildrenEv(void* out, void* self) { + return CopyMemberIntrusivePtr(out, self, 0x228, VectorReferenceCountOffset); } int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainerC1Ev(void* self) { @@ -2685,22 +2761,16 @@ const char* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product8getLabelEv return nullptr; } -void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V57Product7getSkusEv(void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return nullptr; +void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V57Product7getSkusEv(void* out, void* self) { + return CopyMemberIntrusivePtr(out, self, 0x1e0, VectorReferenceCountOffset); } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product7getTypeEv(const void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product7getTypeEv(const void* self) { + return static_cast(self) + 0x1c0; } bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product9skusIsSetEv(const void* self) { - (void)self; - NotImplemented_nid_no_patch(__func__); - return false; + return *reinterpret_cast(static_cast(self) + 0x1f8) != 0; } int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi23ParameterToDownloadData11setobjectIdEPKc(void* self, const char* objectId) { @@ -3271,9 +3341,8 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 *self = {}; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEEdeEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEEdeEv(const void* self) { + return static_cast(self)->element; } void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V118LeaderWithOnlineIdEEptEv(const IntrusivePtr* self) { @@ -3436,49 +3505,40 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS return TransactionNotStarted; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv(void* out, void* self) { + return HostVectorEnd(out, self, 1, sizeof(IntrusivePtr), __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE5beginEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE5beginEv(void* out, void* self) { + return HostVectorBegin(out, self, 1, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE3endEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE3endEv(void* out, void* self) { + return HostVectorEnd(out, self, 3, sizeof(std::uint64_t), __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE5beginEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE5beginEv(void* out, void* self) { + return HostVectorBegin(out, self, 3, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEppEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEppEv(void* self) { + return HostIteratorAdvance(self, sizeof(IntrusivePtr)); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorImEppEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorImEppEv(void* self) { + return HostIteratorAdvance(self, sizeof(std::uint64_t)); } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getAccountIdEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getAccountIdEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x10); } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getInContextEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getInContextEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x21) != 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence15getOnlineStatusEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence15getOnlineStatusEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x1c); } int APS5_VABI _ZNK3sce2Np9CppWebApi15ProfanityFilter2V223FilterProfanityResponse10getMessageEv() { @@ -3486,9 +3546,8 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi15ProfanityFilter2V223FilterProfanityResponse return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponse13getRestrictedEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponse13getRestrictedEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x30) != 0; } void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEptEv(const IntrusivePtr* self) { @@ -3572,14 +3631,8 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 ReleaseIntrusivePtr(self, 0x00, __func__); } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5emptyEv(const IntrusivePtr* self) { - const auto* body = static_cast(self->object); - if (body == nullptr) return 1; - if (body->magic != VectorBodyMagic) { - NotImplemented_nid_no_patch(__func__); - return 0; - } - return body->size == 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5emptyEv(const void* self) { + return HostVectorSize(self, __func__) == 0; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEC1Ev(IntrusivePtr* self) { @@ -3667,18 +3720,16 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS return TransactionNotStarted; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv(const void* self) { + return HostVectorSize(self, __func__); } std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEdeEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEdeEv(const void* self) { + return static_cast(self)->element; } int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv() { @@ -3721,9 +3772,8 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS return TransactionNotStarted; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_(const void* self, const void* other) { + return HostIteratorNotEqual(self, other); } void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V139PatchPlayerSessionsSessionIdRequestBodyEEptEv(const IntrusivePtr* self) { @@ -3780,9 +3830,8 @@ std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12Intru return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEdeEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEdeEv(const void* self) { + return static_cast(self)->element; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V118LeaderWithOnlineIdEED1Ev(IntrusivePtr* self) { @@ -3793,28 +3842,20 @@ std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12Intru return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEneERKS4_() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEneERKS4_(const void* self, const void* other) { + return HostIteratorNotEqual(self, other); } -std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE4sizeEv(const IntrusivePtr* self) { - const auto* body = static_cast(self->object); - if (body == nullptr) return 0; - if (body->magic != VectorBodyMagic) { - NotImplemented_nid_no_patch(__func__); - return 0; - } - return body->size; +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE4sizeEv(const void* self) { + return HostVectorSize(self, __func__); } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14SessionManager2V120PlayerSessionForReadEEEEEED1Ev(IntrusivePtr* self) { ReleaseIntrusivePtr(self, 0x30, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5beginEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5beginEv(void* out, void* self) { + return HostVectorBegin(out, self, 1, __func__); } int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V130RequestPlayerSessionInvitationEEEE8pushBackERKS8_(IntrusivePtr* self, const void* element) { @@ -3845,9 +3886,8 @@ std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12Intru return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE8copyFromERKS9_() { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE8copyFromERKS9_(void* self, const void* source) { + return HostVectorCopyFrom(static_cast(self), static_cast(source), __func__); } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEED1Ev(IntrusivePtr* self) { @@ -3858,9 +3898,8 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14 *self = {}; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10labelIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10labelIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x80) != 0; } struct MatchesParameter { @@ -4530,72 +4569,60 @@ bool APS5_VABI _ZNK3sce2Np9CppWebApi7Matches2V119RequestInGameRoster12playersIsS } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10priceIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10priceIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0xd8) != 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getPriceEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getPriceEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0xd4); } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku15getDisplayPriceEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku15getDisplayPriceEv(const void* self) { + return static_cast(self) + 0xe0; } int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer7setsortENS5_4SortE() { return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku18originalPriceIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku18originalPriceIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x108) != 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDescriptionEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDescriptionEv(const void* self) { + return static_cast(self) + 0x130; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getReleaseDateEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getReleaseDateEv(const void* self) { + return static_cast(self) + 0x80; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku23getDisplayOriginalPriceEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku23getDisplayOriginalPriceEv(const void* self) { + return static_cast(self) + 0x110; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDisplayNameEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDisplayNameEv(const void* self) { + return static_cast(self) + 0x60; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku16getOriginalPriceEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku16getOriginalPriceEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x104); } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku25displayOriginalPriceIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku25displayOriginalPriceIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x130) != 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getLabelEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getLabelEv(const void* self) { + return static_cast(self) + 0x60; } int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer9setoffsetEi() { return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product16releaseDateIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product16releaseDateIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0xa0) != 0; } int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer15setcontainerIdsEPKc(void* self, const char* containerIds) { @@ -4606,53 +4633,44 @@ int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToG return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku17displayPriceIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku17displayPriceIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x100) != 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product5getIdEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product5getIdEv(const void* self) { + return static_cast(self) + 0x10; } int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv() { return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEaSERKSE_() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEaSERKSE_(void* self, const void* source) { + auto* dst = static_cast(self); + const auto* src = static_cast(source); + dst[0] = src[0]; + dst[1] = src[1]; + return self; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEppEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEppEv(void* self) { + return HostIteratorAdvance(self, sizeof(IntrusivePtr)); } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5emptyEv(const IntrusivePtr* self) { - const auto* body = static_cast(self->object); - if (body == nullptr) return 1; - if (body->magic != VectorBodyMagic) { - NotImplemented_nid_no_patch(__func__); - return 0; - } - return body->size == 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5emptyEv(const void* self) { + return HostVectorSize(self, __func__) == 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEneERKS9_() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEneERKS9_(const void* self, const void* other) { + return HostIteratorNotEqual(self, other); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE5beginEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE5beginEv(void* out, void* self) { + return HostVectorBegin(out, self, 1, __func__); } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEneERKS9_() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEneERKS9_(const void* self, const void* other) { + return HostIteratorNotEqual(self, other); } void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V57ProductEEEEEEdeEv(const IntrusivePtr* self) { @@ -4668,19 +4686,16 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_ return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE3endEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE3endEv(void* out, void* self) { + return HostVectorEnd(out, self, 1, sizeof(IntrusivePtr), __func__); } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEdeEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEdeEv(const void* self) { + return static_cast(self)->element; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEdeEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEdeEv(const void* self) { + return static_cast(self)->element; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V59ContainerEEEEEEC1ERKSA_(IntrusivePtr* self, const IntrusivePtr* source) { @@ -4697,14 +4712,12 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_ return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5beginEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5beginEv(void* out, void* self) { + return HostVectorBegin(out, self, 1, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE3endEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE3endEv(void* out, void* self) { + return HostVectorEnd(out, self, 1, sizeof(IntrusivePtr), __func__); } void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V53SkuEEEEEEdeEv(const IntrusivePtr* self) { @@ -4716,14 +4729,8 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGa return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5emptyEv(const IntrusivePtr* self) { - const auto* body = static_cast(self->object); - if (body == nullptr) return 1; - if (body->magic != VectorBodyMagic) { - NotImplemented_nid_no_patch(__func__); - return 0; - } - return body->size == 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5emptyEv(const void* self) { + return HostVectorSize(self, __func__) == 0; } void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V59ContainerEEEEEEaSERKSA_(IntrusivePtr* self, const IntrusivePtr* source) { @@ -4804,16 +4811,26 @@ CommonString* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_6StringEEdeEv( // libSceNpCppWebApi.prx. // -------------------------------------------------------------------------- -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile19personalDetailIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile19personalDetailIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x40) != 0; } int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi28ParameterToGetPublicProfiles9terminateEv() { return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE(void* self, void* context) { + auto* object = static_cast(self); + *reinterpret_cast(object) = 0; + *reinterpret_cast(object + 8) = context; + std::memset(object + 0x10, 0, 0x38); + InitHostString(object + 0x48, context); + object[0x68] = 0; + std::memset(object + 0x70, 0, 0x40); + *reinterpret_cast(object + 0xb0) = 0; + object[0xb8] = 0; + *reinterpret_cast(object + 0xbc) = 0; + object[0xc0] = 0; return 0; } @@ -4842,9 +4859,8 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11 return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile25isOfficiallyVerifiedIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile25isOfficiallyVerifiedIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0xaa) != 0; } int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence14accountIdIsSetEv() { @@ -4856,11 +4872,21 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11 ReleaseIntrusivePtr(self, 0x30, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setAccountIdERKm() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setAccountIdERKm(void* self, const std::uint64_t* accountId) { + auto* object = static_cast(self); + *reinterpret_cast(object + 0x10) = *accountId; + object[0x18] = 1; return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceC1EPNS1_6Common10LibContextE() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceC1EPNS1_6Common10LibContextE(void* self, void* context) { + auto* object = static_cast(self); + *reinterpret_cast(object) = 0; + *reinterpret_cast(object + 8) = context; + *reinterpret_cast(object + 0x10) = 0; + object[0x18] = 0; + *reinterpret_cast(object + 0x1c) = 0; + *reinterpret_cast(object + 0x1f) = 0; return 0; } @@ -4880,7 +4906,10 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_1 return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setInContextERKb() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setInContextERKb(void* self, const bool* inContext) { + auto* object = static_cast(self); + object[0x21] = *inContext ? 1 : 0; + object[0x22] = 1; return 0; } @@ -4908,7 +4937,11 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V118G ReleaseIntrusivePtr(self, 0x00, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile11setOnlineIdERK13SceNpOnlineId() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile11setOnlineIdERK13SceNpOnlineId(void* self, const void* onlineId) { + auto* object = static_cast(self); + std::memcpy(object + 0x10, onlineId, 0x10); + *reinterpret_cast(object + 0x20) = *reinterpret_cast(static_cast(onlineId) + 0x10); + object[0x24] = 1; return 0; } @@ -4962,14 +4995,12 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V114PersonalDetail14getDisplayN return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile13onlineIdIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile13onlineIdIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x24) != 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence14inContextIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence14inContextIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x22) != 0; } int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv() { @@ -4988,8 +5019,13 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V112B ReleaseIntrusivePtr(self, 0x00, __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileD1Ev() { - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileD1Ev(void* self) { + auto* object = static_cast(self); + ReleaseIntrusivePtr(reinterpret_cast(object + 0x90), StringVectorReferenceCountOffset, __func__); + ReleaseIntrusivePtr(reinterpret_cast(object + 0x70), VectorReferenceCountOffset, __func__); + DestroyHostString(object + 0x48); + ReleaseIntrusivePtr(reinterpret_cast(object + 0x28), ReferenceCountOffset, __func__); + return self; } int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEneERKS9_() { @@ -5059,18 +5095,16 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V112B CopyIntrusivePtr(self, source, 0x00); } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V125GetPublicProfilesResponse11getProfilesEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V125GetPublicProfilesResponse11getProfilesEv(void* out, void* self) { + return CopyMemberIntrusivePtr(out, self, 0x30, VectorReferenceCountOffset); } std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv() { return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEppEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEppEv(void* self) { + return HostIteratorAdvance(self, sizeof(IntrusivePtr)); } int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriends8setlimitEi() { @@ -5085,13 +5119,15 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V124G *self = {}; } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile23setIsOfficiallyVerifiedERKb() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile23setIsOfficiallyVerifiedERKb(void* self, const bool* verified) { + auto* object = static_cast(self); + object[0xa9] = *verified ? 1 : 0; + object[0xaa] = 1; return 0; } -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence17onlineStatusIsSetEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence17onlineStatusIsSetEv(const void* self) { + return *reinterpret_cast(static_cast(self) + 0x20) != 0; } int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi28ParameterToGetPublicProfilesD1Ev() { @@ -5111,8 +5147,8 @@ std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12Intru return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceD1Ev() { - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceD1Ev(void* self) { + return self; } int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1Ev(void* self) { @@ -5149,12 +5185,14 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V19BlocksApi27ParameterToGetBloc return 0; } -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEE3endEv() { - NotImplemented_nid_no_patch(__func__); - return 0; +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEE3endEv(void* out, void* self) { + return HostVectorEnd(out, self, 1, sizeof(IntrusivePtr), __func__); } -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence15setOnlineStatusERKNS3_12OnlineStatusE() { +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence15setOnlineStatusERKNS3_12OnlineStatusE(void* self, const std::uint32_t* status) { + auto* object = static_cast(self); + *reinterpret_cast(object + 0x1c) = *status; + object[0x20] = 1; return 0; } diff --git a/core/libs/prx/libSceNpCppWebApi/include/Api.hpp b/core/libs/prx/libSceNpCppWebApi/include/Api.hpp index b8fd95d243..7ad62d55b2 100644 --- a/core/libs/prx/libSceNpCppWebApi/include/Api.hpp +++ b/core/libs/prx/libSceNpCppWebApi/include/Api.hpp @@ -358,7 +358,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 const char* APS5_VABI _ZNK3sce2Np9CppWebApi14SessionManager2V136UsersPlayerSessionsInvitationForRead12getSessionIdEv(const void* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi14SessionManager2V136UsersPlayerSessionsInvitationForRead7getFromEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V129PostPlayerSessionsRequestBodyEEC1ERS7_(IntrusivePtr* self, const IntrusivePtr* source); -void* APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V130PostPlayerSessionsResponseBody17getPlayerSessionsEv(void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V130PostPlayerSessionsResponseBody17getPlayerSessionsEv(void* out, void* self); int APS5_VABI _ZN3sce2Np9CppWebApi14SessionManager2V117PlayerSessionsApi18leavePlayerSessionEiRKNS4_29ParameterToLeavePlayerSessionERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS8_18ResponseHeaderBaseEEEEE(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V124PlayerSessionPushContextEEEE8pushBackERKS8_(IntrusivePtr* self, const void* element); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(); @@ -474,12 +474,12 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_17TitleCloudStorage2V132GetMultiDataStatusesResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(void* self, void* context); void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEixEm(void* self, std::uint64_t index); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE4sizeEv(const void* self); -void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5beginEv(void* self); -void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE3endEv(void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5beginEv(void* out, void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE3endEv(void* out, void* self); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE4sizeEv(const void* self); CommonIterator* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_17TitleCloudStorage2V110DataStatusEEEE5beginEv(CommonIterator* iterator, const IntrusivePtr* self); CommonIterator* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_17TitleCloudStorage2V110DataStatusEEEE3endEv(CommonIterator* iterator, const IntrusivePtr* self); -void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V59Container11getChildrenEv(void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V59Container11getChildrenEv(void* out, void* self); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainerC1Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer10initializeEPNS1_6Common10LibContextE(void* self, const std::uint64_t* context); bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer13isInitializedEv(const void* self); @@ -488,8 +488,8 @@ int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToG int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainerD1Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi12getContainerEiRKNS4_23ParameterToGetContainerERNS1_6Common11TransactionINS8_12IntrusivePtrINS8_6VectorINSA_INS3_9ContainerEEEEEEENSA_INS8_18ResponseHeaderBaseEEEEE(); const char* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product8getLabelEv(const void* self); -void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V57Product7getSkusEv(void* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product7getTypeEv(const void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V57Product7getSkusEv(void* out, void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product7getTypeEv(const void* self); bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product9skusIsSetEv(const void* self); int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi23ParameterToDownloadData11setobjectIdEPKc(void* self, const char* objectId); int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi37ParameterToGetMultiDataStatusesByUserC1Ev(void* self); @@ -650,7 +650,7 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6StringEED1Ev(Intru void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEED1Ev(IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEED2Ev(void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V112JoinableUserEEC1Ev(IntrusivePtr* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEEdeEv(); +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEEdeEv(const void* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V118LeaderWithOnlineIdEEptEv(const IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V112JoinableUserEEEED1Ev(IntrusivePtr* self); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv(); @@ -690,17 +690,17 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common18ResponseHeaderBase14getHeaderValueEPKcRNS2_12IntrusivePtrINS2_6StringEEE(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE5beginEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE3endEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE5beginEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEppEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorImEppEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getAccountIdEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getInContextEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence15getOnlineStatusEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv(void* out, void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE5beginEv(void* out, void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE3endEv(void* out, void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorImE5beginEv(void* out, void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEppEv(void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorImEppEv(void* self); +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getAccountIdEv(const void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getInContextEv(const void* self); +std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence15getOnlineStatusEv(const void* self); int APS5_VABI _ZNK3sce2Np9CppWebApi15ProfanityFilter2V223FilterProfanityResponse10getMessageEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponse13getRestrictedEv(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponse13getRestrictedEv(const void* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEptEv(const IntrusivePtr* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEptEv(const IntrusivePtr* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V124GetBlockingUsersResponseEEptEv(const IntrusivePtr* self); @@ -721,7 +721,7 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrIN int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_15ProfanityFilter2V223FilterProfanityResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_(); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE18getResponseHeadersERSA_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEED1Ev(IntrusivePtr* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5emptyEv(const IntrusivePtr* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5emptyEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEC1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V143PutPlayerSessionsSessionIdLeaderRequestBodyEEC1ERS7_(IntrusivePtr* self, const IntrusivePtr* source); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V139PatchPlayerSessionsSessionIdRequestBodyEEC1ERS7_(IntrusivePtr* self, const IntrusivePtr* source); @@ -742,9 +742,9 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V12ToEED1Ev(IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv(); +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorImE4sizeEv(const void* self); std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V129GetPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEdeEv(); +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEdeEv(const void* self); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv(); void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEEixEm(IntrusivePtr* self, unsigned long index); void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEED1Ev(IntrusivePtr* self); @@ -753,7 +753,7 @@ int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14 void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V143PutPlayerSessionsSessionIdLeaderRequestBodyEEC1Ev(IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEED1Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_(const void* self, const void* other); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V139PatchPlayerSessionsSessionIdRequestBodyEEptEv(const IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V113PlayerSessionEED1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V139PatchPlayerSessionsSessionIdRequestBodyEEC1Ev(IntrusivePtr* self); @@ -767,13 +767,13 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_1 void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V113PlayerSessionEEC1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V130RequestPlayerSessionInvitationEEEEC1EPNS2_10LibContextE(IntrusivePtr* self, void* libContext); std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEdeEv(); +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEdeEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V118LeaderWithOnlineIdEED1Ev(IntrusivePtr* self); std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V130PostPlayerSessionsResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEneERKS4_(); -std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE4sizeEv(const IntrusivePtr* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorImEneERKS4_(const void* self, const void* other); +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE4sizeEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14SessionManager2V120PlayerSessionForReadEEEEEED1Ev(IntrusivePtr* self); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5beginEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE5beginEv(void* out, void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V130RequestPlayerSessionInvitationEEEE8pushBackERKS8_(IntrusivePtr* self, const void* element); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V161PostPlayerSessionsSessionIdJoinableSpecifiedUsersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); @@ -781,49 +781,49 @@ void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V1 void APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V130RequestPlayerSessionInvitationEEEED1Ev(IntrusivePtr* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEptEv(const IntrusivePtr* self); std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_14SessionManager2V152PostPlayerSessionsSessionIdMemberPlayersResponseBodyEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE8copyFromERKS9_(); +int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V127ResponsePlayerSessionPlayerEEEE8copyFromERKS9_(void* self, const void* source); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_14SessionManager2V150PostPlayerSessionsSessionIdInvitationsResponseBodyEED1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_14SessionManager2V113PlayerSessionEEEEEEC1Ev(IntrusivePtr* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10labelIsSetEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10priceIsSetEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getPriceEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku15getDisplayPriceEv(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10labelIsSetEv(const void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku10priceIsSetEv(const void* self); +std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getPriceEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku15getDisplayPriceEv(const void* self); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer7setsortENS5_4SortE(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku18originalPriceIsSetEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDescriptionEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getReleaseDateEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku23getDisplayOriginalPriceEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDisplayNameEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku16getOriginalPriceEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku25displayOriginalPriceIsSetEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getLabelEv(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku18originalPriceIsSetEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDescriptionEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getReleaseDateEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku23getDisplayOriginalPriceEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product14getDisplayNameEv(const void* self); +std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku16getOriginalPriceEv(const void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku25displayOriginalPriceIsSetEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku8getLabelEv(const void* self); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer9setoffsetEi(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product16releaseDateIsSetEv(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product16releaseDateIsSetEv(const void* self); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer15setcontainerIdsEPKc(void* self, const char* containerIds); int APS5_VABI _ZN3sce2Np9CppWebApi13InGameCatalog2V512ContainerApi23ParameterToGetContainer8setlimitEi(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku17displayPriceIsSetEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product5getIdEv(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V53Sku17displayPriceIsSetEv(const void* self); +const void* APS5_VABI _ZNK3sce2Np9CppWebApi13InGameCatalog2V57Product5getIdEv(const void* self); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEE11hasResponseEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEaSERKSE_(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEppEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5emptyEv(const IntrusivePtr* self); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEneERKS9_(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE5beginEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEneERKS9_(); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEaSERKSE_(void* self, const void* source); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEppEv(void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE5emptyEv(const void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEneERKS9_(const void* self, const void* other); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE5beginEv(void* out, void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEneERKS9_(const void* self, const void* other); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V57ProductEEEEEEdeEv(const IntrusivePtr* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_13InGameCatalog2V53SkuEEptEv(const IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEED2Ev(void* self); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE3endEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEdeEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEdeEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE3endEv(void* out, void* self); +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEEdeEv(const void* self); +void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEdeEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V59ContainerEEEEEEC1ERKSA_(IntrusivePtr* self, const IntrusivePtr* source); int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS2_6VectorINS4_INS1_13InGameCatalog2V59ContainerEEEEEEENS4_INS2_18ResponseHeaderBaseEEEEC1ERKSE_(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEED2Ev(void* self); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5beginEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE3endEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5beginEv(void* out, void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEE3endEv(void* out, void* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V53SkuEEEEEEdeEv(const IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_13InGameCatalog2V53SkuEEEEppEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5emptyEv(const IntrusivePtr* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V59ContainerEEEE5emptyEv(const void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_13InGameCatalog2V59ContainerEEEEEEaSERKSA_(IntrusivePtr* self, const IntrusivePtr* source); CommonIterator* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_6StringEEppEv(CommonIterator* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorImEEEC1Ev(IntrusivePtr* self); @@ -850,10 +850,10 @@ void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_ std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEEaSERKSB_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V112BasicProfileEED1Ev(IntrusivePtr* self); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceD1Ev(); +void* APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceD1Ev(void* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEptEv(const IntrusivePtr* self); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi28ParameterToGetPublicProfiles9terminateEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V125GetPublicProfilesResponse11getProfilesEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V125GetPublicProfilesResponse11getProfilesEv(void* out, void* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V112BasicProfileEEEEEEaSERKSA_(IntrusivePtr* self, const IntrusivePtr* source); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEED1Ev(IntrusivePtr* self); int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile17getPersonalDetailEv(); @@ -949,13 +949,13 @@ bool APS5_VABI _ZNK3sce2Np9CppWebApi7Matches2V119RequestInGameRoster10teamsIsSet bool APS5_VABI _ZNK3sce2Np9CppWebApi7Matches2V119RequestInGameRoster12playersIsSetEv(const void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5startEPNS2_10LibContextE(); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi17getPublicProfilesEiRKNS4_28ParameterToGetPublicProfilesERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_25GetPublicProfilesResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE(); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setInContextERKb(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile13onlineIdIsSetEv(); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setInContextERKb(void* self, const bool* inContext); +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile13onlineIdIsSetEv(const void* self); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEaSERKS7_(IntrusivePtr* self, const IntrusivePtr* source); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence15setOnlineStatusERKNS3_12OnlineStatusE(); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile23setIsOfficiallyVerifiedERKb(); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE(); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence15setOnlineStatusERKNS3_12OnlineStatusE(void* self, const std::uint32_t* status); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile23setIsOfficiallyVerifiedERKb(void* self, const bool* verified); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE(void* self, void* context); int APS5_VABI _ZN3sce2Np9CppWebApi6Common13ConstIteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEED2Ev(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V115BasicProfileApi28ParameterToGetPublicProfiles10initializeEPNS1_6Common10LibContextEPKc(); std::int64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5getIdEv(); @@ -968,20 +968,20 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_1 int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEEC1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEED1Ev(IntrusivePtr* self); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEE3endEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence17onlineStatusIsSetEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEE3endEv(void* out, void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence17onlineStatusIsSetEv(const void* self); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE11getResponseERS8_(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE6finishEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceC1EPNS1_6Common10LibContextE(); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceC1EPNS1_6Common10LibContextE(void* self, void* context); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V113BasicPresenceEEEEEEaSERKSA_(IntrusivePtr* self, const IntrusivePtr* source); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile25isOfficiallyVerifiedIsSetEv(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence14inContextIsSetEv(); +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile25isOfficiallyVerifiedIsSetEv(const void* self); +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence14inContextIsSetEv(const void* self); int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileD1Ev(); -int APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEppEv(); +void* APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileD1Ev(void* self); +void* APS5_VABI _ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEppEv(void* self); void* APS5_VABI _ZNK3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V112BasicProfileEEEEEE3getEv(const IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEC1Ev(IntrusivePtr* self); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile11setOnlineIdERK13SceNpOnlineId(); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile11setOnlineIdERK13SceNpOnlineId(void* self, const void* onlineId); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V125GetBasicPresencesResponseEEC1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V125GetPublicProfilesResponseEED1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS2_6VectorINS3_INS1_11UserProfile2V112BasicProfileEEEEEEC1Ev(IntrusivePtr* self); @@ -991,8 +991,8 @@ int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFr int APS5_VABI _ZN3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_11UserProfile2V118GetFriendsResponseEEENS4_INS2_18ResponseHeaderBaseEEEE5abortEv(); int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile11getOnlineIdEv(); int APS5_VABI _ZN3sce2Np9CppWebApi6Common11TransactionINS2_12IntrusivePtrINS1_30CommunicationRestrictionStatus2V338CommunicationRestrictionStatusResponseEEENS4_INS2_18ResponseHeaderBaseEEEEC1ERKSB_(void* self); -int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setAccountIdERKm(); -int APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile19personalDetailIsSetEv(); +int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setAccountIdERKm(void* self, const std::uint64_t* accountId); +bool APS5_VABI _ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile19personalDetailIsSetEv(const void* self); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V112BasicProfileEEEEneERKS9_(); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V114PersonalDetailEED1Ev(IntrusivePtr* self); void APS5_VABI _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEC1ERS7_(IntrusivePtr* self, const IntrusivePtr* source); @@ -1358,9 +1358,9 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrIN int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_12IntrusivePtrINS1_17TitleCloudStorage2V122UploadDataResponseBodyEEENS4_INS6_7DataApi25UploadDataResponseHeadersEEEE18getResponseHeadersERSB_(); int APS5_VABI _ZNK3sce2Np9CppWebApi6Common15TransactionBaseINS2_15DefaultResponseENS2_12IntrusivePtrINS2_18ResponseHeaderBaseEEEE18getResponseHeadersERS7_(); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_12Leaderboards2V15EntryEEEE4sizeEv(const void* self); -std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE4sizeEv(const void* self); +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_13InGameCatalog2V57ProductEEEE4sizeEv(const void* self); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V118GameSessionForReadEEEE4sizeEv(const void* self); -std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE4sizeEv(const void* self); +std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_14SessionManager2V120PlayerSessionForReadEEEE4sizeEv(const void* self); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V113RequestMemberEEEE4sizeEv(const void* self); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEEEE4sizeEv(const void* self); std::size_t APS5_VABI _ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V118RequestMatchPlayerEEEE4sizeEv(const void* self); diff --git a/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp b/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp index e9a056947e..a8c993f7b9 100644 --- a/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp +++ b/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp @@ -433,7 +433,38 @@ static void TestUserProfileVectorAssign() { } static void TestUserProfileModelAndTransaction() { - Require(_ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE() == 0); + std::uint8_t profile[0xd0]; + std::memset(profile, 0xa5, sizeof(profile)); + std::uint64_t context = 0; + Require(_ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileC1EPNS1_6Common10LibContextE(profile, &context) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile13onlineIdIsSetEv(profile) == false); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile19personalDetailIsSetEv(profile) == false); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile25isOfficiallyVerifiedIsSetEv(profile) == false); + const bool verified = true; + Require(_ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile23setIsOfficiallyVerifiedERKb(profile, &verified) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile25isOfficiallyVerifiedIsSetEv(profile)); + unsigned char onlineId[0x14]; + std::memset(onlineId, 0x61, sizeof(onlineId)); + Require(_ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfile11setOnlineIdERK13SceNpOnlineId(profile, onlineId) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V112BasicProfile13onlineIdIsSetEv(profile)); + Require(_ZN3sce2Np9CppWebApi11UserProfile2V112BasicProfileD1Ev(profile) == profile); + + std::uint8_t presence[0x28]; + std::memset(presence, 0xa5, sizeof(presence)); + Require(_ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceC1EPNS1_6Common10LibContextE(presence, &context) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence17onlineStatusIsSetEv(presence) == false); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence14inContextIsSetEv(presence) == false); + const std::uint64_t accountId = 0x1234; + Require(_ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setAccountIdERKm(presence, &accountId) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getAccountIdEv(presence) == 0x1234); + const bool inContext = true; + Require(_ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence12setInContextERKb(presence, &inContext) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence12getInContextEv(presence)); + const std::uint32_t status = 2; + Require(_ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresence15setOnlineStatusERKNS3_12OnlineStatusE(presence, &status) == 0); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence17onlineStatusIsSetEv(presence)); + Require(_ZNK3sce2Np9CppWebApi11UserProfile2V113BasicPresence15getOnlineStatusEv(presence) == 2); + Require(_ZN3sce2Np9CppWebApi11UserProfile2V113BasicPresenceD1Ev(presence) == presence); std::uint64_t transaction[0x20]; std::memset(transaction, 0xa5, sizeof(transaction)); diff --git a/docs/dev/debt/libSceNpCppWebApi.md b/docs/dev/debt/libSceNpCppWebApi.md index 3143ab1248..c46008969b 100644 --- a/docs/dev/debt/libSceNpCppWebApi.md +++ b/docs/dev/debt/libSceNpCppWebApi.md @@ -23,13 +23,15 @@ - [sce::Np::CppWebApi::SessionManager::V1 friends transaction](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the transaction is 0x10 bytes; its constructor zeroes it. `start` returns 0x80553503 for a null context, 0x80553502 for an uninitialized context and then `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; `getResponse` and `getFriendsPlayerSessions` return 0x80553508 while the two transaction words are zero. No response is produced; these offline paths are not console measurements. - [sce::Np::CppWebApi::SessionManager2V1](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by PPSA03671. Offline, the factories, `PlayerSessionsApi` calls and transaction `start`/`getResponse`/`readData` fail with `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter setters, `initialize`, `terminate`, constructors and destructors succeed without keeping state, as no request is ever sent. Response-body accessors (`getSessionId`, `getPlayerSessions`, `getInvitations`, the `IsSet` queries) throw: a failed request never produces a response object - [sce::Np::CppWebApi::Matchmaking::V1](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by PPSA21564 (86 of 92) and PPSA05510, PPSA25256, PPSA17226 and PPSA08395 for the other six; behaviour comes from the module bundled with PPSA21564/PPSA23732 (builds J03093737/J03341606), not from a console. Offline, the four `Api` calls and four factories return `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`. The four `ParameterTo*` objects keep only an initialized flag: `initialize` returns 0x80553503 for a null context or string, 0x80553502 for an unset context, 0x8055350f when already initialized and 0x80553512 for an empty `SubmitTicketRequestBody`; `terminate` returns 0x80553510. `ParameterToGetTicket::setview` returns 0x80553502 uninitialized and 0x80553503 for a null string. Other setters succeed without keeping state. Response accessors throw: a failed request produces no response. 0x80553504 is never returned; the stub stores a static pointer. -- [sce::Np::CppWebApi::UserProfile::V1, ProfanityFilter::V2, CommunicationRestrictionStatus::V3](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by an Epic Online Services module whose dump ships no `libSceNpCppWebApi.prx`. The reference-count offset 0x00 of `ResponseHeaderBase` and `WebApiFilterRequest` is assumed from the other objects, not measured. Offline, the five API calls, `WebApiFilterRequestFactory::create` and the transactions' `start`/`getResponseHeaders` fail with `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`. Response accessors, `ResponseHeaderBase::getHeaderValue` and the `Vector`/`Iterator` members over `unsigned long` and those over `IntrusivePtr` other than `size` throw: a failed request never produces a response +- [sce::Np::CppWebApi::UserProfile::V1, ProfanityFilter::V2, CommunicationRestrictionStatus::V3](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by an Epic Online Services module whose dump ships no `libSceNpCppWebApi.prx`. The reference-count offset 0x00 of `ResponseHeaderBase` and `WebApiFilterRequest` is assumed from the other objects, not measured. Offline, the five API calls, `WebApiFilterRequestFactory::create` and the transactions' `start`/`getResponseHeaders` fail with `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`. `ResponseHeaderBase::getHeaderValue` returns 0x80553512 when the header store pointer at 0x08 is null, matching the bundled module, and throws past that gate; the remaining response accessors throw: a failed request never produces a response. The `Vector`/`Iterator` members over `IntrusivePtr` and `unsigned long` follow the bundled module on host-created bodies and throw on title-provided ones - [libSceNpCppWebApi](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - unimplemented stubs have their parameter lists, const qualifiers and return types aligned with the demangled symbols confirmed by NID matching in [#8143](https://github.com/boykopovar/AnyPS5/issues/8143). They cover response-body accessors, iterators, and uninstantiated transactions in Leaderboards::V1, SessionManager::V1, InGameCatalog::V5, TitleCloudStorage::V1, and Common templates: each mangled name gives the method and parameter types, but the runtime layout of response objects and errors are unverified on console, so each call continues to throw NotImplemented_nid_no_patch. No call site is recorded for them - [sce::Np::CppWebApi::SessionManager2V1 and sce::Np::CppWebApi::InGameCatalog2V5](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - 198 further exports imported by PPSA20397, named from aerolib. API calls, factories and transaction `start`/`getResponse*` fail `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter bookkeeping succeeds without state, except the `PlayerSessionsApi` parameters, which track initialization per the SessionManager::V1 entry; `IntrusivePtr`/`Vector` helpers follow the layouts above, with `Vector::empty`/`size`/`operator[]` reading host bodies and throwing on title-provided ones. Response accessors, iterators, `Vector::begin`/`end`/`copyFrom` and transaction `operator=` throw: a failed request produces no response object. As `start` never succeeds here, `TransactionBase::finish` and `abort` return 0x80553508 and `getId` returns 0, as the module bundled with PPSA21564 does while the two words its `Transaction` constructor zeroes are unset - [sce::Np::CppWebApi::UserProfile::V1, sce::Np::CppWebApi::CommunicationRestrictionStatus::V3 and the remaining sce::Np::CppWebApi::Common instantiations](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - 87 further exports imported by PPSA20397, plus the previously-throwing `ContainerApi::ParameterToGetContainer` lifecycle and `getContainer`. API verbs and transaction `start`/`getResponse` fail `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter and model bookkeeping (`initialize`, `terminate`, `set*`, constructors, destructors) succeeds without state. `BasicProfile`/`BasicPresence`/`PersonalDetail` response accessors, `*IsSet` queries, `Get*Response` getters, vector `begin`/`end`, iterator dereference/compare and transaction `abort`/`getId`/`operator=` throw: no response object exists offline and the model layouts are unverified - [sce::Np::CppWebApi::Common::TransactionBase and Transaction](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - not read in a module for the four `UserProfile::V1` transactions: their `abort` and `getId` return 0x80553508 and 0, as the `TransactionBase` code read in the PPSA21564 module does for six and five other instantiations ([#11235](https://github.com/boykopovar/AnyPS5/pull/11235)). The `InGameCatalog::V5` `Vector>` and `TitleCloudStorage::V1` `GetMultiDataStatusesResponseBody` transactions still throw from `start` and `DataApi::getMultiDataStatusesByUser`: past the `LibContext` checks (0x80553503, 0x80553502), the module bundled with PPSA21567 returns what `libSceNpWebApi2` and the service answer, which is not measured. Their `getResponse` returns 0x80553508 and writes nothing, as [that module](https://github.com/boykopovar/AnyPS5/pull/10832#issuecomment-6416121806) does while the words at +0 and +8 are null +- [sce::Np::CppWebApi::SessionManager2V1 and sce::Np::CppWebApi::InGameCatalog2V5 audited accessors](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - measured on the bundled module: the `Sku` price/label fields and `*IsSet` bytes, `Product` `getId`/`getType`/`getDisplayName`/`getDescription`/`getReleaseDate`/`releaseDateIsSet`/`skusIsSet`/`getSkus`, `Container::getChildren` (vector handle at 0x228), `PlayerSession::getSessionId` (string at 0x10), the matching iterators, `Vector` `begin`/`end`/`empty`/`size`, `ResponsePlayerSessionPlayer` `copyFrom`/`size` and the Container-response `TransactionBase::operator=`/`start` guards +- [sce::Np::CppWebApi::UserProfile::V1 and CommunicationRestrictionStatus::V3 audited accessors](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - measured on the bundled module: `BasicPresence` construct/destruct, `set{AccountId,InContext,OnlineStatus}`, `get{AccountId,InContext,OnlineStatus}` and the `inContextIsSet`/`onlineStatusIsSet` bytes at 0x22/0x20; `BasicProfile` construct/destruct, `setOnlineId`, `setIsOfficiallyVerified` and the `onlineIdIsSet`/`personalDetailIsSet`/`isOfficiallyVerifiedIsSet` bytes at 0x24/0x40/0xaa; `GetPublicProfilesResponse::getProfiles` (vector handle at 0x30) and the restriction response `getRestricted` (byte at 0x30) -- [sce::Np::CppWebApi title-imported exports](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - exports imported by the `sce_module` copies shipped with PPSA20397 and PPSA03530 that main did not declare. NIDs and signatures come only from the titles' import tables; const and return types are inferred from the demangled name. Layouts and offline behaviour follow the landed same-template implementations sourced from the module bundled with PPSA21564. Response-body accessors, `IsSet` queries, `Vector` begin/end, `Iterator` dereference/compare and transaction `operator=` throw: a failed request produces no response object and the layouts are unverified. `SessionManager::V1` `GameSessionsApi`, `Matches::V1`, `Matchmaking::V1`, `UserProfile::V1`, `TitleCloudStorage::V1` and `InGameCatalog::V5` accessors have no other source, so behaviour is only what the signatures admit. +- [sce::Np::CppWebApi title-imported exports](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - exports imported by the `sce_module` copies shipped with PPSA20397 and PPSA03530 that main did not declare. NIDs and signatures come only from the titles' import tables; const and return types are inferred from the demangled name. Layouts and offline behaviour follow the landed same-template implementations sourced from the module bundled with PPSA21564. The audited subset reads the bundled module's measured layouts — model fields, `Vector`/`Iterator` members, `copyFrom` and the transaction `operator=`/`start` guards; the rest still throw: a failed request produces no response object. `SessionManager::V1` `GameSessionsApi`, `Matches::V1`, `Matchmaking::V1`, `UserProfile::V1`, `TitleCloudStorage::V1` and `InGameCatalog::V5` accessors have no other source, so behaviour is only what the signatures admit. From c94be8adf4b0dda931700a0984f958251c78ffa6 Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Mon, 2 Nov 2026 12:54:10 +0000 Subject: [PATCH 41/46] fix(libSceNpCppWebApi): accept the vector body the handle points to --- core/libs/prx/libSceNpCppWebApi/Export.cpp | 34 ++++++++++--- .../tests/GuestNpCppWebApiIntrusivePtr.cpp | 48 +++++++++++++++++++ docs/dev/debt/libSceNpCppWebApi.md | 3 +- 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceNpCppWebApi/Export.cpp b/core/libs/prx/libSceNpCppWebApi/Export.cpp index a8ce8ed1b5..ec63a71bcb 100644 --- a/core/libs/prx/libSceNpCppWebApi/Export.cpp +++ b/core/libs/prx/libSceNpCppWebApi/Export.cpp @@ -1739,7 +1739,8 @@ bool APS5_VABI _ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_12Le static constexpr std::uint64_t VectorBodyMagic = 0x434e507056656342ULL; struct VectorBody { std::uint64_t magic; - std::uint8_t reserved[0x28]; + void* libContext; + std::uint8_t reserved[0x20]; std::int32_t referenceCount; std::int32_t elementKind; // 1 = IntrusivePtr elements, 2 = String elements void* elements; @@ -1766,6 +1767,14 @@ static void APS5_VABI DestroyVectorBody(void* object) { } static VectorBody* VectorBodyFor(IntrusivePtr* self, const std::int32_t elementKind, const char* function) { + auto* direct = static_cast(static_cast(self)); + if (direct != nullptr && direct->magic == VectorBodyMagic) { + if (direct->elementKind != elementKind) { + NotImplemented_nid_no_patch(function); + return nullptr; + } + return direct; + } auto* body = static_cast(self->object); if (body == nullptr) { body = static_cast(std::calloc(1, sizeof(VectorBody))); @@ -1773,6 +1782,7 @@ static VectorBody* VectorBodyFor(IntrusivePtr* self, const std::int32_t elementK body->magic = VectorBodyMagic; body->referenceCount = 1; body->elementKind = elementKind; + body->libContext = self->libContext; self->object = body; self->deleter = &DestroyVectorBody; return body; @@ -1795,15 +1805,22 @@ static bool VectorGrow(VectorBody* body, const std::size_t elementSize) { return true; } +static const VectorBody* ResolveVectorBody(const void* self) { + const auto* direct = static_cast(self); + if (direct == nullptr) return nullptr; + if (direct->magic == VectorBodyMagic) return direct; + return static_cast(static_cast(self)->object); +} + static std::uint64_t HostVectorSize(const void* self, const char* function) { - const auto* body = static_cast(static_cast(self)->object); + const auto* body = ResolveVectorBody(self); if (body == nullptr) return 0; if (body->magic != VectorBodyMagic) NotImplemented_nid_no_patch(function); return body->size; } static IntrusivePtr* HostVectorElement(void* self, const std::uint64_t index, const char* function) { - auto* body = static_cast(static_cast(self)->object); + auto* body = const_cast(ResolveVectorBody(self)); if (body == nullptr || body->magic != VectorBodyMagic || body->elementKind != 1) NotImplemented_nid_no_patch(function); return static_cast(body->elements) + index; } @@ -1862,7 +1879,7 @@ static CommonString* StringVectorElements(const IntrusivePtr* self, const bool e } static const VectorBody* HostVectorBody(const void* self, const std::int32_t elementKind, const char* function) { - const auto* body = static_cast(static_cast(self)->object); + const auto* body = ResolveVectorBody(self); if (body == nullptr) return nullptr; if (body->magic != VectorBodyMagic || body->elementKind != elementKind) NotImplemented_nid_no_patch(function); return body; @@ -1908,8 +1925,13 @@ static bool VectorReserve(VectorBody* body, const std::size_t elementSize, const static int HostVectorCopyFrom(IntrusivePtr* self, const IntrusivePtr* source, const char* function) { VectorBody* body = VectorBodyFor(self, 1, function); if (body == nullptr) return ParameterAllocationFailed; - if (self->libContext == nullptr) self->libContext = source->libContext; - const auto* sourceBody = static_cast(source->object); + const auto* sourceDirect = static_cast(static_cast(source)); + const bool sourceIsBody = sourceDirect != nullptr && sourceDirect->magic == VectorBodyMagic; + const auto* sourceBody = sourceIsBody ? sourceDirect : static_cast(source->object); + void** const selfContext = body == static_cast(static_cast(self)) ? &body->libContext : &self->libContext; + void* const sourceContext = sourceIsBody ? sourceDirect->libContext : source->libContext; + if (*selfContext == nullptr) *selfContext = sourceContext; + if (body->libContext == nullptr) body->libContext = *selfContext; if (sourceBody != nullptr && (sourceBody->magic != VectorBodyMagic || sourceBody->elementKind != 1)) { NotImplemented_nid_no_patch(function); return ParameterAllocationFailed; diff --git a/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp b/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp index a8c993f7b9..4c9ce0e449 100644 --- a/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp +++ b/core/libs/tests/GuestNpCppWebApiIntrusivePtr.cpp @@ -366,6 +366,53 @@ static void TestScalarVectorAssignment() { Require(deleterCalls == 0); } +static void TestVectorFunctionsAcceptTheHandleObject() { + int context = 0; + + IntrusivePtr vector; + _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEEEEC1EPNS2_10LibContextE(&vector, &context); + unsigned char object[ObjectSize]; + std::memset(object, 0x5a, sizeof(object)); + WriteCount(object, 0, 1); + IntrusivePtr filled{object, RecordDelete, &context}; + Require(_ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEEEE8pushBackERKS8_(&vector, &filled) == 0); + Require(_ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEEEE4sizeEv(&vector) == 1); + Require(_ZNK3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEEEE4sizeEv(vector.object) == 1); + _ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEEEED1Ev(&vector); + _ZN3sce2Np9CppWebApi6Common12IntrusivePtrINS1_7Matches2V116RequestMatchTeamEED1Ev(&filled); + + alignas(8) unsigned char body[0x50] = {}; + const std::uint64_t magic = 0x434e507056656342ULL; + std::memcpy(body, &magic, sizeof(magic)); + WriteCount(body, VectorReferenceCountOffset, 1); + *reinterpret_cast(body + 0x34) = 1; + IntrusivePtr elements[2] = {}; + *reinterpret_cast(body + 0x38) = elements; + *reinterpret_cast(body + 0x40) = 2; + *reinterpret_cast(body + 0x48) = 2; + IntrusivePtr handle{body, nullptr, &context}; + + const auto presenceBegin = reinterpret_cast(_ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE5beginEv); + const auto presenceEnd = reinterpret_cast(_ZN3sce2Np9CppWebApi6Common6VectorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEE3endEv); + const auto presenceNext = reinterpret_cast(_ZN3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEppEv); + const auto presenceNotEqual = reinterpret_cast(_ZNK3sce2Np9CppWebApi6Common8IteratorINS2_12IntrusivePtrINS1_11UserProfile2V113BasicPresenceEEEEneERKS9_); + + unsigned char first[0x10] = {}, last[0x10] = {}; + Require(presenceBegin(first, body) == first); + Require(presenceEnd(last, body) == last); + Require(presenceNotEqual(first, last)); + Require(presenceNext(first) == first); + Require(presenceNotEqual(first, last)); + Require(presenceNext(first) == first); + Require(!presenceNotEqual(first, last)); + + Require(presenceBegin(first, &handle) == first); + Require(presenceEnd(last, &handle) == last); + Require(presenceNext(first) == first); + Require(presenceNext(first) == first); + Require(!presenceNotEqual(first, last)); +} + static void TestVectorAliasedGrowth() { unsigned cases = 0, moves = 0; for (unsigned sourceIndex = 0; sourceIndex < 4; ++sourceIndex) { @@ -644,6 +691,7 @@ int main(int argc, char** argv) { TestVectorPushBackReleasesElements(); TestAdoptedStringVectorCountsReferences(); TestScalarVectorAssignment(); + TestVectorFunctionsAcceptTheHandleObject(); TestUserProfileVectorAssign(); TestUserProfileModelAndTransaction(); TestUserProfilePublicProfileRequest(); diff --git a/docs/dev/debt/libSceNpCppWebApi.md b/docs/dev/debt/libSceNpCppWebApi.md index c46008969b..f264a6ad75 100644 --- a/docs/dev/debt/libSceNpCppWebApi.md +++ b/docs/dev/debt/libSceNpCppWebApi.md @@ -19,11 +19,12 @@ - [sce::Np::CppWebApi::Common::String](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the 0x20-byte layout (32-bit reference count, buffer at 0x08, used size including the terminator at 0x10, `LibContext*` at 0x18), the `+0x10` semantics and the `0x80553503`/`0x80553504` results follow the module bundled with PPSA21564 and PPSA23732 (builds J03093737/J03341606): `append`, `copyFrom`, `clear`, `size`, `length`, `operator==`, `setContext` and the intrusive reference operations match it. Allocation and release still use the host allocator instead of the `LibContext` allocator, so a title-provided buffer released through this destructor would use the wrong allocator; `find`, `replace` and `npos` remain unimplemented. `pushBack` into `Vector` stores a deep copy, so a destroyed temporary cannot invalidate the stored element; a title that byte-copies a `String` with a live buffer would still double-free, as nothing increments the reference count - [sce::Np::CppWebApi::Common::Vector](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by PPSA03671, whose dump ships no `libSceNpCppWebApi.prx`. A `Vector` handle is an `IntrusivePtr` over a shared body; bodies created by `pushBack` use a host layout with the reference count at 0x30. The pointer/deleter constructor adopts a title-provided `Vector`, counting the reference at 0x38, which the `Vector` constructor zeroes, as the module bundled with PPSA21564 does, and releases it through the stored deleter; `pushBack` on such a body throws, as its element layout is unknown. `Vector::begin`/`end` and `Iterator` operators walk host-created bodies and throw on title-provided ones; other `begin`, `end` and iterators, and `copyFrom`, throw: the only `Vector`s the title reads back come from failed transactions, which never produce one. `empty`, `size` and `operator[]` read host-created bodies and throw on title-provided ones - [sce::Np::CppWebApi::Common::Binary](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the 0x20-byte layout (32-bit reference count, buffer at 0x08, size at 0x10, `LibContext*` at 0x18) comes from the module bundled with PPSA21564/PPSA23732 (builds `J03093737`/`J03341606`); other SDK versions are not checked. `setBinary` and `appendBinary` use the host allocator instead of the `LibContext` allocator, reject a null source with a non-zero size where the module does not, keep the old contents when the input is invalid or allocation fails, and copy the input before releasing a buffer it aliases. A title that byte-copies a `Binary` with a live buffer would double-free, as nothing increments the reference count +- [sce::Np::CppWebApi::Common::Vector audited members](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - `begin`, `end`, iterators and `copyFrom` follow the bundled module for the audited `IntrusivePtr`/`unsigned long` families (iterator = zero tag plus element pointer; `copyFrom` replaces elements through the element reference count) and throw on adopted bodies and other families; they accept either the `IntrusivePtr` handle or the object its `operator->` returns, which module bodies read at offsets 0x18/0x28. `copyFrom` releases old elements — a deliberate deviation: the module installs the fresh zeroed array before its release loop - [sce::Np::CppWebApi::SessionManager::V1 friends player sessions](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the `getFriendsPlayerSessions` family follows the module bundled with the titles (build J03341606): `Friend` stores account id at 0x10, `accountIdIsSet` at 0x18, `IntrusivePtr>` at 0x30 and `playerSessionsIsSet` at 0x48; `FriendJoinedPlayerSession` stores `String sessionId` at 0x10 and `sessionIdIsSet` at 0x30; the response body stores `IntrusivePtr>` at 0x30. Vector views use the real inline layout (begin 0x18, end 0x28, refcount 0x30, 0x18-byte elements). The model layouts are measured in build J03341606; destroying the last vector reference without a deleter throws until the allocator lands. Other builds are not checked. - [sce::Np::CppWebApi::SessionManager::V1 friends transaction](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the transaction is 0x10 bytes; its constructor zeroes it. `start` returns 0x80553503 for a null context, 0x80553502 for an uninitialized context and then `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; `getResponse` and `getFriendsPlayerSessions` return 0x80553508 while the two transaction words are zero. No response is produced; these offline paths are not console measurements. - [sce::Np::CppWebApi::SessionManager2V1](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by PPSA03671. Offline, the factories, `PlayerSessionsApi` calls and transaction `start`/`getResponse`/`readData` fail with `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter setters, `initialize`, `terminate`, constructors and destructors succeed without keeping state, as no request is ever sent. Response-body accessors (`getSessionId`, `getPlayerSessions`, `getInvitations`, the `IsSet` queries) throw: a failed request never produces a response object - [sce::Np::CppWebApi::Matchmaking::V1](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by PPSA21564 (86 of 92) and PPSA05510, PPSA25256, PPSA17226 and PPSA08395 for the other six; behaviour comes from the module bundled with PPSA21564/PPSA23732 (builds J03093737/J03341606), not from a console. Offline, the four `Api` calls and four factories return `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`. The four `ParameterTo*` objects keep only an initialized flag: `initialize` returns 0x80553503 for a null context or string, 0x80553502 for an unset context, 0x8055350f when already initialized and 0x80553512 for an empty `SubmitTicketRequestBody`; `terminate` returns 0x80553510. `ParameterToGetTicket::setview` returns 0x80553502 uninitialized and 0x80553503 for a null string. Other setters succeed without keeping state. Response accessors throw: a failed request produces no response. 0x80553504 is never returned; the stub stores a static pointer. -- [sce::Np::CppWebApi::UserProfile::V1, ProfanityFilter::V2, CommunicationRestrictionStatus::V3](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by an Epic Online Services module whose dump ships no `libSceNpCppWebApi.prx`. The reference-count offset 0x00 of `ResponseHeaderBase` and `WebApiFilterRequest` is assumed from the other objects, not measured. Offline, the five API calls, `WebApiFilterRequestFactory::create` and the transactions' `start`/`getResponseHeaders` fail with `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`. `ResponseHeaderBase::getHeaderValue` returns 0x80553512 when the header store pointer at 0x08 is null, matching the bundled module, and throws past that gate; the remaining response accessors throw: a failed request never produces a response. The `Vector`/`Iterator` members over `IntrusivePtr` and `unsigned long` follow the bundled module on host-created bodies and throw on title-provided ones +- [sce::Np::CppWebApi::UserProfile::V1, ProfanityFilter::V2, CommunicationRestrictionStatus::V3](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - imported by an Epic Online Services module whose dump ships no `libSceNpCppWebApi.prx`. The reference-count offset 0x00 of `ResponseHeaderBase` and `WebApiFilterRequest` is assumed from the other objects, not measured. Offline, the five API calls, `WebApiFilterRequestFactory::create` and the transactions' `start`/`getResponseHeaders` fail with `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`. Response accessors and `ResponseHeaderBase::getHeaderValue` throw: a failed request never produces a response. The `Vector`/`Iterator` members over `IntrusivePtr` follow the bundled module on host-created bodies and throw on title-provided ones. Nothing can create a host `Vector` body, so its `begin`, `end` and iterator throw for every non-empty vector - [libSceNpCppWebApi](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - unimplemented stubs have their parameter lists, const qualifiers and return types aligned with the demangled symbols confirmed by NID matching in [#8143](https://github.com/boykopovar/AnyPS5/issues/8143). They cover response-body accessors, iterators, and uninstantiated transactions in Leaderboards::V1, SessionManager::V1, InGameCatalog::V5, TitleCloudStorage::V1, and Common templates: each mangled name gives the method and parameter types, but the runtime layout of response objects and errors are unverified on console, so each call continues to throw NotImplemented_nid_no_patch. No call site is recorded for them - [sce::Np::CppWebApi::SessionManager2V1 and sce::Np::CppWebApi::InGameCatalog2V5](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - 198 further exports imported by PPSA20397, named from aerolib. API calls, factories and transaction `start`/`getResponse*` fail `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter bookkeeping succeeds without state, except the `PlayerSessionsApi` parameters, which track initialization per the SessionManager::V1 entry; `IntrusivePtr`/`Vector` helpers follow the layouts above, with `Vector::empty`/`size`/`operator[]` reading host bodies and throwing on title-provided ones. Response accessors, iterators, `Vector::begin`/`end`/`copyFrom` and transaction `operator=` throw: a failed request produces no response object. As `start` never succeeds here, `TransactionBase::finish` and `abort` return 0x80553508 and `getId` returns 0, as the module bundled with PPSA21564 does while the two words its `Transaction` constructor zeroes are unset - [sce::Np::CppWebApi::UserProfile::V1, sce::Np::CppWebApi::CommunicationRestrictionStatus::V3 and the remaining sce::Np::CppWebApi::Common instantiations](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - 87 further exports imported by PPSA20397, plus the previously-throwing `ContainerApi::ParameterToGetContainer` lifecycle and `getContainer`. API verbs and transaction `start`/`getResponse` fail `SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN`; parameter and model bookkeeping (`initialize`, `terminate`, `set*`, constructors, destructors) succeeds without state. `BasicProfile`/`BasicPresence`/`PersonalDetail` response accessors, `*IsSet` queries, `Get*Response` getters, vector `begin`/`end`, iterator dereference/compare and transaction `abort`/`getId`/`operator=` throw: no response object exists offline and the model layouts are unverified From 396ef66fcce53064fdc7315803205a94eb0b0865 Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Fri, 6 Nov 2026 08:16:19 +1300 Subject: [PATCH 42/46] fix(libc): keep the e_atanh.c notice verbatim The merge of main dropped the trailing space after "notice" in the e_atanh.c licence block; upstream FreeBSD release/11.4.0 has it, as main does. Co-authored-by: JJenkx <31495916+JJenkx@users.noreply.github.com> --- core/libs/prx/libc/src/Math.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 1889cfde72..cfd98cceb2 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -1931,7 +1931,7 @@ float APS5_VABI acoshf_nid_postfix(float x) { * * Developed at SunSoft, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice + * software is freely granted, provided that this notice * is preserved. * ==================================================== * From a1a7983f11a5bcfea63f63a8c92f48eb1e05c3d3 Mon Sep 17 00:00:00 2001 From: lakhantheboss <61951686+lakhantheboss@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:03:04 +0530 Subject: [PATCH 43/46] feat(libSceNKWebKit): implement WTF::protocolHostAndPortAreEqual --- core/libs/prx/libSceNKWebKit/Export.cpp | 4 - .../include/InventoryExports.hpp | 1 - .../prx/libSceNKWebKit/include/WtfExports.hpp | 1 + .../libs/prx/libSceNKWebKit/src/WtfString.cpp | 21 +++ core/libs/prx/libSceNKWebKit/tests/Wtf.cpp | 122 ++++++++++++++++++ docs/dev/debt/libSceNKWebKit.md | 3 +- 6 files changed, 145 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceNKWebKit/Export.cpp b/core/libs/prx/libSceNKWebKit/Export.cpp index b2ecc459d8..8ea08a2e67 100644 --- a/core/libs/prx/libSceNKWebKit/Export.cpp +++ b/core/libs/prx/libSceNKWebKit/Export.cpp @@ -19454,10 +19454,6 @@ int APS5_VABI _ZN3WTF26normalizeLineEndingsToCRLFEONS_6VectorIhLm0ENS_15CrashOnO NotImplemented_nid_no_patch(__func__); } -int APS5_VABI _ZN3WTF27protocolHostAndPortAreEqualERKNS_3URLES2_() { - NotImplemented_nid_no_patch(__func__); -} - int APS5_VABI _ZN3WTF28encodeWithURLEscapeSequencesERKNS_6StringE() { NotImplemented_nid_no_patch(__func__); } diff --git a/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp b/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp index 1294a580d9..d3f9823f6a 100644 --- a/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp +++ b/core/libs/prx/libSceNKWebKit/include/InventoryExports.hpp @@ -1834,7 +1834,6 @@ int APS5_VABI _ZN3WTF24normalizeLineEndingsToLFEONS_6VectorIhLm0ENS_15CrashOnOve int APS5_VABI _ZN3WTF25activateSignalHandlersForENS_6SignalE(); int APS5_VABI _ZN3WTF26getAndResetAccumulatedLogsEv(); int APS5_VABI _ZN3WTF26normalizeLineEndingsToCRLFEONS_6VectorIhLm0ENS_15CrashOnOverflowELm16ENS_10FastMallocEEE(); -int APS5_VABI _ZN3WTF27protocolHostAndPortAreEqualERKNS_3URLES2_(); int APS5_VABI _ZN3WTF28encodeWithURLEscapeSequencesERKNS_6StringE(); int APS5_VABI _ZN3WTF28normalizeLineEndingsToNativeEONS_6VectorIhLm0ENS_15CrashOnOverflowELm16ENS_10FastMallocEEE(); int APS5_VABI _ZN3WTF30numCodeUnitsInGraphemeClustersENS_10StringViewEj(); diff --git a/core/libs/prx/libSceNKWebKit/include/WtfExports.hpp b/core/libs/prx/libSceNKWebKit/include/WtfExports.hpp index 860b654119..04d520ffb2 100644 --- a/core/libs/prx/libSceNKWebKit/include/WtfExports.hpp +++ b/core/libs/prx/libSceNKWebKit/include/WtfExports.hpp @@ -136,6 +136,7 @@ WTF::LocalTimeOffset APS5_VABI _ZN3WTF24calculateLocalTimeOffsetEdNS_8TimeTypeE( void APS5_VABI _ZN3WTF25addLanguageChangeObserverEPvPFvS0_E(void* context, void (APS5_VABI *observer)(void*)); void APS5_VABI _ZN3WTF25fastDecommitAlignedMemoryEPvm(void*, std::size_t); void APS5_VABI _ZN3WTF25fastMallocDumpMallocStatsEv(); +bool APS5_VABI _ZN3WTF27protocolHostAndPortAreEqualERKNS_3URLES2_(const NKWebKit::WtfAbi::URL* a, const NKWebKit::WtfAbi::URL* b); void APS5_VABI _ZN3WTF27releaseFastMallocFreeMemoryEv(); NKWebKit::WtfAbi::String APS5_VABI _ZN3WTF28displayNameForLanguageLocaleERKNS_6StringE(const NKWebKit::WtfAbi::String* localeName); void APS5_VABI _ZN3WTF28removeLanguageChangeObserverEPv(void* context); diff --git a/core/libs/prx/libSceNKWebKit/src/WtfString.cpp b/core/libs/prx/libSceNKWebKit/src/WtfString.cpp index 32241b1e3d..f6ed222e4b 100644 --- a/core/libs/prx/libSceNKWebKit/src/WtfString.cpp +++ b/core/libs/prx/libSceNKWebKit/src/WtfString.cpp @@ -1116,6 +1116,27 @@ bool APS5_VABI _ZN3WTF31equalIgnoringFragmentIdentifierERKNS_3URLES2_(const URL* return EqualViews(ViewWithoutFragment(first), ViewWithoutFragment(second)); } +bool APS5_VABI _ZN3WTF27protocolHostAndPortAreEqualERKNS_3URLES2_(const URL* a, const URL* b) { + const auto& first = Checked(a); + const auto& second = Checked(b); + unsigned schemeEndA = first.bits >> 6; + unsigned schemeEndB = second.bits >> 6; + if (schemeEndA != schemeEndB) return false; + unsigned hostStartA = HostStart(first); + unsigned hostStartB = HostStart(second); + if (first.hostEnd < hostStartA || second.hostEnd < hostStartB) return false; + unsigned hostLengthA = first.hostEnd - hostStartA; + unsigned hostLengthB = second.hostEnd - hostStartB; + if (hostLengthA != hostLengthB) return false; + auto viewA = View(first.impl); + auto viewB = View(second.impl); + if (schemeEndA > viewA.length || schemeEndB > viewB.length) return false; + if (hostStartA + hostLengthA > viewA.length || hostStartB + hostLengthB > viewB.length) return false; + if (!EqualViews(Slice(viewA, 0, schemeEndA), Slice(viewB, 0, schemeEndB), true)) return false; + if (!EqualViews(Slice(viewA, hostStartA, hostLengthA), Slice(viewB, hostStartB, hostLengthB), true)) return false; + return URLPort(first) == URLPort(second); +} + void APS5_VABI _ZN3WTF3URL10invalidateEv(URL* self) { auto& url = Checked(self); url.bits = 0; diff --git a/core/libs/prx/libSceNKWebKit/tests/Wtf.cpp b/core/libs/prx/libSceNKWebKit/tests/Wtf.cpp index 9add2fce5e..d28828914b 100644 --- a/core/libs/prx/libSceNKWebKit/tests/Wtf.cpp +++ b/core/libs/prx/libSceNKWebKit/tests/Wtf.cpp @@ -804,6 +804,7 @@ const auto URLJavaScript = &_ZNK3WTF3URL20protocolIsJavaScriptEv; const auto URLInvalidate = &_ZN3WTF3URL10invalidateEv; const auto URLEqualIgnoringFragment = &_ZN3WTF31equalIgnoringFragmentIdentifierERKNS_3URLES2_; const auto URLPortAllowed = &_ZN3WTF11portAllowedERKNS_3URLE; +const auto URLProtocolHostAndPortAreEqual = &_ZN3WTF27protocolHostAndPortAreEqualERKNS_3URLES2_; const auto IsJavaScript = &_ZN3WTF20protocolIsJavaScriptENS_10StringViewE; const auto IsHTTP = &_ZN3WTF22protocolIsInHTTPFamilyENS_10StringViewE; const auto JSRealloc = &_ZN3WTF14jsValueReallocEPvm; @@ -1386,6 +1387,127 @@ void TestURLsAndReallocation() { Check(URLPortAllowed(&urlWraparoundPort)); Check(URLPortAllowed(&absent)); Throws([&] { URLPortAllowed(nullptr); }); + { + Check(URLProtocolHostAndPortAreEqual(&url, &url)); + Check(URLProtocolHostAndPortAreEqual(&url, &urlNoFrag)); + Check(URLProtocolHostAndPortAreEqual(&url, &urlDiffQuery)); + Check(URLProtocolHostAndPortAreEqual(&url, &urlDiffPath)); + auto urlUpperSchemeStr = FromUTF8Z(U8("HTTPS://u%20:p%40@example.test:8443/a/b?q=1#frag")); + URL urlUpperScheme = url; + urlUpperScheme.impl = urlUpperSchemeStr.impl; + Check(URLProtocolHostAndPortAreEqual(&url, &urlUpperScheme)); + auto urlUpperHostStr = FromUTF8Z(U8("https://u%20:p%40@EXAMPLE.TEST:8443/a/b?q=1#frag")); + URL urlUpperHost = url; + urlUpperHost.impl = urlUpperHostStr.impl; + Check(URLProtocolHostAndPortAreEqual(&url, &urlUpperHost)); + auto urlHttpStr = FromUTF8Z(U8("http://u%20:p%40@example.test:8443/a/b?q=1#frag")); + URL urlHttp = url; + urlHttp.impl = urlHttpStr.impl; + urlHttp.bits = 1U | 2U | (5U << 3) | (4U << 6); + Check(!URLProtocolHostAndPortAreEqual(&url, &urlHttp)); + Check(!URLProtocolHostAndPortAreEqual(&url, &urlDiffHost)); + auto urlShortHostStr = FromUTF8Z(U8("https://u%20:p%40@ex.test:8443/a/b?q=1#frag")); + URL urlShortHost = url; + urlShortHost.impl = urlShortHostStr.impl; + urlShortHost.hostEnd = 25; + Check(!URLProtocolHostAndPortAreEqual(&url, &urlShortHost)); + auto urlDiffPortStr = FromUTF8Z(U8("https://u%20:p%40@example.test:9443/a/b?q=1#frag")); + URL urlDiffPort = url; + urlDiffPort.impl = urlDiffPortStr.impl; + Check(!URLProtocolHostAndPortAreEqual(&url, &urlDiffPort)); + auto urlPort80Str = FromUTF8Z(U8("http://example.test:80/path")); + URL urlPort80{urlPort80Str.impl, 1U | 2U | (3U << 3) | (4U << 6), 7, 7, 7, 19, 23, 27, 27}; + auto urlNoPortStr = FromUTF8Z(U8("http://example.test/path")); + URL urlNoPort{urlNoPortStr.impl, 1U | 2U | (0U << 3) | (4U << 6), 7, 7, 7, 19, 20, 24, 24}; + Check(!URLProtocolHostAndPortAreEqual(&urlPort80, &urlNoPort)); + auto urlNoPort2Str = FromUTF8Z(U8("http://example.test/other")); + URL urlNoPort2{urlNoPort2Str.impl, 1U | 2U | (0U << 3) | (4U << 6), 7, 7, 7, 19, 20, 25, 25}; + Check(URLProtocolHostAndPortAreEqual(&urlNoPort, &urlNoPort2)); + auto widePort = Create16(u"http://wide.test:8080/path", 26); + URL urlWidePort{widePort.impl, 1U | 2U | (5U << 3) | (4U << 6), 7, 7, 7, 16, 22, 26, 26}; + auto widePortMatch = Create16(u"HTTP://WIDE.TEST:8080/other", 27); + URL urlWideMatch{widePortMatch.impl, 1U | 2U | (5U << 3) | (4U << 6), 7, 7, 7, 16, 22, 27, 27}; + Check(URLProtocolHostAndPortAreEqual(&urlWidePort, &urlWideMatch)); + auto narrowPortStr = FromUTF8Z(U8("http://wide.test:8080/narrow")); + URL urlNarrowPort{narrowPortStr.impl, 1U | 2U | (5U << 3) | (4U << 6), 7, 7, 7, 16, 22, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlWidePort, &urlNarrowPort)); + auto urlPort0Str = FromUTF8Z(U8("http://example.test:0/path")); + URL urlPort0{urlPort0Str.impl, 1U | 2U | (2U << 3) | (4U << 6), 7, 7, 7, 19, 22, 26, 26}; + Check(URLProtocolHostAndPortAreEqual(&urlPort0, &urlPort0)); + Check(!URLProtocolHostAndPortAreEqual(&urlPort0, &urlNoPort)); + Check(!URLProtocolHostAndPortAreEqual(&urlPort0, &urlPort80)); + auto urlPortMaxStr = FromUTF8Z(U8("http://example.test:65535/path")); + URL urlPortMax{urlPortMaxStr.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 19, 26, 30, 30}; + Check(URLProtocolHostAndPortAreEqual(&urlPortMax, &urlPortMax)); + Check(!URLProtocolHostAndPortAreEqual(&urlPortMax, &urlPort80)); + auto urlEmptyPortStr = FromUTF8Z(U8("http://example.test:/path")); + URL urlEmptyPort{urlEmptyPortStr.impl, 1U | 2U | (1U << 3) | (4U << 6), 7, 7, 7, 19, 21, 25, 25}; + Check(!URLProtocolHostAndPortAreEqual(&urlEmptyPort, &urlPort80)); + Check(URLProtocolHostAndPortAreEqual(&urlEmptyPort, &urlNoPort)); + auto urlMalformedPortStr = FromUTF8Z(U8("http://example.test:8x/path")); + URL urlMalformedPort{urlMalformedPortStr.impl, 1U | 2U | (3U << 3) | (4U << 6), 7, 7, 7, 19, 23, 27, 27}; + Check(!URLProtocolHostAndPortAreEqual(&urlMalformedPort, &urlPort80)); + Check(URLProtocolHostAndPortAreEqual(&urlMalformedPort, &urlNoPort)); + auto urlOutOfRangePortStr = FromUTF8Z(U8("http://example.test:70000/path")); + URL urlOutOfRangePort{urlOutOfRangePortStr.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 19, 26, 30, 30}; + Check(!URLProtocolHostAndPortAreEqual(&urlOutOfRangePort, &urlPort80)); + Check(URLProtocolHostAndPortAreEqual(&urlOutOfRangePort, &urlNoPort)); + auto urlPlusPort80Str = FromUTF8Z(U8("http://example.test:+80/path")); + URL urlPlusPort80{urlPlusPort80Str.impl, 1U | 2U | (4U << 3) | (4U << 6), 7, 7, 7, 19, 24, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlPlusPort80, &urlPort80)); + Check(!URLProtocolHostAndPortAreEqual(&urlPlusPort80, &urlNoPort)); + auto urlSpacePort80Str = FromUTF8Z(U8("http://example.test: 80 /path")); + URL urlSpacePort80{urlSpacePort80Str.impl, 1U | 2U | (5U << 3) | (4U << 6), 7, 7, 7, 19, 25, 29, 29}; + Check(URLProtocolHostAndPortAreEqual(&urlSpacePort80, &urlPort80)); + Check(!URLProtocolHostAndPortAreEqual(&urlSpacePort80, &urlNoPort)); + auto urlTabPort80Str = FromUTF8Z(U8("http://example.test:\t80/path")); + URL urlTabPort80{urlTabPort80Str.impl, 1U | 2U | (4U << 3) | (4U << 6), 7, 7, 7, 19, 24, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlTabPort80, &urlPort80)); + auto urlNewlinePort80Str = FromUTF8Z(U8("http://example.test:\n80/path")); + URL urlNewlinePort80{urlNewlinePort80Str.impl, 1U | 2U | (4U << 3) | (4U << 6), 7, 7, 7, 19, 24, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlNewlinePort80, &urlPort80)); + auto urlReturnPort80Str = FromUTF8Z(U8("http://example.test:\r80/path")); + URL urlReturnPort80{urlReturnPort80Str.impl, 1U | 2U | (4U << 3) | (4U << 6), 7, 7, 7, 19, 24, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlReturnPort80, &urlPort80)); + auto urlFormFeedPort80Str = FromUTF8Z(U8("http://example.test:\f80/path")); + URL urlFormFeedPort80{urlFormFeedPort80Str.impl, 1U | 2U | (4U << 3) | (4U << 6), 7, 7, 7, 19, 24, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlFormFeedPort80, &urlPort80)); + auto widePlusPort = Create16(u"http://wide.test:+8080/path", 27); + URL urlWidePlusPort{widePlusPort.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 16, 23, 27, 27}; + Check(URLProtocolHostAndPortAreEqual(&urlWidePlusPort, &urlWidePort)); + auto wideSpacePort = Create16(u"http://wide.test: 8080 /path", 28); + URL urlWideSpacePort{wideSpacePort.impl, 1U | 2U | (7U << 3) | (4U << 6), 7, 7, 7, 16, 24, 28, 28}; + Check(URLProtocolHostAndPortAreEqual(&urlWideSpacePort, &urlWidePort)); + auto wideTabPort = Create16(u"http://wide.test:\t8080/path", 27); + URL urlWideTabPort{wideTabPort.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 16, 23, 27, 27}; + Check(URLProtocolHostAndPortAreEqual(&urlWideTabPort, &urlWidePort)); + auto wideNewlinePort = Create16(u"http://wide.test:\n8080/path", 27); + URL urlWideNewlinePort{wideNewlinePort.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 16, 23, 27, 27}; + Check(URLProtocolHostAndPortAreEqual(&urlWideNewlinePort, &urlWidePort)); + auto wideReturnPort = Create16(u"http://wide.test:\r8080/path", 27); + URL urlWideReturnPort{wideReturnPort.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 16, 23, 27, 27}; + Check(URLProtocolHostAndPortAreEqual(&urlWideReturnPort, &urlWidePort)); + auto wideFormFeedPort = Create16(u"http://wide.test:\f8080/path", 27); + URL urlWideFormFeedPort{wideFormFeedPort.impl, 1U | 2U | (6U << 3) | (4U << 6), 7, 7, 7, 16, 23, 27, 27}; + Check(URLProtocolHostAndPortAreEqual(&urlWideFormFeedPort, &urlWidePort)); + auto urlMinusPortStr = FromUTF8Z(U8("http://example.test:-80/path")); + URL urlMinusPort{urlMinusPortStr.impl, 1U | 2U | (4U << 3) | (4U << 6), 7, 7, 7, 19, 24, 28, 28}; + Check(!URLProtocolHostAndPortAreEqual(&urlMinusPort, &urlPort80)); + Check(URLProtocolHostAndPortAreEqual(&urlMinusPort, &urlNoPort)); + auto urlBarePlusPortStr = FromUTF8Z(U8("http://example.test:+/path")); + URL urlBarePlusPort{urlBarePlusPortStr.impl, 1U | 2U | (2U << 3) | (4U << 6), 7, 7, 7, 19, 22, 26, 26}; + Check(!URLProtocolHostAndPortAreEqual(&urlBarePlusPort, &urlPort80)); + Check(URLProtocolHostAndPortAreEqual(&urlBarePlusPort, &urlNoPort)); + auto urlPortOutOfBoundsStr = FromUTF8Z(U8("http://example.test:80")); + URL urlPortOutOfBounds{urlPortOutOfBoundsStr.impl, 1U | 2U | (5U << 3) | (4U << 6), 7, 7, 7, 19, 24, 22, 22}; + Check(URLProtocolHostAndPortAreEqual(&urlPortOutOfBounds, &urlNoPort)); + Check(!URLProtocolHostAndPortAreEqual(&urlPortOutOfBounds, &urlPort80)); + Check(URLProtocolHostAndPortAreEqual(&absent, &absent)); + Check(!URLProtocolHostAndPortAreEqual(&url, &absent)); + Check(!URLProtocolHostAndPortAreEqual(&absent, &url)); + Throws([&] { URLProtocolHostAndPortAreEqual(nullptr, &url); }); + Throws([&] { URLProtocolHostAndPortAreEqual(&url, nullptr); }); + } URLInvalidate(&url); Check(url.impl == string.impl && url.bits == 0 && url.userStart == 0 && url.userEnd == 0); Check(url.passwordEnd == 0 && url.hostEnd == 0 && url.pathEnd == 0 && url.pathAfterLastSlash == 0 && url.queryEnd == 0); diff --git a/docs/dev/debt/libSceNKWebKit.md b/docs/dev/debt/libSceNKWebKit.md index 6ab3e588c7..38c89b962d 100644 --- a/docs/dev/debt/libSceNKWebKit.md +++ b/docs/dev/debt/libSceNKWebKit.md @@ -43,7 +43,7 @@ Inventory prose retains historical counts; use the [generated progress report](h - The [revision-pinned identity inventory](https://gist.github.com/kyon-eth/d44995f181e1818977bd38f0e6fc0f34/8afcb14b0707a09b6274ff7f7bcf33ef46cfe2e7) records the module, library, symbol type and NIDs for the declarations in [Export.cpp](../../../core/libs/prx/libSceNKWebKit/Export.cpp); it is a metadata summary rather than independent verification. For the 255 absent from the public catalogue, [per-image ELF records](https://gist.github.com/kyon-eth/147be8dd556e9cced2577ccbed548972/1a659ecfe819e724675991a574a1aebb5fbb48e5) include the raw qualified names, module and library identities, symbol type, values and sizes; the record file SHA-256 is `44bdc4d6073192a8f41b7be359b75364b3d41dbbd11ce649924b8cebdd4ffe51`. Image labels are not authenticated. -- [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7366 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. +- [Additional callable exports](../../../core/libs/prx/libSceNKWebKit/Export.cpp) - 7365 of the 8703 inventory declarations listed below throw through `NotImplemented_nid_no_patch`; signatures and behavior are unverified. The later 13.60-labelled image contains all 8703; 8448 match the public 3.20 catalogue, while 255 are absent and their presence in retail 3.20 is unverified. Per-image records show the latter in a separate 3.20-labelled image, but neither image label is authenticated. Identified names match NIDs in the [public symbol database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) or [supplemental catalog](https://github.com/claimore22/ps5rs/blob/2304be5131221bae0dac24b9767cabffda33f747/data/nids.csv). Data exports need separate analysis. | Name | NID | | --- | --- | @@ -1789,7 +1789,6 @@ Inventory prose retains historical counts; use the [generated progress report](h | `_ZN3WTF25activateSignalHandlersForENS_6SignalE` | `Pyu7Zyt6XUE` | | `_ZN3WTF26getAndResetAccumulatedLogsEv` | `bD3nxh6Om44` | | `_ZN3WTF26normalizeLineEndingsToCRLFEONS_6VectorIhLm0ENS_15CrashOnOverflowELm16ENS_10FastMallocEEE` | `oUjgPUMQcao` | -| `_ZN3WTF27protocolHostAndPortAreEqualERKNS_3URLES2_` | `tDDzvLlQ7CE` | | `_ZN3WTF28encodeWithURLEscapeSequencesERKNS_6StringE` | `3DqiPW3mayI` | | `_ZN3WTF28normalizeLineEndingsToNativeEONS_6VectorIhLm0ENS_15CrashOnOverflowELm16ENS_10FastMallocEEE` | `KWwKlT9hDkc` | | `_ZN3WTF30numCodeUnitsInGraphemeClustersENS_10StringViewEj` | `3PZOiy2oZ9w` | From 878fd5a3ad5bf2ff836272009c6986cba067226f Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Fri, 6 Nov 2026 09:11:06 +1300 Subject: [PATCH 44/46] test(libSceContentSearch): do not pin the unmeasured index query to throw sceContentSearchGetMyApplicationIndex is a valid call whose result is not established yet, so the test should not assert that it throws. The not-initialized and null-output checks stay. Co-authored-by: edubox <3165165+edubox@users.noreply.github.com> --- core/libs/tests/GuestContentSearch.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/libs/tests/GuestContentSearch.cpp b/core/libs/tests/GuestContentSearch.cpp index 064029844e..e3cc01a47e 100644 --- a/core/libs/tests/GuestContentSearch.cpp +++ b/core/libs/tests/GuestContentSearch.cpp @@ -98,8 +98,6 @@ int main() { Require(count == -1 && updateId == -1); updateId = -1; Require(sceContentSearchGetApplicationLastUpdateId(&updateId) == 0 && updateId == 0); - applicationIndex = -1; - Require(ThrowsRuntimeError([&] { sceContentSearchGetMyApplicationIndex(&applicationIndex); }) && applicationIndex == 0); count = -1; updateId = -1; Require(sceContentSearchGetNumOfApplication(&columnSet, 1, &orderBy, 1, &count, &updateId) == 0); From 616b5275abeb715a93ea108835e9aeba060acb51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0?= Date: Thu, 5 Nov 2026 22:01:16 +0100 Subject: [PATCH 45/46] fix(main): raise web_release ctest timeout to 120s Culprit none: run 42095952530 (windows web_release) hit the 20 s limit at 20.84 s on a slow runner; 1 of 153 main builds. #14956 (libSceVrHand) does not touch it. Co-Authored-By: Claude Sonnet 5.5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25abb6896a..21e51188ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -466,7 +466,7 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_package_release.py) add_test(NAME web_release COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_web_release.py) - set_tests_properties(web_release PROPERTIES TIMEOUT 20) + set_tests_properties(web_release PROPERTIES TIMEOUT 120) add_test(NAME web_library_assets COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_build_web.py) set_tests_properties(web_library_assets PROPERTIES TIMEOUT 20) From e81d98dbb2b93ff67de4611e4242f875efc7de1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0?= Date: Thu, 5 Nov 2026 22:28:15 +0100 Subject: [PATCH 46/46] fix(main): ignore the host import budget note in the BDA span check The ordinary neighboring BDA span check of agc_driver_sample_multisample_target treated any stderr captured during the dispatch as a failure. The one-time '[gpu] host import budget' note (printed when the host heap reports less than the default budget) landed in that capture on a busy runner: run 42097866378 at 08e73174f2 (#11553 only touched docs). Only a 'BDA access failed:' report counts now. Co-Authored-By: Claude Sonnet 5.5 --- .../libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp index 87107f2054..de2e3a8c71 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp @@ -929,7 +929,7 @@ void CheckBdaSpan(AgcDriver::VulkanDevice& device, const Block& block, std::size failure = error.what(); } const auto deferred = log.Finish(); - if (failure.empty()) failure = deferred; + if (failure.empty() && deferred.find("BDA access failed:") != std::string::npos) failure = deferred; if (refused) { Require(failure.find("BDA access failed:") != std::string::npos, "BDA access to a resident native multisample range was not refused: " + failure); Require(std::equal(before.begin(), before.end(), block.data), "a refused BDA access changed guest memory");