From 8df40f6579f8e35e9c3bc5434e48a7b095c11460 Mon Sep 17 00:00:00 2001 From: nahshongraham97 Date: Fri, 9 Oct 2026 19:27:18 -0600 Subject: [PATCH 001/523] perf(libkernel): shard SyncOnAddress waiters across hashed buckets A single global waiter lock serializes unrelated sceKernelSyncOnAddressWait/Wake calls. Hash the address into 64 cache-line-aligned buckets so unrelated waiters no longer contend, without changing wake semantics. --- .../SyncOnAddress/src/SyncOnAddress.cpp | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp b/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp index f75b779338..563c67f585 100644 --- a/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp +++ b/core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -22,8 +23,19 @@ struct AddressWaiter { bool woken = false; }; -std::mutex g_waitersLock; -std::unordered_map> g_waiters; +constexpr std::size_t kBucketCount = 64; + +struct alignas(64) WaiterBucket { + std::mutex mutex; + std::unordered_map> waiters; +}; + +std::array g_buckets; + +WaiterBucket& BucketOf(std::uintptr_t address) { + const auto hashed = (address >> 2) * UINT64_C(0x9e3779b97f4a7c15); + return g_buckets[hashed >> 58]; +} template bool IsAlignedAddress(std::uintptr_t address) { @@ -33,13 +45,14 @@ bool IsAlignedAddress(std::uintptr_t address) { template int WaitOnAddress(TValue* address, TValue expected, const KernelUseconds* timeout, const void* caller) { const auto key = reinterpret_cast(address); - std::unique_lock lock(g_waitersLock); + auto& bucket = BucketOf(key); + std::unique_lock lock(bucket.mutex); if (std::atomic_ref(*address).load() != expected) { return SYNC_ON_ADDRESS_OK; } AddressWaiter waiter; - auto& queue = g_waiters[key]; + auto& queue = bucket.waiters[key]; const auto position = queue.insert(queue.end(), &waiter); const auto isWoken = [&] { return waiter.woken; }; const auto waitStart = std::chrono::steady_clock::now(); @@ -54,7 +67,7 @@ int WaitOnAddress(TValue* address, TValue expected, const KernelUseconds* timeou if (!woken) { queue.erase(position); if (queue.empty()) { - g_waiters.erase(key); + bucket.waiters.erase(key); } } lock.unlock(); @@ -112,9 +125,10 @@ int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count) { APS5_INVALID_ARG_EX; } - std::lock_guard lock(g_waitersLock); - const auto entry = g_waiters.find(key); - if (entry == g_waiters.end()) { + auto& bucket = BucketOf(key); + std::lock_guard lock(bucket.mutex); + const auto entry = bucket.waiters.find(key); + if (entry == bucket.waiters.end()) { return SYNC_ON_ADDRESS_OK; } auto& queue = entry->second; @@ -125,7 +139,7 @@ int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count) { waiter->condition.NotifyOne(); } if (queue.empty()) { - g_waiters.erase(entry); + bucket.waiters.erase(entry); } return SYNC_ON_ADDRESS_OK; } From 377301ef36c13fe4dc44f4c816b8aeffdfd765e3 Mon Sep 17 00:00:00 2001 From: notaxeltv Date: Fri, 16 Oct 2026 00:32:24 +0200 Subject: [PATCH 002/523] fix(recompiler): fill missing pixel interpolators as DEFAULT PPSA03541 prepares a pixel shader that still reads attribute 0 while SPI_PS_IN_CONTROL.NUM_INTERP is 0; extend the CNTL table with OFFSET 0x20 defaults instead of throwing. Co-authored-by: Cursor --- .../tests/InterpolationReferenceRange.cpp | 4 ++- .../Optimization/src/ShaderInfoCollector.cpp | 32 +++++++++++++++++++ docs/dev/debt/shader.md | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/core/libs/tests/InterpolationReferenceRange.cpp b/core/libs/tests/InterpolationReferenceRange.cpp index 7bf9c80ad3..594adbd1d2 100644 --- a/core/libs/tests/InterpolationReferenceRange.cpp +++ b/core/libs/tests/InterpolationReferenceRange.cpp @@ -42,7 +42,9 @@ int main() { } }; const auto attribute = Collect(5u, 1u, 0u, 2u); - expect(attribute.find("interpolation parameter reference is out of range (attribute 5 component 1 mode 0, 2 inputs)") != std::string::npos, "an attribute past the pixel inputs", attribute); + expect(attribute.find("interpolation parameter reference") == std::string::npos, "an attribute past the pixel inputs becomes a default", attribute); + const auto empty = Collect(0u, 0u, 0u, 0u); + expect(empty.find("interpolation parameter reference") == std::string::npos, "attribute 0 with no SPI inputs becomes a default", empty); const auto component = Collect(0u, 4u, 2u, 1u); expect(component.find("(attribute 0 component 4 mode 2, 1 inputs)") != std::string::npos, "a component past w", component); const auto mode = Collect(1u, 3u, 3u, 3u); diff --git a/core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp b/core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp index fac95cb5d4..d89e500cb6 100644 --- a/core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp +++ b/core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp @@ -73,6 +73,35 @@ void ValidateOptions(const IrProgram& program, ShaderStageInputInfo inputInfo) { } } +void EnsurePixelInterpolatorCoverage(const IrProgram& program, ShaderPixelInputInfo& pixel) { + std::uint32_t needed = pixel.inputNum; + const auto consider = [&](std::uint32_t attribute) { + if (attribute < 32u && attribute + 1u > needed) needed = attribute + 1u; + }; + for (const auto& block : program.Blocks()) { + for (const IrValue* inst : block->Instructions()) { + switch (inst->Opcode()) { + case IrOpcode::GetAttribute: + case IrOpcode::GetInterpolationParameter: + case IrOpcode::GetInterpolationParameterF16: + case IrOpcode::InterpolateHostP1: + case IrOpcode::InterpolateHostP2: { + const IrValue* attribute = inst->Argument(0)->Resolve(); + if (attribute->HasImmediate() && attribute->Type() == IrType::U32) consider(attribute->ImmediateU32()); + break; + } + default: + break; + } + } + } + if (needed <= pixel.inputNum) return; + for (std::uint32_t input = pixel.inputNum; input < needed; ++input) { + pixel.interpolatorSettings[input] = 0x20u; + } + pixel.inputNum = needed; +} + void ValidateValueReferences(const IrProgram& program, ShaderStageInputInfo inputInfo) { for (const auto& block : program.Blocks()) { for (const IrValue* inst : block->Instructions()) { @@ -525,6 +554,9 @@ void ShaderInfoCollector::Collect(IrProgram& program, const ShaderStageInputInfo if (!program.Resources().resourceTrackingComplete || program.Metadata().shaderInfoComplete) { return Fail(!program.Resources().resourceTrackingComplete ? "shader resources were not tracked" : "shader info already collected"); } + if (program.Resources().stage == IrShaderStage::Pixel && inputInfo.pixel != nullptr) { + EnsurePixelInterpolatorCoverage(program, const_cast(*inputInfo.pixel)); + } ValidateOptions(program, inputInfo); ValidateValueReferences(program, inputInfo); auto next = program.Info(); diff --git a/docs/dev/debt/shader.md b/docs/dev/debt/shader.md index dea88ed54f..0e37433597 100644 --- a/docs/dev/debt/shader.md +++ b/docs/dev/debt/shader.md @@ -22,6 +22,7 @@ ### Functional +- [Pixel interpolators past `SPI_PS_IN_CONTROL.NUM_INTERP`](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp) (shader recompiler) - when the IR reads an attribute at or past the guest interpolator count, the missing `SPI_PS_INPUT_CNTL` slots are filled as DEFAULT (`OFFSET` 0x20, constant 0/1) before info collection, matching Mesa's treatment of a PS input with no VS output. Component and mode bounds still throw. PPSA03541 (UFC 5) prepares pixel shader `0x114073b400` with `NUM_INTERP` 0 while it still issues `v_interp` on attribute 0. Console behaviour for that pairing is not measured. - [Unlinked calls](../../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) - the `s_swappc_b64` a table call's compare chain falls back to, when its target matches no linked copy, records the `UnlinkedCall` fault with the target and the program counter and stops the invocation, or continues after the call in programs whose faults do not stop invocations (see `bdaStopsInvocations`). The driver lists those calls in the request (`linkedCallFallbacks`), and it reports the fault like a BDA fault, naming the target: a dispatch that checks its faults when it completes throws, one whose check runs in a deferred write-back logs it and continues. The hardware would jump to the target, which is not modelled. Any other `s_swappc_b64` whose target is not resolved still throws when the shader is compiled. - [Cached source IR](../../../core/shader/recompiler/Recompiler.cpp) releases lazily allocated SSA construction tables after resource preparation and reclaims values unreachable from instructions, metadata and resource plans when retaining a pending source program. Immediate shader preparation skips reclamation because compilation consumes the program. Reclamation must account for every new IR pointer owner; a remaining external use throws. Live IDs are retained. Source and variant entries remain cached without an eviction bound. A pending source program is released after its first variant is obtained; memory beyond the measured title checkpoint and Linux performance remain unmeasured. - [Vertex user-data tail jumps](../../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) link a linear raw vertex prolog's terminal `s_setpc_b64` through an unchanged user-data pair to its continuation. The instruction semantics follow AMD RDNA2 ISA section 12.3; the prolog convention follows Mesa `aco_select_vs_prolog.cpp` at `0b2d6d1a61d9bbf89cf8beb88a696144f67c61f8`. Console hardware has not been measured. Branching prologs, computed low pointers and continuations reading `s_getpc_b64` remain unsupported From c32c26edb2beb9069dde096e5e3ff86cab3635f6 Mon Sep 17 00:00:00 2001 From: notaxeltv Date: Fri, 16 Oct 2026 05:52:28 +0200 Subject: [PATCH 003/523] fix(recompiler): emit DEFAULT for missing interpolation bindings After collector coverage, emit still rebuilt pixel input info from SPI and threw undefined attribute for the same UFC5 shader; return OFFSET 0x20 defaults from SPIR-V emission too. Co-authored-by: Cursor --- .../recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp | 9 ++++++--- docs/dev/debt/shader.md | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp index 2c92bf42a0..22e6c006ac 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp @@ -183,12 +183,15 @@ std::uint32_t EmitAttributeValue(SpirvEmitterState& state, std::uint32_t attr, s } std::uint32_t EmitInterpolationParameterValue(SpirvEmitterState& state, std::uint32_t attr, std::uint32_t chan, std::uint32_t mode) { - if (PixelParameterIsDefault(state, attr)) { - return mode == 2u ? EmitAttributeValue(state, attr, chan) : ConstantU32(state, 0u); + if (state.inputInfo.pixel == nullptr) { + throw std::runtime_error("interpolation parameter refers to an undefined attribute"); + } + if (attr >= state.inputInfo.pixel->inputNum || PixelParameterIsDefault(state, attr)) { + return mode == 2u ? ConstantU32(state, state.inputInfo.pixel->InputDefaultBits(attr, chan & 3u)) : ConstantU32(state, 0u); } const auto* input = SpirvInputBindingForParameter(state, attr); if (input == nullptr) { - throw std::runtime_error("interpolation parameter refers to an undefined attribute"); + return mode == 2u ? ConstantU32(state, state.inputInfo.pixel->InputDefaultBits(attr, chan & 3u)) : ConstantU32(state, 0u); } if (!input->perVertex) { return EmitAttributeValue(state, attr, chan); diff --git a/docs/dev/debt/shader.md b/docs/dev/debt/shader.md index 0e37433597..6566fda668 100644 --- a/docs/dev/debt/shader.md +++ b/docs/dev/debt/shader.md @@ -22,7 +22,7 @@ ### Functional -- [Pixel interpolators past `SPI_PS_IN_CONTROL.NUM_INTERP`](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp) (shader recompiler) - when the IR reads an attribute at or past the guest interpolator count, the missing `SPI_PS_INPUT_CNTL` slots are filled as DEFAULT (`OFFSET` 0x20, constant 0/1) before info collection, matching Mesa's treatment of a PS input with no VS output. Component and mode bounds still throw. PPSA03541 (UFC 5) prepares pixel shader `0x114073b400` with `NUM_INTERP` 0 while it still issues `v_interp` on attribute 0. Console behaviour for that pairing is not measured. +- [Pixel interpolators past `SPI_PS_IN_CONTROL.NUM_INTERP`](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp) (shader recompiler) - when the IR reads an attribute at or past the guest interpolator count, the missing `SPI_PS_INPUT_CNTL` slots are filled as DEFAULT (`OFFSET` 0x20, constant 0/1) before info collection, and SPIR-V emission of `GetInterpolationParameter` for a missing binding returns the same defaults instead of throwing, matching Mesa's treatment of a PS input with no VS output. Component and mode bounds still throw. PPSA03541 (UFC 5) prepares pixel shader `0x114073b400` with `NUM_INTERP` 0 while it still issues `v_interp` on attribute 0 (`undefined attribute` after the collector fill alone). Console behaviour for that pairing is not measured. - [Unlinked calls](../../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) - the `s_swappc_b64` a table call's compare chain falls back to, when its target matches no linked copy, records the `UnlinkedCall` fault with the target and the program counter and stops the invocation, or continues after the call in programs whose faults do not stop invocations (see `bdaStopsInvocations`). The driver lists those calls in the request (`linkedCallFallbacks`), and it reports the fault like a BDA fault, naming the target: a dispatch that checks its faults when it completes throws, one whose check runs in a deferred write-back logs it and continues. The hardware would jump to the target, which is not modelled. Any other `s_swappc_b64` whose target is not resolved still throws when the shader is compiled. - [Cached source IR](../../../core/shader/recompiler/Recompiler.cpp) releases lazily allocated SSA construction tables after resource preparation and reclaims values unreachable from instructions, metadata and resource plans when retaining a pending source program. Immediate shader preparation skips reclamation because compilation consumes the program. Reclamation must account for every new IR pointer owner; a remaining external use throws. Live IDs are retained. Source and variant entries remain cached without an eviction bound. A pending source program is released after its first variant is obtained; memory beyond the measured title checkpoint and Linux performance remain unmeasured. - [Vertex user-data tail jumps](../../../core/shader/recompiler/ControlFlow/src/GraphBuilder.cpp) link a linear raw vertex prolog's terminal `s_setpc_b64` through an unchanged user-data pair to its continuation. The instruction semantics follow AMD RDNA2 ISA section 12.3; the prolog convention follows Mesa `aco_select_vs_prolog.cpp` at `0b2d6d1a61d9bbf89cf8beb88a696144f67c61f8`. Console hardware has not been measured. Branching prologs, computed low pointers and continuations reading `s_getpc_b64` remain unsupported From 4c475452d472643c7de250a855c07339d5dabf65 Mon Sep 17 00:00:00 2001 From: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com> Date: Sun, 18 Oct 2026 10:41:36 -0700 Subject: [PATCH 004/523] fix(agc): destroy settle aliases before an unmap releases their imports On Windows a host import under 1 MiB is settled by a batched 4-byte read. When the settle queue is in another queue family than the import's, as on AMD's Windows driver with its single graphics queue, the batch reads through an alias buffer bound to the import's memory. Reaping the batch destroys those aliases before it drops the imports retired while the batch held them. An unmap that needs such an import (releaseSettleRetired) waited for the batch and then released the import, destroying its buffer and freeing its memory while the alias was still bound to that memory; the alias went at the next reap. Destroy the batch's aliases first, as reapSettleBatches and drainSettles do. --- .../libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index 3d233e6868..abe0f6738d 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -386,6 +386,8 @@ bool releaseSettleRetired(HostImports& state, std::uint64_t begin, std::uint64_t ++state.settleUnmapWaits; waitSettleBatch(settle, device, batch); } + for (const auto alias : batch.aliases) settle.settleDestroyBuffer(device, alias, nullptr); + batch.aliases.clear(); for (auto& holder : batch.retired) released.push_back(std::move(holder)); batch.retired.clear(); } From e22b41c56c3e84a5a0ed38829a7bec126eaf906f Mon Sep 17 00:00:00 2001 From: LeandroLP23 <92315992+LeandroLP23@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:26:54 +0000 Subject: [PATCH 005/523] fix(agc): keep a retired import with its settle batch until it is reaped forgetSettle handed back an import's settle batch only while the batch was still running. A batch the GPU had finished but that had not been reaped yet still held its aliases, so retireImport released the import at once, freeing its memory before the alias bound to it was destroyed. Hand back any busy batch. The import then stays with its batch until the batch is reaped or an unmap releases it, and both destroy the aliases first. A finished batch now keeps its imports' Vulkan objects until it is reaped (#7601). --- .../libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index abe0f6738d..a314f42abf 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -259,7 +259,7 @@ SettleBatch* forgetSettle(HostImports& state, std::uint64_t base) { SettleBatch* running = nullptr; if (const auto settle = state.settles.find(found->second.device); found->second.phase == SettlePhase::Submitted && settle != state.settles.end()) { auto& batch = settle->second.batches[found->second.batch]; - if (settleBatchRunning(settle->second, settle->first, batch)) running = &batch; + if (batch.busy) running = &batch; } state.pendingSettles.erase(found); return running; From 8a1e7371823221ea1e61d127892749e099f0f440 Mon Sep 17 00:00:00 2001 From: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com> Date: Sun, 18 Oct 2026 10:41:37 -0700 Subject: [PATCH 006/523] test(agc): run the host import lifetime tests on another settle family agc_driver_host_import_lifetime takes the settle alias path only on a device whose graphics family has a single queue, such as AMD's Windows driver; elsewhere the settles share the graphics family and make no alias. agc_driver_host_import_lifetime_other_family runs the same tests with the settles forced onto another queue family, as agc_import_settle_deferred_other_family does, so a device with spare graphics queues covers that path too. It is registered on Windows and skips where the device has no other queue family. --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 7 +++++++ core/libs/prx/libSceAgcDriver/tests/Recorder.cpp | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 5480263227..cb9ea09f7a 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -1341,6 +1341,13 @@ if(BUILD_TESTING) endif() set_property(TEST agc_driver_host_import_lifetime APPEND PROPERTY ENVIRONMENT_MODIFICATION "APS5_RELEASE_UNDER_LOCK=unset:" "APS5_RELEASE_ON_UNLOCK=unset:") + if(WIN32) + add_test(NAME agc_driver_host_import_lifetime_other_family COMMAND agc_driver_recorder_tests --host-import-lifetime-other-family-only) + set_tests_properties(agc_driver_host_import_lifetime_other_family PROPERTIES + ENVIRONMENT "APS5_PIN_WAIT_MS=2000" SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + set_property(TEST agc_driver_host_import_lifetime_other_family APPEND PROPERTY ENVIRONMENT_MODIFICATION + "APS5_RELEASE_UNDER_LOCK=unset:" "APS5_RELEASE_ON_UNLOCK=unset:") + endif() endif() add_test_executable(agc_recorder_kept_bytes_tests EXCLUDE_FROM_ALL tests/RecorderKeptBytes.cpp) diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 2e9cd49f46..d4b369cdc7 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -8763,7 +8763,7 @@ int main(int argc, char** argv) { } std::unique_ptr created; try { - created = std::make_unique(argc == 2 && (std::string_view(argv[1]) == "--import-watch-other-family-only" || std::string_view(argv[1]) == "--deferred-settle-other-family-only")); + created = std::make_unique(argc == 2 && (std::string_view(argv[1]) == "--import-watch-other-family-only" || std::string_view(argv[1]) == "--deferred-settle-other-family-only" || std::string_view(argv[1]) == "--host-import-lifetime-other-family-only")); } catch (const std::exception& error) { if (std::getenv("ANYPS5_REQUIRE_VULKAN") != nullptr) throw; std::cout << "skipped, no usable Vulkan device: " << error.what() << '\n'; @@ -8785,6 +8785,13 @@ int main(int argc, char** argv) { return 0; } #ifdef _WIN32 + if (argc == 2 && std::string_view(argv[1]) == "--host-import-lifetime-other-family-only") { + if (device.GetContext().settleFamily == device.GetContext().queueFamily) { + std::cout << "skipped, no other queue family to settle imports on\n"; + return 77; + } + return RunHostImportLifetimeTests(device.GetContext()); + } if (argc == 2 && (std::string_view(argv[1]) == "--deferred-settle-only" || std::string_view(argv[1]) == "--deferred-settle-other-family-only")) { if (std::string_view(argv[1]) == "--deferred-settle-other-family-only" && device.GetContext().settleFamily == device.GetContext().queueFamily) { std::cout << "skipped, no other queue family to settle imports on\n"; From c9fe9292d84e59eaa5e7bdd87c3d12a83aebb167 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Mon, 19 Oct 2026 02:45:52 +0000 Subject: [PATCH 007/523] fix(agc): draw a stencil replacement against a known stencil value as an inversion DB_STENCILREFMASK holds a test value and a separate operation value, and REPLACE_OP, ONES and REPLACE_TEST write a value that can differ from the test value on bits the compare reads. Vulkan uses one reference for both, so such a face threw. Ghost of Tsushima (PPSA02225) stops on one at its splash screen (test value 0xff, operation value 0x40, both masks 0xff). Some compares fix the stencil value wherever an operation runs: EQUAL on pass and depth fail, NOTEQUAL on fail, and LEQUAL, GEQUAL, LESS or GREATER against the largest or smallest masked value. There, if every written bit is compared, the written bits hold the test value, so the replacement is an INVERT whose write mask holds only the bits that differ from the value written. The operations of a face share its write mask, so a face whose other operations can't be drawn the same way still throws. Co-Authored-By: Claude Opus 5.5 --- .../libSceAgcDriver/Graphics/src/State.cpp | 25 ++- .../prx/libSceAgcDriver/tests/Graphics.cpp | 34 ++++ .../tests/execution/StencilKnownReplace.cpp | 191 ++++++++++++++++++ 3 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/StencilKnownReplace.cpp diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 36293b21a8..11588edf0e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -232,7 +232,9 @@ VkStencilOpState stencilFace(std::uint32_t compare, std::uint32_t ops, std::uint const auto opValue = refMask >> 24u; auto reference = test; auto fixed = compare == 0 || compare == 7 ? 0u : face.compareMask; - const auto convert = [&](std::uint32_t op) { + std::uint32_t invertMask = 0; + std::uint32_t inverted = 0; + const auto convert = [&](std::uint32_t op, bool testFailed) { if (face.writeMask == 0) return VK_STENCIL_OP_KEEP; char detail[96]; switch (op) { @@ -247,6 +249,16 @@ VkStencilOpState stencilFace(std::uint32_t compare, std::uint32_t ops, std::uint } const auto value = op == 2 ? 0xffu : op == 3 ? test : opValue; if (((reference ^ value) & fixed & face.writeMask) != 0) { + const auto compared = test & face.compareMask; + const bool passKnown = compare == 2 || (compare == 3 && compared == face.compareMask) || (compare == 6 && compared == 0u); + const bool failKnown = compare == 5 || (compare == 1 && compared == 0u) || (compare == 4 && compared == face.compareMask); + const bool testValueKnown = testFailed ? failKnown : passKnown; + const auto flipped = (test ^ value) & face.writeMask; + if (testValueKnown && (face.writeMask & ~face.compareMask) == 0 && (invertMask == 0 || invertMask == flipped)) { + invertMask = flipped; + ++inverted; + return VK_STENCIL_OP_INVERT; + } std::snprintf(detail, sizeof(detail), "AGC graphics: stencil replacement 0x%02x against test value 0x%02x (masks 0x%02x/0x%02x) is unsupported", value, test, face.compareMask, face.writeMask); throw std::runtime_error(detail); } @@ -269,9 +281,14 @@ VkStencilOpState stencilFace(std::uint32_t compare, std::uint32_t ops, std::uint throw std::runtime_error(detail); } }; - face.failOp = convert(ops & 0xfu); - face.passOp = convert((ops >> 4u) & 0xfu); - face.depthFailOp = convert((ops >> 8u) & 0xfu); + face.failOp = convert(ops & 0xfu, true); + face.passOp = convert((ops >> 4u) & 0xfu, false); + face.depthFailOp = convert((ops >> 8u) & 0xfu, false); + if (invertMask != 0) { + const auto active = ((ops & 0xfu) != 0u) + (((ops >> 4u) & 0xfu) != 0u) + (((ops >> 8u) & 0xfu) != 0u); + if (inverted != active) throw std::runtime_error("AGC graphics: a stencil replacement drawn as an inversion beside another stencil operation is unsupported"); + face.writeMask = invertMask; + } face.reference = reference; return face; } diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index c4d4e56402..cebf2c6444 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2180,7 +2180,41 @@ void DepthStencilTests() { queue.context[0x10b] = 0x40; queue.context[0x10c] = 0x05ffff02; queue.context[0x200] = 0x00200211; + state = AgcDriver::Graphics::DecodeState(queue); + Require(state.stencilFront.passOp == VK_STENCIL_OP_INVERT && state.stencilFront.failOp == VK_STENCIL_OP_KEEP && state.stencilFront.depthFailOp == VK_STENCIL_OP_KEEP && state.stencilFront.compareOp == VK_COMPARE_OP_EQUAL, "an equal test replaced by another value is not drawn as an inversion"); + Require(state.stencilFront.reference == 2 && state.stencilFront.compareMask == 0xff && state.stencilFront.writeMask == 7, "an inverted replacement does not keep the test value and write only the bits that differ from it"); + queue.context[0x10b] = 0x440; + state = AgcDriver::Graphics::DecodeState(queue); + Require(state.stencilFront.passOp == VK_STENCIL_OP_INVERT && state.stencilFront.depthFailOp == VK_STENCIL_OP_INVERT && state.stencilFront.writeMask == 7, "a replacement on pass and depth fail is not drawn as one inversion"); + queue.context[0x10b] = 0x4; + queue.context[0x200] = 0x00200511; + state = AgcDriver::Graphics::DecodeState(queue); + Require(state.stencilFront.failOp == VK_STENCIL_OP_INVERT && state.stencilFront.passOp == VK_STENCIL_OP_KEEP && state.stencilFront.writeMask == 7, "a not-equal test that fails is not replaced as an inversion"); + queue.context[0x10c] = 0x40ffffff; + queue.context[0x10b] = 0x40; + queue.context[0x200] = 0x00200311; + state = AgcDriver::Graphics::DecodeState(queue); + Require(state.stencilFront.passOp == VK_STENCIL_OP_INVERT && state.stencilFront.writeMask == 0xbf && state.stencilFront.reference == 0xff, "a less-or-equal test against 0xff, which passes only on 0xff, is not replaced as an inversion"); + queue.context[0x10b] = 0x4; + queue.context[0x200] = 0x00200411; + state = AgcDriver::Graphics::DecodeState(queue); + Require(state.stencilFront.failOp == VK_STENCIL_OP_INVERT && state.stencilFront.writeMask == 0xbf, "a greater test against 0xff, which fails only on 0xff, is not replaced as an inversion"); + queue.context[0x10c] = 0x40ffff7f; + queue.context[0x10b] = 0x40; + queue.context[0x200] = 0x00200311; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "stencil replacement"); + queue.context[0x10c] = 0x05ffff02; + queue.context[0x200] = 0x00200511; + queue.context[0x10b] = 0x40; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "stencil replacement"); + queue.context[0x200] = 0x00200111; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "stencil replacement"); + queue.context[0x200] = 0x00200211; + queue.context[0x10c] = 0x05ff0f02; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "stencil replacement"); + queue.context[0x10c] = 0x05ffff02; + queue.context[0x10b] = 0x41; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "beside another stencil operation"); queue.context[0x10b] = 0x50; queue.context[0x200] = 0x00700711; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "stencil add/subtract"); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/StencilKnownReplace.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/StencilKnownReplace.cpp new file mode 100644 index 0000000000..284eb2e7d6 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/StencilKnownReplace.cpp @@ -0,0 +1,191 @@ +#include "prx/libSceAgcDriver/Execution/include/Driver.hpp" +#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp" +#include "prx/libSceAgcDriver/Submit/include/Dcb.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; + +constexpr std::byte Kept{0x40}; + +alignas(256) constexpr std::array VertexCode{ + 0x7e000d05u, 0x080200f4u, 0x10020300u, 0x100202f4u, 0x060202f3u, 0x060400f3u, 0x10040500u, 0x060404f3u, + 0x7e060280u, 0x7e0802f2u, 0xf80008cfu, 0x04030201u, 0xbf810000u, 0xbf810000u, 0xbf810000u, 0xbf810000u, +}; +alignas(256) constexpr std::array WhitePixelCode{0x7e0e02f2u, 0xf800180fu, 0x07070707u, 0xbf810000u}; +alignas(256) std::array ColorMemory{}; +alignas(256) std::array DepthMemory{}; +alignas(256) std::array StencilMemory{}; + +struct Header { + Shader shader{}; + ShaderUserData userData{}; + std::array registers{}; +}; + +alignas(8) Header VertexHeader{}; +alignas(8) Header PixelHeader{}; + +void Registered(Header& header, const std::uint32_t* code, std::size_t bytes, std::uint8_t type) { + header.shader.file_header = 0x34333231u; + header.shader.version = 0x18u; + header.shader.header_size = sizeof(Header); + header.shader.shader_size = static_cast(bytes); + header.shader.code = code; + header.shader.type = type; + header.shader.user_data = &header.userData; + if (type == 1) { + const auto address = reinterpret_cast(code); + header.registers = {{{0x008, static_cast(address >> 8u)}, {0x009, static_cast(address >> 40u)}, {0x00b, 1u << 1u}, {0x00c, std::bit_cast(1.0f)}}}; + header.shader.sh_registers = header.registers.data(); + header.shader.num_sh_registers = header.registers.size(); + } + AgcDriverRegisterShader_nid_postfix(&header.shader); +} + +void Append(std::vector& commands, std::uint32_t opcode, std::initializer_list payload) { + commands.push_back(0xc0000000u | (static_cast(payload.size() - 1) << 16u) | (opcode << 8u)); + commands.insert(commands.end(), payload); +} + +struct StencilState { + std::uint32_t depthControl; + std::uint32_t operations; + std::uint32_t refMask; +}; + +std::vector Draw(const StencilState& stencilState) { + const auto vertex = reinterpret_cast(VertexCode.data()); + const auto pixel = reinterpret_cast(WhitePixelCode.data()); + const auto color = reinterpret_cast(ColorMemory.data()); + const auto depth = reinterpret_cast(DepthMemory.data()); + const auto stencil = reinterpret_cast(StencilMemory.data()); + const std::vector> context{ + {0x2d5, 0x2000}, + {0x1b3, 0}, {0x1b4, 0}, {0x1b6, 0}, {0x207, 0}, {0x200, stencilState.depthControl}, {0x203, 0x800}, + {0x2dc, 0xaa00}, {0x2f8, 0}, {0x293, 0}, + {0x80, 0}, {0x8d, 0}, {0x83, 0xffff}, {0x8c, 0xa}, + {0x2f9, 0x2d}, {0x313, 0x6000}, {0x30e, 0xffffffff}, {0x30f, 0xffffffff}, + {0x206, 0x43f}, {0x204, 0x80000}, {0x205, 0x240}, + {0x8e, 0xf}, {0x8f, 0xf}, {0x202, 0xcc0010}, + {0x1c4, 9}, {0x1c5, 9}, {0x1c3, 4}, {0x31c, 0x28028}, + {0x31b, 0}, {0x31d, 0}, {0x3b0, (63u << 14u) | 3u}, + {0x3b8, 0x9000000}, {0x1e0, 0}, + {0xc, 0}, {0xd, 0x40040}, + {0x81, 0x80000000}, {0x82, 0x40040}, + {0x90, 0x80000000}, {0x91, 0x40040}, + {0x94, 0x80000000}, {0x95, 0x40040}, + {0x10f, std::bit_cast(32.0f)}, {0x110, std::bit_cast(32.0f)}, + {0x111, std::bit_cast(-2.0f)}, {0x112, std::bit_cast(2.0f)}, + {0x113, std::bit_cast(1.0f)}, {0x114, 0}, + {0xb4, 0}, {0xb5, std::bit_cast(1.0f)}, + {0x2fe, 0}, {0x302, 0}, {0x306, 0}, {0x30a, 0}, + {0x318, static_cast(color >> 8u)}, {0x390, static_cast(color >> 40u)}, {0x292, 2}, + {0x000, 0}, {0x002, 0}, {0x007, (3u << 16u) | 63u}, + {0x008, 0}, {0x009, std::bit_cast(1.0f)}, {0x00a, 0}, {0x00b, 0}, + {0x010, 3}, {0x011, 1}, + {0x012, static_cast(depth >> 8u)}, {0x014, static_cast(depth >> 8u)}, + {0x013, static_cast(stencil >> 8u)}, {0x015, static_cast(stencil >> 8u)}, + {0x01a, static_cast(depth >> 40u)}, {0x01c, static_cast(depth >> 40u)}, + {0x01b, static_cast(stencil >> 40u)}, {0x01d, static_cast(stencil >> 40u)}, + {0x10b, stencilState.operations}, {0x10c, stencilState.refMask}, {0x10d, stencilState.refMask}, + }; + const std::vector> shader{ + {0xc8, static_cast(vertex >> 8u)}, {0xc9, static_cast(vertex >> 40u)}, {0x8b, 1u << 1u}, {0x8c, 0}, + {0x008, static_cast(pixel >> 8u)}, {0x009, static_cast(pixel >> 40u)}, {0x00b, 1u << 1u}, {0x00c, std::bit_cast(1.0f)}, + }; + std::vector commands; + Append(commands, 0x79, {0x242, 4}); + for (const auto& [offset, value] : context) Append(commands, 0x69, {offset, value}); + for (const auto& [offset, value] : shader) Append(commands, 0x76, {offset, value}); + Append(commands, 0x2d, {3, 2}); + return commands; +} + +void Submit(const StencilState& stencilState) { + ColorMemory.fill(Kept); + auto commands = Draw(stencilState); + Packet packet{commands.data(), static_cast(commands.size()), 0, {}}; + Require(sceAgcDriverSubmitDcb(&packet) == 0, "the stencil draw submission failed"); + AgcDriverWaitIdle_nid_postfix(); +} + +constexpr std::uint32_t StencilEnable = 1u; +constexpr std::uint32_t Equal = 2u << 8u; +constexpr std::uint32_t LessEqual = 3u << 8u; +constexpr std::uint32_t Greater = 4u << 8u; +constexpr std::uint32_t NotEqual = 5u << 8u; +constexpr std::uint32_t Always = 7u << 8u; + +std::uint32_t RefMask(std::uint32_t test, std::uint32_t compareMask, std::uint32_t writeMask, std::uint32_t opValue) { + return test | (compareMask << 8u) | (writeMask << 16u) | (opValue << 24u); +} + +std::array Fill(std::uint32_t value) { + Submit({StencilEnable | Always, 0x30u, RefMask(value, 0xff, 0xff, 0)}); + const auto coverage = ColorMemory; + Require(std::count(coverage.begin(), coverage.end(), Kept) != static_cast(coverage.size()), "the stencil fill covered no pixels"); + return coverage; +} + +void Expect(const std::array& coverage, std::uint32_t value, const std::string& what) { + Submit({StencilEnable | Equal, 0u, RefMask(value, 0xff, 0, 0)}); + std::size_t drawn = 0; + for (std::size_t pixel = 0; pixel < coverage.size(); pixel += 4u) { + const bool covered = coverage[pixel] != Kept; + drawn += covered; + for (std::size_t channel = 0; channel < 4u; ++channel) Require(ColorMemory[pixel + channel] == (covered ? std::byte{255} : Kept), what + ": the stencil is not " + std::to_string(value) + " exactly where the draw covered"); + } + Require(drawn != 0, what + ": the probe covered no pixels"); +} + +void Case(const std::string& what, std::uint32_t initial, const StencilState& stencilState, std::uint32_t expected, bool testPasses = true) { + const auto coverage = Fill(initial); + Submit(stencilState); + if (testPasses) Require(ColorMemory == coverage, what + ": the draw under test did not color exactly the filled pixels"); + else Require(std::all_of(ColorMemory.begin(), ColorMemory.end(), [](auto value) { return value == Kept; }), what + ": a draw whose stencil test fails wrote color"); + Expect(coverage, expected, what); +} + +} + +int main() { + try { + { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + } + Registered(VertexHeader, VertexCode.data(), sizeof(VertexCode), 2); + Registered(PixelHeader, WhitePixelCode.data(), sizeof(WhitePixelCode), 1); + const std::array pixelContext{{{0x203, 0x800}, {0x1c4, 9}, {0x1b6, 0}}}; + AgcDriverResolveShaderAbi_nid_postfix(&PixelHeader.shader, pixelContext, {}); + Case("an equal test replaced by the operation value", 0xff, {StencilEnable | Equal, 0x40u, RefMask(0xff, 0xff, 0xff, 0x40)}, 0x40); + Case("an equal test replaced by the operation value on pass and depth fail", 0xff, {StencilEnable | Equal, 0x440u, RefMask(0xff, 0xff, 0xff, 0x40)}, 0x40); + Case("an equal test replaced by 0xff", 0x25, {StencilEnable | Equal, 0x20u, RefMask(0x25, 0xff, 0xff, 0)}, 0xff); + Case("an equal test replaced through a partial write mask", 0xff, {StencilEnable | Equal, 0x40u, RefMask(0xff, 0xff, 0xf0, 0x40)}, 0x4f); + Case("a failed not-equal test replaced by the operation value", 0xff, {StencilEnable | NotEqual, 0x4u, RefMask(0xff, 0xff, 0xff, 0x40)}, 0x40, false); + Case("a less-or-equal test against 0xff replaced by the operation value", 0xff, {StencilEnable | LessEqual, 0x40u, RefMask(0xff, 0xff, 0xff, 0x40)}, 0x40); + Case("a failed greater test against 0xff replaced by the operation value", 0xff, {StencilEnable | Greater, 0x4u, RefMask(0xff, 0xff, 0xff, 0x40)}, 0x40, false); + AgcDriverShutdown_nid_postfix(); + std::puts("stencil replacements with a known stencil value passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + try { AgcDriverShutdown_nid_postfix(); } catch (...) {} + return 1; + } +} From c2764f31f297c9b5c6d1bc3409635d7d65f9592a Mon Sep 17 00:00:00 2001 From: milescoviello Date: Mon, 19 Oct 2026 03:14:21 +0000 Subject: [PATCH 008/523] fix(agc): draw a PS_ITER_SAMPLE program once per pixel into a one-sample depth target A one-sample depth target under a multisampled rasterizer without MSAA_ENABLE is drawn as a one-sample draw at the pixel center, but a pixel program that runs with PS_ITER_SAMPLE threw. Ghost of Tsushima (PPSA02225) draws its depth-only pass this way (2x PA_SC_AA_CONFIG, PA_SC_MODE_CNTL_1 0x06030000, KILL_ENABLE, no sample index or coverage input) and stops on it. Every sample is taken to be at the pixel center on this path, so invocations per sample would get the same inputs and give the same depth, kill and color results, and the target keeps sample 0's. Such a program is now drawn once per pixel, without sample shading. ANCILLARY and SAMPLE_COVERAGE inputs still throw. The debt entry notes that only the memory writes of such a program can differ. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp | 2 +- core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 6 +++++- docs/dev/debt/libSceAgcDriver.md | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 32b2bc8e08..623d147851 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -1193,7 +1193,7 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) { if (depthKeepsSampleZero && (read(cx, 0x292) & 1u) == 0) { const bool filled = result.topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST && result.topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST && result.topology != VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; const auto sampleInputs = read(cx, 0x1b3) & read(cx, 0x1b4) & (ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::Ancillary) | ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::SampleCoverage)); - Require(filled && !result.alphaToCoverage && (PixelProgramSkipped(queue) || ((read(cx, 0x293) & PixelShaderIteratesSamples) == 0 && sampleInputs == 0)) && (read(cx, 0x203) & 0x100u) == 0 && (read(cx, 0x201) & 0x0f0c0000u) == 0, "a one-sample depth target under a multisampled rasterizer without MSAA_ENABLE is unsupported for points or lines and with alpha-to-coverage, PS_ITER_SAMPLE, a sample index or coverage input, a sample-mask export, or DB_EQAA Z interpolation or overrasterization"); + Require(filled && !result.alphaToCoverage && (PixelProgramSkipped(queue) || sampleInputs == 0) && (read(cx, 0x203) & 0x100u) == 0 && (read(cx, 0x201) & 0x0f0c0000u) == 0, "a one-sample depth target under a multisampled rasterizer without MSAA_ENABLE is unsupported for points or lines and with alpha-to-coverage, a sample index or coverage input, a sample-mask export, or DB_EQAA Z interpolation or overrasterization"); result.samples = 1; result.customSampleLocations = false; result.sampleLocations.fill(0); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index f6b15bbc10..eaadf2b988 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -822,7 +822,7 @@ void msaaEnableClearOneSampleTargetTests() { queue.context[0x1b3] = queue.context[0x1b4] = 2u | ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::Ancillary) | ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::SampleCoverage); Require(AgcDriver::Graphics::PixelProgramSkipped(queue), "the depth-only draw did not skip its pixel program"); requireOneSampleDepth(queue, 0, "a one-sample depth target whose skipped pixel program has PS_ITER_SAMPLE and sample index and coverage inputs"); - constexpr std::string_view depthRefused = "a one-sample depth target under a multisampled rasterizer without MSAA_ENABLE is unsupported for points or lines and with alpha-to-coverage, PS_ITER_SAMPLE, a sample index or coverage input, a sample-mask export, or DB_EQAA Z interpolation or overrasterization"; + constexpr std::string_view depthRefused = "a one-sample depth target under a multisampled rasterizer without MSAA_ENABLE is unsupported for points or lines and with alpha-to-coverage, a sample index or coverage input, a sample-mask export, or DB_EQAA Z interpolation or overrasterization"; for (const bool color : {false, true}) { for (const auto input : {ShaderRecompiler::PixelInput::Ancillary, ShaderRecompiler::PixelInput::SampleCoverage}) { queue = depthTarget(color); @@ -833,6 +833,10 @@ void msaaEnableClearOneSampleTargetTests() { queue = depthTarget(color); queue.context[0x203] |= 0x40u; queue.context[0x293] = 0x10000u; + Require(!AgcDriver::Graphics::PixelProgramSkipped(queue), "the PS_ITER_SAMPLE draw skipped its pixel program"); + requireOneSampleDepth(queue, color ? 1u : 0u, "a one-sample depth target whose running pixel program has PS_ITER_SAMPLE and no sample index or coverage input"); + Require(!DecodeState(queue).perSampleShading, "a PS_ITER_SAMPLE program drawn as one sample still shades per sample"); + queue.context[0x1b3] = queue.context[0x1b4] = 2u | ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::Ancillary); expectFailure([&] { DecodeState(queue); }, depthRefused); queue = depthTarget(color); queue.context[0x1c4] = 9; diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index e8afc7eb4b..cd6fc367c5 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -21,7 +21,7 @@ - [GS_FAST_LAUNCH geometry](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH = 1` with a point list and `GE_CNTL` groups of one vertex and one primitive decodes as one point per subgroup, with the primitive limit taken from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR` and the GS/ES VGPR counts not required. Follows how RADV drives mesh shaders on gfx10.3 (`ac_nir_lower_ngg.c`, the workgroup index comes from the vertex id of the first thread), not measured on the console. Fast launch 2, other group sizes or input primitives and indexed fast-launch draws still throw. PPSA03671's fast-launch draws decode, but the title stops earlier in a wave64 vertex program, so they have not reached the GPU yet. - [Line-strip geometry output](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - a geometry program with `VGT_GS_OUT_PRIM_TYPE = 1` (line strip) runs as a mesh shader with `OutputLinesEXT`: each primitive export is one line whose two vertex indices are packed like the three of a triangle, as Mesa's NGG lowering exports line strips (`ac_nir_lower_ngg_gs.c`), and each input primitive gets `VGT_GS_MAX_VERT_OUT - 1` lines where a triangle strip gets `- 2` triangles. A subgroup gets at least `GE_MAX_OUTPUT_PER_SUBGROUP` line slots, because Mesa's lowering exports one primitive, possibly null, per emitted vertex; whether the console's compiler does the same is not known, and triangle strips keep `- 2` per input. A fast-launch subgroup takes its line count from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR` as for triangles. Line output counts as lines for the polygon offset decode, as on the vertex path. The lines are drawn 1 pixel wide with Vulkan's default line rasterization, like lines on the vertex path: `PA_SU_LINE_CNTL` is not read, and neither its width nor the console's line rules were measured. Points, rectangle lists, per-stream types and line strips from a vertex program on the mesh path still throw. - [Alpha-to-coverage](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - only `DB_ALPHA_TO_MASK = 0xaa01` is accepted: the enable bit with the four quad offsets at 2 and no `OFFSET_ROUND`, the undithered setting [radeonsi](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/gallium/drivers/radeonsi/si_state.c#L371-L381) programs. Draws are single-sampled, so the pixel shader [writes](../../../core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp) `SampleMask` 0 when its MRT0 alpha is below one half or NaN and all ones otherwise, and runs with late fragment tests. That rule was measured on a Radeon 660M (gfx1035, RADV with `RADV_DEBUG=noatocdithering`, which programs the same register) for 0, -0, 0.25, the two floats below one half, one half, the two above, 0.75, 1, 2, -1, both infinities and both NaNs, and is pinned in [AlphaToCoverage.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp); it is not measured on gfx1013. Vulkan's `alphaToCoverageEnable` is not used: the NVIDIA driver 615.78.08 dithers single-sample coverage (an alpha of 0.25 writes half the pixels of an RTX 3050 Laptop target). Other offsets, `OFFSET_ROUND`, a sample-mask export, and an MRT0 that is missing, swizzled, packed or exported without a floating-point alpha throw. A draw without a color target on MRT0 keeps alpha-to-coverage from its MRT0 export, as [radeonsi exports MRT0 as `32_AR` for alpha-to-coverage without a color buffer](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-24.0.0/src/gallium/drivers/radeonsi/si_state_shaders.cpp#L2425-L2431). A draw that enables alpha-to-coverage with no MRT0 export still throws: what the console covers then is not measured. Default dithered `DB_ALPHA_TO_MASK = 0x18701` additionally uses a per-quad sample-mask table [measured on gfx1036](https://github.com/boykopovar/AnyPS5/pull/4451#issuecomment-6124848921) for eight standard samples with FP16_ABGR or 32_ABGR exports; other sample counts, custom locations, EQAA combinations and dithered UNORM16/SNORM16 exports throw. The dithered rule is not measured on gfx1013. -- [A one-sample target under a multisampled rasterizer without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear, one-sample color targets and no depth target, or a one-sample depth target with no color target or one-sample color targets, is drawn as a one-sample draw at the pixel center. Without the bit every sample is taken to be at the pixel center (see the entry for a multisampled target rasterized without MSAA_ENABLE below), and a one-sample target is taken to keep the coverage of sample 0, so a one-sample depth target is tested and written once per pixel with the depth at the pixel center. That is the rule [`VK_AMD_mixed_attachment_samples`](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/fragops.adoc#L2510-L2516) gives a color target with fewer samples than the rasterizer, but Vulkan reaches it only beside a depth or stencil target with the rasterizer's sample count ([VUID-VkGraphicsPipelineCreateInfo-subpass-01505](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/pipelines.adoc#L3033-L3041)), so here it is an analogy, and for a one-sample depth target an extension of it. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp#L127-L128) also rasterizes with the largest sample count of the bound depth and color targets ([L416-L417](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L416-L417), [L477-L480](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L477-L480)), and reads `PA_SC_AA_CONFIG` only for a `SAMPLE_COVERAGE` input ([L174-L177](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L174-L177)). This is not measured on hardware. `PS_ITER_SAMPLE`, an `ANCILLARY` or `SAMPLE_COVERAGE` pixel input and a sample-mask export throw in a pixel program that runs, because what the console gives them under a multisampled configuration is not known; a depth-only draw that skips its pixel program is drawn with the null program, and its `PS_ITER_SAMPLE` and pixel input bits are ignored. Points, lines and alpha-to-coverage throw as for a multisampled target. With a one-sample depth target, `DB_EQAA` Z interpolation (`INTERPOLATE_COMP_Z`, `INTERPOLATE_SRC_Z`) or overrasterization (`OVERRASTERIZATION_AMOUNT`, `ENABLE_POSTZ_OVERRASTERIZATION`) also throws. METAL GEAR SOLID: Peace Walker (PPSA18216) draws a one-sample color target under a 4x configuration this way; the first draw of its opening movie binds a one-sample depth target and no color target under the same configuration. +- [A one-sample target under a multisampled rasterizer without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear, one-sample color targets and no depth target, or a one-sample depth target with no color target or one-sample color targets, is drawn as a one-sample draw at the pixel center. Without the bit every sample is taken to be at the pixel center (see the entry for a multisampled target rasterized without MSAA_ENABLE below), and a one-sample target is taken to keep the coverage of sample 0, so a one-sample depth target is tested and written once per pixel with the depth at the pixel center. That is the rule [`VK_AMD_mixed_attachment_samples`](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/fragops.adoc#L2510-L2516) gives a color target with fewer samples than the rasterizer, but Vulkan reaches it only beside a depth or stencil target with the rasterizer's sample count ([VUID-VkGraphicsPipelineCreateInfo-subpass-01505](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/pipelines.adoc#L3033-L3041)), so here it is an analogy, and for a one-sample depth target an extension of it. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp#L127-L128) also rasterizes with the largest sample count of the bound depth and color targets ([L416-L417](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L416-L417), [L477-L480](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L477-L480)), and reads `PA_SC_AA_CONFIG` only for a `SAMPLE_COVERAGE` input ([L174-L177](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L174-L177)). This is not measured on hardware. `PS_ITER_SAMPLE`, an `ANCILLARY` or `SAMPLE_COVERAGE` pixel input and a sample-mask export throw in a pixel program that runs, because what the console gives them under a multisampled configuration is not known; a depth-only draw that skips its pixel program is drawn with the null program, and its `PS_ITER_SAMPLE` and pixel input bits are ignored. With a one-sample depth target, a pixel program that runs with `PS_ITER_SAMPLE` and neither input is drawn once per pixel: every sample is taken to be at the pixel center, so invocations per sample would get the same inputs and give the same depth, kill and color results, and the target keeps sample 0's. Only the memory writes of such a program can differ, if the console runs it once per sample. Points, lines and alpha-to-coverage throw as for a multisampled target. With a one-sample depth target, `DB_EQAA` Z interpolation (`INTERPOLATE_COMP_Z`, `INTERPOLATE_SRC_Z`) or overrasterization (`OVERRASTERIZATION_AMOUNT`, `ENABLE_POSTZ_OVERRASTERIZATION`) also throws. METAL GEAR SOLID: Peace Walker (PPSA18216) draws a one-sample color target under a 4x configuration this way; the first draw of its opening movie binds a one-sample depth target and no color target under the same configuration. - [MSAA_ENABLE on a single-sample target](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_MODE_CNTL_0.MSAA_ENABLE` with `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES = 0` is drawn like the same state without the bit when sample 0 of the four `PA_SC_AA_SAMPLE_LOCS_PIXEL_*_0` registers is at the pixel center and the draw rasterizes filled triangles or rectangles (`VGT_PRIMITIVE_TYPE` 7 or 17 as well as the triangle types) with alpha-to-coverage and conservative rasterization off. [RADV](https://gitlab.freedesktop.org/mesa/mesa/-/blob/745709a000482a4a24c91faef5c5a40bd653a863/src/amd/vulkan/radv_cmd_buffer.c#L13606-13609) programs this state for one-sample custom sample locations on GFX10 and later, where the bit makes the rasterizer use the programmed location of sample 0 ([061bc615](https://gitlab.freedesktop.org/mesa/mesa/-/commit/061bc6151a11597f720535f00e79a5752e24aff6)); that the bit changes nothing else for such a draw is inferred from that and is not measured on hardware. EA Sports UFC 5 (PPSA03541) reaches a rect-list draw in this state. Other locations of sample 0, absent location registers, points, lines, alpha-to-coverage and conservative rasterization throw - [A multisampled target rasterized without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear is drawn with the target's sample count and every sample at the pixel center, as a single-sample scan into a multisampled target already is. On GFX10 and later the bit is what makes the rasterizer use the programmed sample locations ([RADV 061bc615](https://gitlab.freedesktop.org/mesa/mesa/-/commit/061bc6151a11597f720535f00e79a5752e24aff6)), and with multisampling disabled GL rasterizes at the pixel center and gives the fragment full coverage, which radeonsi programs by clearing the bit; that the hardware does the same with `MSAA_NUM_SAMPLES` > 0 is inferred from these and is not measured on hardware. A depth target with the same sample count also has every sample at the pixel center, so each depth and stencil sample is tested and written with the depth at the pixel center, as GL allows when multisampling is disabled ([L185-L190](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L185-L190), [L422-L425](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L422-L425)). GL also lets an implementation test only a centermost sample and set every sample from it ([L425-L434](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L425-L434)), which differs when a pixel's stored samples differ; which one the console does, and how its occlusion queries count such a draw, is not measured on hardware. `DB_EQAA` Z interpolation or overrasterization and a sample-mask export with a depth target throw. Points, lines and alpha-to-coverage throw. SONIC SUPERSTARS (PPSA06888) draws such a 2x target, and METAL GEAR SOLID: Peace Walker (PPSA18216) a 4x depth and color pair. - [sceAgcDriverGetEqContextId](../../../core/libs/prx/libSceAgcDriver/Eq/src/Query.cpp) (libSceAgcDriver) - returns `ident`: an end-of-pipe interrupt is delivered only to the registration whose id equals the raising queue, so `ident` is that context's id (titles register 0 or 1 for graphics and `0x20 + 8 * pipe + queue` for compute). The console's value is not known; KytyPS5 returns the event's `data`, which it fills with the interrupt's context id, whereas `data` here is the EV_CLEAR trigger count. Other event filters throw From 1f63ba35af1a5b6c787c796941a59f9fade27b29 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Mon, 19 Oct 2026 06:04:08 +0000 Subject: [PATCH 009/523] docs(agc): say where PS_ITER_SAMPLE alone still throws and which memory accesses can differ Co-Authored-By: Claude Opus 5.5 --- docs/dev/debt/libSceAgcDriver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index cd6fc367c5..389123ff63 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -21,7 +21,7 @@ - [GS_FAST_LAUNCH geometry](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH = 1` with a point list and `GE_CNTL` groups of one vertex and one primitive decodes as one point per subgroup, with the primitive limit taken from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR` and the GS/ES VGPR counts not required. Follows how RADV drives mesh shaders on gfx10.3 (`ac_nir_lower_ngg.c`, the workgroup index comes from the vertex id of the first thread), not measured on the console. Fast launch 2, other group sizes or input primitives and indexed fast-launch draws still throw. PPSA03671's fast-launch draws decode, but the title stops earlier in a wave64 vertex program, so they have not reached the GPU yet. - [Line-strip geometry output](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - a geometry program with `VGT_GS_OUT_PRIM_TYPE = 1` (line strip) runs as a mesh shader with `OutputLinesEXT`: each primitive export is one line whose two vertex indices are packed like the three of a triangle, as Mesa's NGG lowering exports line strips (`ac_nir_lower_ngg_gs.c`), and each input primitive gets `VGT_GS_MAX_VERT_OUT - 1` lines where a triangle strip gets `- 2` triangles. A subgroup gets at least `GE_MAX_OUTPUT_PER_SUBGROUP` line slots, because Mesa's lowering exports one primitive, possibly null, per emitted vertex; whether the console's compiler does the same is not known, and triangle strips keep `- 2` per input. A fast-launch subgroup takes its line count from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR` as for triangles. Line output counts as lines for the polygon offset decode, as on the vertex path. The lines are drawn 1 pixel wide with Vulkan's default line rasterization, like lines on the vertex path: `PA_SU_LINE_CNTL` is not read, and neither its width nor the console's line rules were measured. Points, rectangle lists, per-stream types and line strips from a vertex program on the mesh path still throw. - [Alpha-to-coverage](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - only `DB_ALPHA_TO_MASK = 0xaa01` is accepted: the enable bit with the four quad offsets at 2 and no `OFFSET_ROUND`, the undithered setting [radeonsi](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/gallium/drivers/radeonsi/si_state.c#L371-L381) programs. Draws are single-sampled, so the pixel shader [writes](../../../core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp) `SampleMask` 0 when its MRT0 alpha is below one half or NaN and all ones otherwise, and runs with late fragment tests. That rule was measured on a Radeon 660M (gfx1035, RADV with `RADV_DEBUG=noatocdithering`, which programs the same register) for 0, -0, 0.25, the two floats below one half, one half, the two above, 0.75, 1, 2, -1, both infinities and both NaNs, and is pinned in [AlphaToCoverage.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp); it is not measured on gfx1013. Vulkan's `alphaToCoverageEnable` is not used: the NVIDIA driver 615.78.08 dithers single-sample coverage (an alpha of 0.25 writes half the pixels of an RTX 3050 Laptop target). Other offsets, `OFFSET_ROUND`, a sample-mask export, and an MRT0 that is missing, swizzled, packed or exported without a floating-point alpha throw. A draw without a color target on MRT0 keeps alpha-to-coverage from its MRT0 export, as [radeonsi exports MRT0 as `32_AR` for alpha-to-coverage without a color buffer](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-24.0.0/src/gallium/drivers/radeonsi/si_state_shaders.cpp#L2425-L2431). A draw that enables alpha-to-coverage with no MRT0 export still throws: what the console covers then is not measured. Default dithered `DB_ALPHA_TO_MASK = 0x18701` additionally uses a per-quad sample-mask table [measured on gfx1036](https://github.com/boykopovar/AnyPS5/pull/4451#issuecomment-6124848921) for eight standard samples with FP16_ABGR or 32_ABGR exports; other sample counts, custom locations, EQAA combinations and dithered UNORM16/SNORM16 exports throw. The dithered rule is not measured on gfx1013. -- [A one-sample target under a multisampled rasterizer without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear, one-sample color targets and no depth target, or a one-sample depth target with no color target or one-sample color targets, is drawn as a one-sample draw at the pixel center. Without the bit every sample is taken to be at the pixel center (see the entry for a multisampled target rasterized without MSAA_ENABLE below), and a one-sample target is taken to keep the coverage of sample 0, so a one-sample depth target is tested and written once per pixel with the depth at the pixel center. That is the rule [`VK_AMD_mixed_attachment_samples`](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/fragops.adoc#L2510-L2516) gives a color target with fewer samples than the rasterizer, but Vulkan reaches it only beside a depth or stencil target with the rasterizer's sample count ([VUID-VkGraphicsPipelineCreateInfo-subpass-01505](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/pipelines.adoc#L3033-L3041)), so here it is an analogy, and for a one-sample depth target an extension of it. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp#L127-L128) also rasterizes with the largest sample count of the bound depth and color targets ([L416-L417](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L416-L417), [L477-L480](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L477-L480)), and reads `PA_SC_AA_CONFIG` only for a `SAMPLE_COVERAGE` input ([L174-L177](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L174-L177)). This is not measured on hardware. `PS_ITER_SAMPLE`, an `ANCILLARY` or `SAMPLE_COVERAGE` pixel input and a sample-mask export throw in a pixel program that runs, because what the console gives them under a multisampled configuration is not known; a depth-only draw that skips its pixel program is drawn with the null program, and its `PS_ITER_SAMPLE` and pixel input bits are ignored. With a one-sample depth target, a pixel program that runs with `PS_ITER_SAMPLE` and neither input is drawn once per pixel: every sample is taken to be at the pixel center, so invocations per sample would get the same inputs and give the same depth, kill and color results, and the target keeps sample 0's. Only the memory writes of such a program can differ, if the console runs it once per sample. Points, lines and alpha-to-coverage throw as for a multisampled target. With a one-sample depth target, `DB_EQAA` Z interpolation (`INTERPOLATE_COMP_Z`, `INTERPOLATE_SRC_Z`) or overrasterization (`OVERRASTERIZATION_AMOUNT`, `ENABLE_POSTZ_OVERRASTERIZATION`) also throws. METAL GEAR SOLID: Peace Walker (PPSA18216) draws a one-sample color target under a 4x configuration this way; the first draw of its opening movie binds a one-sample depth target and no color target under the same configuration. +- [A one-sample target under a multisampled rasterizer without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear, one-sample color targets and no depth target, or a one-sample depth target with no color target or one-sample color targets, is drawn as a one-sample draw at the pixel center. Without the bit every sample is taken to be at the pixel center (see the entry for a multisampled target rasterized without MSAA_ENABLE below), and a one-sample target is taken to keep the coverage of sample 0, so a one-sample depth target is tested and written once per pixel with the depth at the pixel center. That is the rule [`VK_AMD_mixed_attachment_samples`](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/fragops.adoc#L2510-L2516) gives a color target with fewer samples than the rasterizer, but Vulkan reaches it only beside a depth or stencil target with the rasterizer's sample count ([VUID-VkGraphicsPipelineCreateInfo-subpass-01505](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/pipelines.adoc#L3033-L3041)), so here it is an analogy, and for a one-sample depth target an extension of it. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp#L127-L128) also rasterizes with the largest sample count of the bound depth and color targets ([L416-L417](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L416-L417), [L477-L480](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L477-L480)), and reads `PA_SC_AA_CONFIG` only for a `SAMPLE_COVERAGE` input ([L174-L177](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L174-L177)). This is not measured on hardware. An `ANCILLARY` or `SAMPLE_COVERAGE` pixel input and a sample-mask export throw in a pixel program that runs, and so does `PS_ITER_SAMPLE` with one-sample color targets and no depth target, because what the console gives them under a multisampled configuration is not known; a depth-only draw that skips its pixel program is drawn with the null program, and its `PS_ITER_SAMPLE` and pixel input bits are ignored. With a one-sample depth target, a pixel program with `PS_ITER_SAMPLE` and neither input runs once per pixel; if the console runs it once per sample, its atomics and append or consume counters count differently. Points, lines and alpha-to-coverage throw as for a multisampled target. With a one-sample depth target, `DB_EQAA` Z interpolation (`INTERPOLATE_COMP_Z`, `INTERPOLATE_SRC_Z`) or overrasterization (`OVERRASTERIZATION_AMOUNT`, `ENABLE_POSTZ_OVERRASTERIZATION`) also throws. METAL GEAR SOLID: Peace Walker (PPSA18216) draws a one-sample color target under a 4x configuration this way; the first draw of its opening movie binds a one-sample depth target and no color target under the same configuration. - [MSAA_ENABLE on a single-sample target](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_MODE_CNTL_0.MSAA_ENABLE` with `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES = 0` is drawn like the same state without the bit when sample 0 of the four `PA_SC_AA_SAMPLE_LOCS_PIXEL_*_0` registers is at the pixel center and the draw rasterizes filled triangles or rectangles (`VGT_PRIMITIVE_TYPE` 7 or 17 as well as the triangle types) with alpha-to-coverage and conservative rasterization off. [RADV](https://gitlab.freedesktop.org/mesa/mesa/-/blob/745709a000482a4a24c91faef5c5a40bd653a863/src/amd/vulkan/radv_cmd_buffer.c#L13606-13609) programs this state for one-sample custom sample locations on GFX10 and later, where the bit makes the rasterizer use the programmed location of sample 0 ([061bc615](https://gitlab.freedesktop.org/mesa/mesa/-/commit/061bc6151a11597f720535f00e79a5752e24aff6)); that the bit changes nothing else for such a draw is inferred from that and is not measured on hardware. EA Sports UFC 5 (PPSA03541) reaches a rect-list draw in this state. Other locations of sample 0, absent location registers, points, lines, alpha-to-coverage and conservative rasterization throw - [A multisampled target rasterized without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear is drawn with the target's sample count and every sample at the pixel center, as a single-sample scan into a multisampled target already is. On GFX10 and later the bit is what makes the rasterizer use the programmed sample locations ([RADV 061bc615](https://gitlab.freedesktop.org/mesa/mesa/-/commit/061bc6151a11597f720535f00e79a5752e24aff6)), and with multisampling disabled GL rasterizes at the pixel center and gives the fragment full coverage, which radeonsi programs by clearing the bit; that the hardware does the same with `MSAA_NUM_SAMPLES` > 0 is inferred from these and is not measured on hardware. A depth target with the same sample count also has every sample at the pixel center, so each depth and stencil sample is tested and written with the depth at the pixel center, as GL allows when multisampling is disabled ([L185-L190](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L185-L190), [L422-L425](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L422-L425)). GL also lets an implementation test only a centermost sample and set every sample from it ([L425-L434](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L425-L434)), which differs when a pixel's stored samples differ; which one the console does, and how its occlusion queries count such a draw, is not measured on hardware. `DB_EQAA` Z interpolation or overrasterization and a sample-mask export with a depth target throw. Points, lines and alpha-to-coverage throw. SONIC SUPERSTARS (PPSA06888) draws such a 2x target, and METAL GEAR SOLID: Peace Walker (PPSA18216) a 4x depth and color pair. - [sceAgcDriverGetEqContextId](../../../core/libs/prx/libSceAgcDriver/Eq/src/Query.cpp) (libSceAgcDriver) - returns `ident`: an end-of-pipe interrupt is delivered only to the registration whose id equals the raising queue, so `ident` is that context's id (titles register 0 or 1 for graphics and `0x20 + 8 * pipe + queue` for compute). The console's value is not known; KytyPS5 returns the event's `data`, which it fills with the interrupt's context id, whereas `data` here is the EV_CLEAR trigger count. Other event filters throw From c5dbdf1fde231a5a9bc0e7cf6212019aeb9e9aa3 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Mon, 19 Oct 2026 16:18:13 +0300 Subject: [PATCH 010/523] fix(agc): name the RELEASE_MEM system counter instead of GDS data A RELEASE_MEM with DATA_SEL 4 was refused as "GDS RELEASE_MEM data". amdgpu's GFX10 packet header gives 4 as the 64-bit system counter and 5 as GDS data. Validation now refuses 4 as the system counter, whose console frequency and epoch are not known, 5 as GDS data and 6 or 7 as reserved, and the debt file records the system counter. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp | 2 ++ core/libs/prx/libSceAgcDriver/tests/Pm4.cpp | 10 ++++++++++ docs/dev/debt/libSceAgcDriver.md | 1 + 3 files changed, 13 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp index aec2b3069b..bab78881d7 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp @@ -470,6 +470,8 @@ void Validate(std::span packet, std::uint32_t queue) { case 0x49: { size(8); const auto dataSelect = packet[2] >> 29u; + require(dataSelect != 4, "RELEASE_MEM of the 64-bit system counter (DATA_SEL 4) is not implemented"); + require(dataSelect <= 5, "reserved RELEASE_MEM DATA_SEL"); require(dataSelect <= 3, "GDS RELEASE_MEM data is not implemented"); require(dataSelect == 0 || address(packet[3], packet[4]) == 0 || (packet[3] & (dataSelect == 1 ? 3u : 7u)) == 0, "misaligned RELEASE_MEM destination"); break; diff --git a/core/libs/prx/libSceAgcDriver/tests/Pm4.cpp b/core/libs/prx/libSceAgcDriver/tests/Pm4.cpp index d2e9b4c2aa..665623b412 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Pm4.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Pm4.cpp @@ -1029,6 +1029,15 @@ void testIndexAttributesReadAtExecution() { check(std::atomic_ref(done).load() == 1, "INDEX_ATTRIBUTES_INDIRECT did not read its record when the packet ran"); } +void testReleaseMemDataSelects() { + alignas(8) static std::uint64_t target = 0; + const auto release = [](std::uint32_t select) { return makePacket(0x49, {0x0030c514, select << 29u, low(&target), high(&target), 1, 0, 0}); }; + for (std::uint32_t select = 0; select <= 3; ++select) AgcDriver::Pm4::Validate(release(select), 0); + expectFailure([&] { AgcDriver::Pm4::Validate(release(4), 0); }, "64-bit system counter (DATA_SEL 4)"); + expectFailure([&] { AgcDriver::Pm4::Validate(release(5), 0); }, "GDS RELEASE_MEM data"); + for (const auto select : {6u, 7u}) expectFailure([&] { AgcDriver::Pm4::Validate(release(select), 0); }, "reserved RELEASE_MEM DATA_SEL"); +} + void testSuspendPointWritesQueuedLabels() { alignas(8) static std::uint32_t gate = 0, marker = 0; gate = 0; @@ -1847,6 +1856,7 @@ int main(int argc, char** argv) { testConditionalValidation(); testConditionReadSynchronization(); testEventWrite(); + testReleaseMemDataSelects(); testAcquireMem(); testConditionalWrite(); testConditionalWriteSubmission(); diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index c0542af03e..102fd9cad7 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -14,6 +14,7 @@ - [Optional BMP frame dumps](../../../core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp) use checked host stdio. An open, header, pixel or close failure prints one diagnostic and disables subsequent BMP dumps for the process; queued frames drain without more file I/O. Pixel validation and allocation failures retain the existing fatal path. The CPU fixture checks identical BMP bytes and real pipe failures. VideoOut flips on an RTX 3060 continue after a first dump that cannot be opened; header, pixel and close failures during presentation are unmeasured. - [Inactive vertex point size](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) - `PA_CL_VS_OUT_CNTL.USE_VTX_POINT_SIZE` is accepted for lines, triangles and rectangles with `GS_EN` and `HS_EN` clear, when polygon mode is disabled or both faces are filled. Points, patches and absent routing or rasterization registers still throw. Bit 16 selects the misc export's X component as PointSize ([AMD CIK register reference](https://www.amd.com/content/dam/amd/en/documents/radeon-tech-docs/programmer-references/CIK_3D_registers_v2.pdf), PA_CL_VS_OUT_CNTL); ignoring it for non-point rasterization follows [Vulkan PointSize](https://docs.vulkan.org/refpages/latest/refpages/source/PointSize.html). This is inferred, not measured on console hardware. XashPS5 (PPSA19111 v1.3) sets `0x01210000` on a filled triangle draw. +- [RELEASE_MEM of the system counter](../../../core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp) (AGC driver) - a `RELEASE_MEM` with `DATA_SEL = 4` throws: the frequency and epoch of the console's 64-bit system counter are not known. [amdgpu's GFX10 packet header](https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/amdgpu/nvd.h) names the value "send 64bit sys counter value", beside 3, the GPU counter, which the driver writes as a 100 MHz count of the host's steady clock. An older KFD header names 4 a CP performance counter instead. A PS4 title through #6977 writes it at the end of each frame. - [Native multisampled depth clears](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) accept the recognized HTILE mask kernel's direct full-range dispatch after its GPU writes complete. Every metadata byte needs tracked driver-store coverage since notification, no intervening CPU or depth write, and HTILE words proving the current clear value of zero or one. Partial, overlapping, indirect and unproven producers remain unsupported. Existing sampled views retain the native image; guest depth publication and metadata-fill transitions remain unimplemented. Windows shared imports retain write tracking through separate aliases; private host imports that lose tracking and copied or mirrored HTILE uploads remain unsupported. Clear-word encoding and console equivalence remain unmeasured on PS5 hardware. - [Native depth allocation retirement](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) covers sampling-eligible multisampled owners only. Successful release notifications must cumulatively cover both complete depth and HTILE ranges; protection changes, failed releases and partial coverage keep the original guards. Retired owners leave lookup and metadata routing, while held views and recorded work retain their images. Retirement of single-sample or sampling-ineligible depth attachments and console equivalence remain unimplemented or unmeasured. The regression uses real allocation-registry operations with a no-op host-release callback, so it does not validate the host unmap backend. - [Native MSAA depth attachment feedback](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) is not implemented. A draw sampling a native multisampled depth view while attaching its source image throws before pipeline creation or render-pass recording, including read-only attachment feedback. Sampling that image in a separate draw or dispatch remains supported. The refusal compares the held native image views and their device and attachment identity; single-sample depth behavior is retained. From 82be87f1852b539fac780c93841b001c4bc75c9f Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 09:10:23 +0000 Subject: [PATCH 011/523] fix(agc): refuse LDS split before allocation --- .../Execution/src/VulkanDevice.cpp | 14 ++++++++------ .../tests/execution/OversizedDispatch.cpp | 18 +++++++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 1ebaafcc12..46d0e2d70f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -4606,6 +4606,14 @@ void VulkanDevice::recordDispatch(RecordedDispatch& record) { auto& resources = *record.resources; const auto arguments = record.arguments; const auto* argumentImport = record.argumentImport; + const auto* limit = state->properties.limits.maxComputeWorkGroupCount; + if (argumentImport == nullptr && record.shader->program->workgroupMemoryDwords != 0 && (record.x > limit[0] || record.y > limit[1] || record.z > limit[2])) { + char counted[128] = ""; + if (record.cpuArguments != 0) std::snprintf(counted, sizeof(counted), " (read on the CPU from the indirect arguments at 0x%llx, cpuReason %d)", static_cast(record.cpuArguments), record.cpuReason); + char text[384]; + std::snprintf(text, sizeof(text), "Vulkan dispatch: workgroup count %ux%ux%u%s exceeds device limits %ux%ux%u and a split aliases the device-memory LDS slots, whose base is computed from gl_NumWorkGroups of a single chunk", record.x, record.y, record.z, counted, limit[0], limit[1], limit[2]); + throw std::runtime_error(text); + } // The record phase split (APS5_PROFILE_DRAW, rows "record: ..." of the [dispatch] totals): // opening the batch, the keeps, the data refresh, the barriers with the bind and the dispatch // itself, the pending-write notes and marks (MarkGpuWrites), and the completion registration. @@ -4718,17 +4726,11 @@ void VulkanDevice::recordDispatch(RecordedDispatch& record) { if (argumentImport != nullptr) { context.Resolved(&Graphics::DeviceFunctions::cmdDispatchIndirect, "vkCmdDispatchIndirect")(commands, indirectBuffer, indirectOffset); } else { - const auto* limit = state->properties.limits.maxComputeWorkGroupCount; if (record.x <= limit[0] && record.y <= limit[1] && record.z <= limit[2]) { context.Resolved(&Graphics::DeviceFunctions::cmdDispatch, "vkCmdDispatch")(commands, record.x, record.y, record.z); } else { char counted[128] = ""; if (record.cpuArguments != 0) std::snprintf(counted, sizeof(counted), " (read on the CPU from the indirect arguments at 0x%llx, cpuReason %d)", static_cast(record.cpuArguments), record.cpuReason); - if (record.shader->program->workgroupMemoryDwords != 0) { - char text[384]; - std::snprintf(text, sizeof(text), "Vulkan dispatch: workgroup count %ux%ux%u%s exceeds device limits %ux%ux%u and a split aliases the device-memory LDS slots, whose base is computed from gl_NumWorkGroups of a single chunk", record.x, record.y, record.z, counted, limit[0], limit[1], limit[2]); - throw std::runtime_error(text); - } const auto offsets = std::ranges::count_if(record.shader->program->specialization, [](const auto& constant) { return ShaderRecompiler::PipelineSpecialization::IsWorkgroupOffset(constant.id); }); if (offsets != ShaderRecompiler::PipelineSpecialization::WorkgroupOffsetWords) { char text[320]; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/OversizedDispatch.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/OversizedDispatch.cpp index 06b14970f4..9cc490015d 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/OversizedDispatch.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/OversizedDispatch.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -135,14 +136,17 @@ void CheckLdsSplitThrows(AgcDriver::VulkanDevice& device) { request.useCache = false; const auto result = ShaderRecompiler::Recompile(request); Require(result.workgroupMemoryDwords != 0u, "oversized dispatch: the LDS shader did not take the device-memory path"); - bool threw = false; - try { - device.Dispatch(result, 1, device.WorkGroupCountLimit()[1] + 1u, 1, {}, reinterpret_cast(code.data())); - } catch (const std::runtime_error&) { - threw = true; + for (const auto z : {1u, std::numeric_limits::max()}) { + bool threw = false; + try { + device.Dispatch(result, 1, device.WorkGroupCountLimit()[1] + 1u, z, {}, reinterpret_cast(code.data())); + } catch (const std::runtime_error& error) { + Require(std::string(error.what()).find("a split aliases the device-memory LDS slots") != std::string::npos, "oversized dispatch: wrong LDS split refusal: " + std::string(error.what())); + threw = true; + } + device.WaitIdle(); + Require(threw, "oversized dispatch: a Y count over the limit with device-memory LDS did not throw"); } - device.WaitIdle(); - Require(threw, "oversized dispatch: a Y count over the limit with device-memory LDS did not throw"); } alignas(256) constexpr std::array GroupYCode{ From c9c6fc7a955dfaa7f1f5b6bf2b09d21b778ed3ad Mon Sep 17 00:00:00 2001 From: David Date: Mon, 19 Oct 2026 19:09:48 +0400 Subject: [PATCH 012/523] feat(libkernel): check file and shared memory descriptors for the guest's mmap on macOS --- core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp b/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp index 7c69af40fa..55d3b242d1 100644 --- a/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp +++ b/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp @@ -11,7 +11,7 @@ #include #include #include -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) #include #include #include @@ -57,7 +57,7 @@ constexpr std::uintptr_t GuestUserAddressEnd = 0x800000000000; constexpr int GuestAlignedSuper = 0x1000000; constexpr std::size_t SuperpageSize = std::size_t{1} << 21; -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) class ScopedDescriptor { public: explicit ScopedDescriptor(int value) noexcept : descriptor(fcntl(value, F_DUPFD_CLOEXEC, 0)), error(descriptor < 0 ? errno : 0) {} @@ -195,7 +195,7 @@ void* APS5_VABI mmap_nid_postfix(void* address, std::size_t length, int protecti rounded > static_cast(std::numeric_limits::max()) || static_cast(offset) > static_cast(std::numeric_limits::max()) - rounded) return failed(GuestInvalid); -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) const ScopedDescriptor pinnedDescriptor(descriptor); if (pinnedDescriptor.Get() < 0) return failed(pinnedDescriptor.Error()); const int mappingDescriptor = pinnedDescriptor.Get(); From f69f9da2a07494e457dfe3d9defb2cbd790b8567 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 19 Oct 2026 19:09:48 +0400 Subject: [PATCH 013/523] feat(libkernel): map files through the guest's mmap on macOS, adding execute after mapping --- .../prx/libkernel/DirectMemory/DirectMemory.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp b/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp index 8d5b7996ad..e7caaf4091 100644 --- a/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp +++ b/core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp @@ -1069,7 +1069,7 @@ int DoMapAnon(void** addr, size_t len, int prot, int flags, size_t alignment, bo int DoMapFileBacked_nid_no_patch(void** addr, size_t len, int prot, int flags, int descriptor, std::uint64_t offset, size_t alignment) { ValidateOutput(addr); if (len == 0 || (len & (PS5_PAGE_SIZE - 1)) != 0 || (offset & (PS5_PAGE_SIZE - 1)) != 0 || len > static_cast(std::numeric_limits::max()) || offset > static_cast(std::numeric_limits::max()) - len) return SCE_KERNEL_ERROR_EINVAL; -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) const bool shared = (flags & 3) == 1; GuestAllocations::Mutation mutation; const int placementFlags = flags & (GuestMapFixedFlag | 0x80 | 0x400000); @@ -1080,11 +1080,21 @@ int DoMapFileBacked_nid_no_patch(void** addr, size_t len, int prot, int flags, i GuestWriteWatch::GuestWriteWatchUnregister_nid_postfix(mapped, len); const int nativeProtection = LinuxProtFromSce(prot); const int nativeFlags = (shared ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED; - if (::mmap(mapped, len, nativeProtection, nativeFlags, descriptor, static_cast(offset)) == MAP_FAILED) { +#ifdef __APPLE__ + const int mappedProtection = nativeProtection & ~PROT_EXEC; +#else + const int mappedProtection = nativeProtection; +#endif + if (::mmap(mapped, len, mappedProtection, nativeFlags, descriptor, static_cast(offset)) == MAP_FAILED) { const int error = errno; Unmap(mapped, len); throw std::system_error(error, std::generic_category(), "map guest shared memory"); } + if (mappedProtection != nativeProtection && ::mprotect(mapped, len, nativeProtection) != 0) { + const int error = errno; + Unmap(mapped, len); + throw std::system_error(error, std::generic_category(), "make a guest file mapping executable"); + } try { mutation.Add(mapped, len, (prot & 3) != 0, (prot & 2) != 0, false); } catch (...) { From cd0e8efb0771a550bddf12e05ae1b17179112879 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 19 Oct 2026 19:09:49 +0400 Subject: [PATCH 014/523] test(libkernel): run code from an executable mapping of a shared memory object --- core/libs/tests/GuestShmOpen.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/libs/tests/GuestShmOpen.cpp b/core/libs/tests/GuestShmOpen.cpp index bc2fde5cb1..ff4f8fb16c 100644 --- a/core/libs/tests/GuestShmOpen.cpp +++ b/core/libs/tests/GuestShmOpen.cpp @@ -46,6 +46,14 @@ int main() { Require(pread_nid_postfix(first, &last, 1, 0x2000) == 1 && last == 'm'); Require(munmap_nid_postfix(const_cast(view), 0x4000) == 0); #ifndef _WIN32 + const unsigned char code[] = {0xb8, 0x2a, 0, 0, 0, 0xc3}; + Require(pwrite_nid_disambig1_nid_postfix(first, code, sizeof(code), 0) == static_cast(sizeof(code))); + void* executable = mmap_nid_postfix(nullptr, 0x4000, 5, 0x1, first, 0); + Require(executable != reinterpret_cast(static_cast(-1))); + int (*function)(); + std::memcpy(&function, &executable, sizeof(function)); + Require(function() == 42); + Require(munmap_nid_postfix(executable, 0x4000) == 0); Require(pwrite_nid_disambig1_nid_postfix(third, text, sizeof(text), 0) == 0); #endif Require(ftruncate_nid_postfix(third, 0x4000) == 0); From 758e22d4f4bb14713ac5f538c47e5c85197de4cb Mon Sep 17 00:00:00 2001 From: David Date: Mon, 19 Oct 2026 19:09:49 +0400 Subject: [PATCH 015/523] docs(debt): record file mappings on macOS --- docs/dev/debt/libkernel.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 6ccdf90aa5..54df3ab5ff 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -198,6 +198,7 @@ - [fcntl on a guest socket](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answers `F_GETFD`, `F_SETFD`, `F_GETFL` and `F_SETFL` and fails every other command with `EINVAL`; FreeBSD 11.4's `kern_fcntl` also duplicates the descriptor (`F_DUPFD`, `F_DUPFD_CLOEXEC`) and handles `F_GETOWN` and `F_SETOWN` on a socket. `FD_CLOEXEC` is only recorded (there is no `exec`): `dup`, `dup2` and an accepted socket start without it, `SOCK_CLOEXEC` and `F_SETFD` set it. None of this was measured on a console. - [ioctl, _ioctl](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) handle only `FIONBIO` and `FIONREAD` on a socket, and any other request there fails with `ENOTTY`. On the other descriptors they follow FreeBSD's [`kern_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_generic.c#L758), [`vn_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_vnops.c#L1488) and [`pipe_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_pipe.c#L1346): `FIONBIO` sets `O_NONBLOCK` as `fcntl` does, `FIOCLEX` and `FIONCLEX` set and clear the close-on-exec flag `F_GETFD` reports, and `FIONREAD` reports the size past the offset of a file, the queued bytes of a pipe's read end, 0 on its write end and `ENOTTY` on the random device, while it throws on a directory or another character device. Every termios request (group `t`, `TIOCGWINSZ` included) fails with `ENOTTY`, since those descriptors are taken as not a terminal, as in `tcgetattr`, and any other request throws. Stremio Plus (PPSA74126) imports `_ioctl` for its statically linked `tc*` functions, which send `TIOCGETA`, `TIOCSETA`, `TIOCSPGRP` and the other termios requests - [Regular file mappings](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) - on Linux `mmap` maps a regular file's descriptor shared or private like a shared memory object, with the same access checks; SQLite's WAL index (`-shm`) in Dusklight PPSA99305 needs it. FreeBSD 11 `mmap(2)` and shadPS4's `MemoryManager::MapFile` map regular files; the console's behaviour is not measured. Other descriptors (pipes, sockets, directories, devices) still fail with `EOPNOTSUPP`. On Windows a shared mapping is a view of a file section (`CreateFileMappingW` on the descriptor's handle, read-write when the descriptor is, otherwise read-only), so writes through the descriptor and the mapping see each other. This also maps anonymous `shm_open` objects and JIT shared memory descriptors, which are temporary files on Windows. A shared mapping may end inside the 16 KiB page that holds the end of the file, as POSIX [`mmap`](https://pubs.opengroup.org/onlinepubs/9799919799/functions/mmap.html) allows (llama.cpp in PS5LM Plus PPSA99581 maps a whole model file this way): the section keeps the file's size, so the file never grows, the view ends with the 4 KiB host page that holds the end of the file, which Windows fills with zeros past it, and the rest of the guest page is private zero pages. As POSIX requires, the bytes past the end read zero and are never written to the file; bytes written to those private pages are seen only through that mapping, not through other mappings of the file, and a file that grows later does not show its new bytes there. A shared mapping that reaches a whole page past the end of the file fails with `EOPNOTSUPP`, where POSIX maps it and delivers `SIGBUS` on access, as do `PROT_EXEC` and a write-only descriptor. A read-write alias (`GuestArenaMapAlias`) of a mapped page that holds the end of the file throws. `mprotect` above the view's protection (writing through a read-only descriptor, or adding `PROT_EXEC`) fails with `EACCES` before anything changes; FreeBSD allows `PROT_EXEC` on a readable descriptor. Shrinking the file with `ftruncate` fails with `EINVAL` while any shared mapping of it exists, even one that does not cover the removed part, because the section keeps the file's size at `mmap`; FreeBSD truncates. A read-write alias of a view (`GuestArenaMapAlias`, used when the GPU imports guest memory) of a read-only file section fails, as the alias is mapped `PAGE_READWRITE`. Views of the same file made by separate `mmap` calls are separate sections, so write tracking does not treat them as aliases of each other. A private mapping on Windows is a copy of the file read at `mmap`, which POSIX allows (later changes to the file need not show); pages wholly past the end of the file fail with `EOPNOTSUPP` instead of faulting on access, and a write-only descriptor fails with `EOPNOTSUPP`. +- [File mappings on macOS](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) - `mmap` of a regular file, of an anonymous `shm_open` object (an unlinked temporary file on macOS) or of a JIT shared memory descriptor maps the descriptor shared or private as on Linux, with the same access checks. macOS refuses to create an executable file mapping (`EPERM`), so an executable mapping is made without execute and then given it with `mprotect`. Under Rosetta 2 (Apple M2, macOS 26), code rewritten through one view of a file after it ran from another view keeps its old translation until the executable view's protection changes. Named `shm_open` objects still throw on macOS. - [sendfile](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) follows FreeBSD 11's `sendfile` for a connected stream socket: headers, the file from `offset` (to its end when `nbytes` is 0) and trailers, the file offset left unchanged, `EAGAIN` with `*sbytes` set after a partial send on a non-blocking socket. It reads the file through `sceKernelPread` in 64 KiB blocks and sends each with `send`. A socket as the file fails with `EINVAL`, as on FreeBSD, but any other descriptor `sceKernelPread` reads, such as `/dev/urandom`, is sent where FreeBSD requires a regular file or shared memory, and a file descriptor as the socket fails with `EBADF`, as for `send`, where FreeBSD answers `ENOTSOCK`. `flags` are ignored: reads complete before sending, so `SF_NODISKIO` never fails with `EBUSY`, and `SF_SYNC` returns once the data is queued on the socket rather than sent. PS5MC (PPSA99420) imports it - [pthread_kill](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) returns the error number like FreeBSD's `pthread_kill`: `EINVAL` for signals outside 0 to 128 and `ESRCH` for a null thread. Signal 0 reports any other thread handle as existing, without checking that the thread is still alive. A signal to the calling thread goes through `raise`, so it shares `raise`'s limits; a signal to another thread is left on the pending set for whichever thread waits for or unblocks it, since it is not delivered to the chosen thread, and a handle that is not a live guest thread returns `ESRCH` without touching it - [select](../../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) validates `tv_usec` but saturates a valid timeout beyond the remaining `steady_clock` range at `time_point::max()`, so the wait can continue effectively indefinitely. The console's behavior for a valid timeout outside the host clock's range is unknown. From 7701a9b9da6794e5bd638074fbf9bf7fa9ff5e85 Mon Sep 17 00:00:00 2001 From: mugurc Date: Mon, 12 Oct 2026 15:41:54 +0300 Subject: [PATCH 016/523] feat(libkernel): UDP sockets and name resolution on macOS hosts The guest socket layer relies on a few Linux calls: - The equeue wait uses poll with the timeout rounded up to a millisecond; macOS has no ppoll. - A readable stream socket whose peer closed is reported with POLLHUP by peeking one byte; macOS has no POLLRDHUP. - 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; macOS adds the source address to FIONREAD. - socketpair throws for SOCK_SEQPACKET, which has no AF_UNIX pair on macOS. - gethostbyname replaces gethostbyname_r, which macOS lacks. The UDP test sends on its connected socket without an address, as #7475 does, because macOS, like FreeBSD, fails sendto with an address on a connected socket with EISCONN. The log test uses a datagram pair with enlarged buffers instead of SOCK_SEQPACKET. --- .../prx/libScePosixForWebKit/Resolver.cpp | 8 +++++ core/libs/prx/libkernel/Socket/src/Udp.cpp | 29 +++++++++++++++++-- core/libs/tests/GuestSocketPair.cpp | 15 +++++++++- core/libs/tests/GuestUdp.cpp | 4 +-- core/libs/tests/LogMacros.cpp | 6 ++++ docs/dev/debt/libkernel.md | 3 +- 6 files changed, 58 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libScePosixForWebKit/Resolver.cpp b/core/libs/prx/libScePosixForWebKit/Resolver.cpp index 0dd6901832..f9b8a2acc2 100644 --- a/core/libs/prx/libScePosixForWebKit/Resolver.cpp +++ b/core/libs/prx/libScePosixForWebKit/Resolver.cpp @@ -182,9 +182,17 @@ GuestResolver::HostEntry* APS5_VABI gethostbyname_nid_postfix(const char* name) int hostError = 0; int error = 0; if (!numeric) { +#ifdef __APPLE__ + native = ::gethostbyname(name); + if (native == nullptr) { + hostError = h_errno; + error = hostError == TRY_AGAIN ? EAGAIN : 0; + } +#else buffer.resize(1024); while ((error = ::gethostbyname_r(name, &nativeEntry, buffer.data(), buffer.size(), &native, &hostError)) == ERANGE) buffer.resize(buffer.size() * 2); +#endif } if (error != 0 || !native) { *sceNetErrnoLoc() = hostError != 0 ? hostError : 3; diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index b4c71263e0..566de5064b 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -397,6 +397,10 @@ bool GuestSockets::WaitAny(const std::vector& interests, std::uint64_t if (pipes && (timeout < 0 || timeout > 1)) timeout = 1; if (WSAPoll(entries.data(), static_cast(entries.size()), timeout) == SOCKET_ERROR) throw std::runtime_error("equeue wait: WSAPoll failed, guest errno " + std::to_string(NativeError())); +#elif defined(__APPLE__) + const int timeout = deadlineNanos != 0 ? static_cast(std::min((remaining + 999999) / 1000000, INT_MAX)) : -1; + if (::poll(entries.data(), entries.size(), timeout) < 0 && errno != EINTR) + throw std::runtime_error("equeue wait: poll failed, guest errno " + std::to_string(NativeError())); #else timespec timeout{static_cast(remaining / 1000000000), static_cast(remaining % 1000000000)}; if (::ppoll(entries.data(), entries.size(), deadlineNanos != 0 ? &timeout : nullptr, nullptr) < 0 && errno != EINTR) @@ -700,6 +704,9 @@ int NativePair(int type, NativeSocket (&pair)[2]) { } #else int NativePair(int type, NativeSocket (&pair)[2]) { +#ifdef __APPLE__ + if (type == 5) throw std::runtime_error("socketpair: SOCK_SEQPACKET is not supported on macOS"); +#endif const int nativeType = type == 1 ? SOCK_STREAM : type == 2 ? SOCK_DGRAM : SOCK_SEQPACKET; int native[2]; if (::socketpair(AF_UNIX, nativeType, 0, native)) return NativeError(); @@ -707,6 +714,15 @@ int NativePair(int type, NativeSocket (&pair)[2]) { pair[1] = native[1]; return 0; } +int QueuedBytes(const Socket& socket, int& count) { +#ifdef __APPLE__ + if (socket.type == 2) { + socklen_t size = sizeof(count); + return ::getsockopt(socket.value, SOL_SOCKET, SO_NREAD, &count, &size); + } +#endif + return ::ioctl(socket.value, FIONREAD, &count); +} #endif int UnnamedAddress(void* address, std::uint32_t* length) { const unsigned char bytes[16] = {16, GuestUnix}; @@ -737,7 +753,7 @@ int GuestSockets::Stat(int descriptor, FileStat* status) { if (ioctlsocket(socket->value, FIONREAD, &count) == 0) available = static_cast(count); #else int count = 0; - if (::ioctl(socket->value, FIONREAD, &count) == 0) available = count; + if (QueuedBytes(*socket, count) == 0) available = count; #endif int sendBuffer = 0; socklen_t length = sizeof(sendBuffer); @@ -776,6 +792,13 @@ bool GuestSockets::Ready(int descriptor, bool write, std::int64_t* data, bool* e #ifdef _WIN32 WSAPOLLFD entry{socket->value, static_cast(write ? POLLWRNORM : POLLRDNORM), 0}; if (WSAPoll(&entry, 1, 0) <= 0) return false; +#elif defined(__APPLE__) + pollfd entry{socket->value, static_cast(write ? POLLOUT : POLLIN), 0}; + if (::poll(&entry, 1, 0) <= 0) return false; + if (!write && socket->type == 1 && (entry.revents & POLLIN)) { + char byte; + if (::recv(socket->value, &byte, 1, MSG_PEEK | MSG_DONTWAIT) == 0) entry.revents |= POLLHUP; + } #else pollfd entry{socket->value, static_cast(write ? POLLOUT : (POLLIN | POLLRDHUP)), 0}; if (::poll(&entry, 1, 0) <= 0) return false; @@ -799,7 +822,7 @@ bool GuestSockets::Ready(int descriptor, bool write, std::int64_t* data, bool* e if (ioctlsocket(socket->value, FIONREAD, &available) == 0) *data = static_cast(available); #else int available = 0; - if (::ioctl(socket->value, FIONREAD, &available) == 0) *data = available; + if (QueuedBytes(*socket, available) == 0) *data = available; #endif } return true; @@ -1216,7 +1239,7 @@ int APS5_VABI ioctl_nid_postfix(int descriptor, std::uint64_t request, void* arg const auto result = ioctlsocket(socket->value, request == 0x8004667e ? FIONBIO : FIONREAD, &value); #else int nativeValue = static_cast(value); - const auto result = ::ioctl(socket->value, request == 0x8004667e ? FIONBIO : FIONREAD, &nativeValue); + const auto result = request == 0x8004667e ? ::ioctl(socket->value, FIONBIO, &nativeValue) : QueuedBytes(*socket, nativeValue); value = static_cast(nativeValue); #endif if (result) return Fail(NativeError()); diff --git a/core/libs/tests/GuestSocketPair.cpp b/core/libs/tests/GuestSocketPair.cpp index 15dabd1ef6..8632f71da4 100644 --- a/core/libs/tests/GuestSocketPair.cpp +++ b/core/libs/tests/GuestSocketPair.cpp @@ -5,6 +5,7 @@ #include #include #include +#include struct PollDescriptor { int descriptor; @@ -46,6 +47,16 @@ int* APS5_VABI __error_nid_postfix(); static void Require(bool value) { if (!value) std::abort(); } +template +static bool ThrowsRuntimeError(TFunction function) { + try { + function(); + } catch (const std::runtime_error&) { + return true; + } + return false; +} + constexpr int Unix = 1; constexpr int Stream = 1; constexpr int Datagram = 2; @@ -170,7 +181,9 @@ int main() { Require(Exchange(pair[1], pair[0], "back")); Require(close_nid_postfix(pair[0]) == 0 && close_nid_postfix(pair[1]) == 0); -#ifndef _WIN32 +#ifdef __APPLE__ + Require(ThrowsRuntimeError([&] { socketpair_nid_postfix(Unix, SequencedPacket, 0, pair); })); +#elif !defined(_WIN32) Require(socketpair_nid_postfix(Unix, SequencedPacket, 0, pair) == 0); Require(send_nid_postfix(pair[0], "a", 1, 0) == 1); Require(send_nid_postfix(pair[0], "bb", 2, 0) == 2); diff --git a/core/libs/tests/GuestUdp.cpp b/core/libs/tests/GuestUdp.cpp index e9ffebd9a7..a2c863bf64 100644 --- a/core/libs/tests/GuestUdp.cpp +++ b/core/libs/tests/GuestUdp.cpp @@ -199,7 +199,7 @@ int main() { Require(recvfrom_nid_postfix(receiver, buffer, sizeof(buffer), 0, nullptr, nullptr) == -1); Require(*__error_nid_postfix() == 35); const char message[] = "guest UDP loopback"; - Require(sendto_nid_postfix(sender, message, sizeof(message), 0x20000, destination.data(), destination.size()) == sizeof(message)); + Require(send_nid_postfix(sender, message, sizeof(message), 0x20000) == sizeof(message)); int queued = 0; for (int i = 0; i < 100 && queued == 0; ++i) { Require(ioctl_nid_postfix(receiver, 0x4004667f, &queued) == 0); @@ -219,7 +219,7 @@ int main() { char head[] = "scatter "; char tail[] = "gather"; Iovec parts[2]{{head, 8}, {tail, 6}}; - Msghdr outgoing{destination.data(), static_cast(destination.size()), parts, 2, nullptr, 0, 0}; + Msghdr outgoing{nullptr, 0, parts, 2, nullptr, 0, 0}; Require(sendmsg_nid_postfix(sender, &outgoing, 0) == 14); const auto wait = [&] { queued = 0; diff --git a/core/libs/tests/LogMacros.cpp b/core/libs/tests/LogMacros.cpp index 9ff2d5c98f..9217c5e8b5 100644 --- a/core/libs/tests/LogMacros.cpp +++ b/core/libs/tests/LogMacros.cpp @@ -38,7 +38,13 @@ std::vector CaptureWrites(TAction action) { }); #else int sockets[2]; +#ifdef __APPLE__ + Require(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) == 0, "message socket pair"); + constexpr int bufferBytes = 1 << 20; + Require(setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferBytes, sizeof(bufferBytes)) == 0 && setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferBytes, sizeof(bufferBytes)) == 0, "message socket buffers"); +#else Require(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0, "message socket pair"); +#endif std::FILE* stream = fdopen(sockets[0], "w"); std::thread reader([&] { std::string buffer(1 << 20, '\0'); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index f2ed9751ba..a6c03d845c 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -168,7 +168,8 @@ - [setrlimit](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) validates the resource and input in FreeBSD order, maps negative limits to infinity, and lowers a soft limit above the hard limit. A request succeeds when its normalized values match `getrlimit`, or for `RLIMIT_NOFILE` when `rlim_max` does not exceed the current hard limit and `rlim_cur` does not exceed `rlim_max`, storing the updated limits for subsequent `getrlimit` queries; descriptor allocation enforcement of the lowered soft limit is not implemented, and changing other limits throws because guest limit state is not stored. Java 25 LAN / DNS (PPSA99869) sets `rlim_cur = MIN(0x100000, rlim_max)` during VM initialization - [execvp](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) throws: replacing the process image is not supported. PPSA85300 imports it and fails to load without it; its `fork` and `vfork` fail with `ENOSYS` ([libc_missing.c](https://github.com/ZiZc3/VLC/blob/f3253020a64368153233ede29183014c0b6c8979/ps5/compat/libc_missing.c#L290-L292)), so the child process that would call it is never created. - [nmount](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) throws: mounting file systems is not supported. Prospero Themes (PPSA90210) imports it and fails to load without it; it opens its theme library without calling it, and its strings (`/dev/ssd0.system_ex`, `/system_ex`, `exfatfs`, `fstype`, `fspath`, `from`, `ignoreacl`) point to remounting the console's `/system_ex` partition to write theme files there. -- [socketpair](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) - the pair is the host's `AF_UNIX` pair on Linux and, on Windows, a loopback TCP connection for `SOCK_STREAM` or two connected UDP sockets for `SOCK_DGRAM`; `SOCK_SEQPACKET` throws on Windows. A datagram sent to a full receiver gets `ENOBUFS` on the console, waits or gets `EAGAIN` on Linux and is dropped on Windows, and on Windows a datagram `recv` after the peer closed reports `ECONNRESET` instead of the console's `ENOTCONN`, and a stream `send` after the peer closed reports `ECONNABORTED` instead of the console's `EPIPE`. `read` and `write` on a pair end go to the host like on every guest socket. `getpeername` on a pair end succeeds after the peer closed, where the console returns `ENOTCONN`, and `connect`, `bind` and `sendto` with an address on a pair end report an address-family error instead of the console's `EISCONN`. On Windows `poll` on a stream end reports `POLLHUP` without `POLLIN` after the peer's `shutdown(SHUT_WR)` or `close`, where the console reports `POLLIN`, with `POLLHUP` only after `close` +- [socketpair](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) - the pair is the host's `AF_UNIX` pair on Linux and macOS and, on Windows, a loopback TCP connection for `SOCK_STREAM` or two connected UDP sockets for `SOCK_DGRAM`; `SOCK_SEQPACKET` throws on Windows and macOS, which has no such `AF_UNIX` pair. A datagram sent to a full receiver gets `ENOBUFS` on the console, waits or gets `EAGAIN` on Linux and is dropped on Windows, and on Windows a datagram `recv` after the peer closed reports `ECONNRESET` instead of the console's `ENOTCONN`, and a stream `send` after the peer closed reports `ECONNABORTED` instead of the console's `EPIPE`. `read` and `write` on a pair end go to the host like on every guest socket. `getpeername` on a pair end succeeds after the peer closed, where the console returns `ENOTCONN`, and `connect`, `bind` and `sendto` with an address on a pair end report an address-family error instead of the console's `EISCONN`. On Windows `poll` on a stream end reports `POLLHUP` without `POLLIN` after the peer's `shutdown(SHUT_WR)` or `close`, where the console reports `POLLIN`, with `POLLHUP` only after `close` +- Guest sockets on macOS ([the equeue wait, 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; `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. - [UDP recv/recvfrom](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) and [sceNetRecv/sceNetRecvfrom](../../../core/libs/prx/libSceNet/Export.cpp) translate Windows WSAEMSGSIZE into the copied byte count for datagram sockets, including MSG_PEEK, and retain the sender address. This follows FreeBSD 11.4 recv(2) and Winsock's documented truncated-datagram behavior; console behavior and existing title impact are unverified. - [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 these calls still fail with `EOPNOTSUPP` for it - [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). From d42fb3130d3c6aac54232a668315f60614e758e5 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Tue, 20 Oct 2026 00:58:17 +0300 Subject: [PATCH 017/523] feat(libSceNpManager): answer the PS4 account queries for a signed-out user sceNpGetAccountCountry, sceNpGetAccountDateOfBirth, sceNpGetAccountLanguage and sceNpGetParentalControlInfo take an online id that matches no user, sceNpGetUserIdByOnlineId and sceNpCheckPlus report NP as signed out, and sceNpNotifyPlusFeature is declared and throws. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceNpManager/Export.cpp | 38 +++++++++++++++++++ core/libs/tests/GuestNpManager.cpp | 47 ++++++++++++++++++++++++ docs/dev/debt/libSceNpManager.md | 4 ++ 3 files changed, 89 insertions(+) diff --git a/core/libs/prx/libSceNpManager/Export.cpp b/core/libs/prx/libSceNpManager/Export.cpp index 17237a6caf..9498a11598 100644 --- a/core/libs/prx/libSceNpManager/Export.cpp +++ b/core/libs/prx/libSceNpManager/Export.cpp @@ -123,6 +123,44 @@ int APS5_VABI sceNpGetAccountAge(int req_id, int user_id, uint8_t* age) { return SCE_NP_ERROR_SIGNED_OUT; } +int APS5_VABI sceNpGetAccountCountry(const NpOnlineId* online_id, void* country_code) { + if (!online_id || !country_code) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_USER_NOT_FOUND; +} + +int APS5_VABI sceNpGetAccountDateOfBirth(const NpOnlineId* online_id, void* date_of_birth) { + if (!online_id || !date_of_birth) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_USER_NOT_FOUND; +} + +int APS5_VABI sceNpGetAccountLanguage(int req_id, const NpOnlineId* online_id, void* language) { + (void)req_id; + if (!online_id || !language) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_USER_NOT_FOUND; +} + +int APS5_VABI sceNpGetParentalControlInfo(int req_id, const NpOnlineId* online_id, int8_t* age, void* info) { + (void)req_id; + if (!online_id || !age || !info) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_USER_NOT_FOUND; +} + +int APS5_VABI sceNpGetUserIdByOnlineId(const NpOnlineId* online_id, int* user_id) { + if (!online_id || !user_id) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_SIGNED_OUT; +} + +int APS5_VABI sceNpCheckPlus(int req_id, const void* param, void* result) { + if (req_id == 0 || !param || !result) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_SIGNED_OUT; +} + +int APS5_VABI sceNpNotifyPlusFeature(const void* param) { + (void)param; + NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI sceNpGetAccountCountryA(int user_id, void* country_code) { (void)user_id; (void)country_code; diff --git a/core/libs/tests/GuestNpManager.cpp b/core/libs/tests/GuestNpManager.cpp index 4fe07d41f6..ede2cb4c68 100644 --- a/core/libs/tests/GuestNpManager.cpp +++ b/core/libs/tests/GuestNpManager.cpp @@ -12,6 +12,13 @@ int APS5_VABI sceNpGetNpId(int user_id, NpId* np_id); int APS5_VABI sceNpGetUserIdByAccountId(std::uint64_t account_id, int* user_id); int APS5_VABI sceNpUnknown_8weiQyIdI3A(int userId, void* buffer); int APS5_VABI sceNpUnknown_WxLcN76q19o(void); +int APS5_VABI sceNpGetAccountCountry(const NpOnlineId* online_id, void* country_code); +int APS5_VABI sceNpGetAccountDateOfBirth(const NpOnlineId* online_id, void* date_of_birth); +int APS5_VABI sceNpGetAccountLanguage(int req_id, const NpOnlineId* online_id, void* language); +int APS5_VABI sceNpGetParentalControlInfo(int req_id, const NpOnlineId* online_id, std::int8_t* age, void* info); +int APS5_VABI sceNpGetUserIdByOnlineId(const NpOnlineId* online_id, int* user_id); +int APS5_VABI sceNpCheckPlus(int req_id, const void* param, void* result); +int APS5_VABI sceNpNotifyPlusFeature(const void* param); } extern "C" { @@ -36,6 +43,7 @@ namespace { constexpr int InvalidArgument = static_cast(0x80550003u); constexpr int SignedOut = static_cast(0x80550006u); +constexpr int UserNotFound = static_cast(0x80550007u); constexpr int AlreadyRegistered = static_cast(0x80550008u); constexpr int NotRegistered = static_cast(0x80550009u); constexpr int RequestNotFound = static_cast(0x80550014u); @@ -200,9 +208,48 @@ void CheckRequestTimeouts() { Require(sceNpSetTimeout(asyncId, 2, 0, 0, 0, 0) == RequestNotFound, "deleted async request"); } +void CheckOnlineIdQueries() { + NpOnlineId onlineId{}; + std::memcpy(onlineId.data, "player", 6); + unsigned char output[64]; + std::memset(output, 0x5a, sizeof(output)); + unsigned char untouched[64]; + std::memset(untouched, 0x5a, sizeof(untouched)); + std::int8_t age = 0x5a; + int userId = 0x5a5a; + Require(sceNpGetAccountCountry(&onlineId, output) == UserNotFound, "sceNpGetAccountCountry must find no user"); + Require(sceNpGetAccountCountry(nullptr, output) == InvalidArgument, "sceNpGetAccountCountry must reject a null online id"); + Require(sceNpGetAccountCountry(&onlineId, nullptr) == InvalidArgument, "sceNpGetAccountCountry must reject a null output"); + Require(sceNpGetAccountDateOfBirth(&onlineId, output) == UserNotFound, "sceNpGetAccountDateOfBirth must find no user"); + Require(sceNpGetAccountDateOfBirth(nullptr, output) == InvalidArgument, "sceNpGetAccountDateOfBirth must reject a null online id"); + Require(sceNpGetAccountDateOfBirth(&onlineId, nullptr) == InvalidArgument, "sceNpGetAccountDateOfBirth must reject a null output"); + Require(sceNpGetAccountLanguage(1, &onlineId, output) == UserNotFound, "sceNpGetAccountLanguage must find no user"); + Require(sceNpGetAccountLanguage(1, nullptr, output) == InvalidArgument, "sceNpGetAccountLanguage must reject a null online id"); + Require(sceNpGetAccountLanguage(1, &onlineId, nullptr) == InvalidArgument, "sceNpGetAccountLanguage must reject a null output"); + Require(sceNpGetParentalControlInfo(1, &onlineId, &age, output) == UserNotFound, "sceNpGetParentalControlInfo must find no user"); + Require(sceNpGetParentalControlInfo(1, nullptr, &age, output) == InvalidArgument, "sceNpGetParentalControlInfo must reject a null online id"); + Require(sceNpGetParentalControlInfo(1, &onlineId, nullptr, output) == InvalidArgument, "sceNpGetParentalControlInfo must reject a null age"); + Require(sceNpGetParentalControlInfo(1, &onlineId, &age, nullptr) == InvalidArgument, "sceNpGetParentalControlInfo must reject a null info"); + Require(sceNpGetUserIdByOnlineId(&onlineId, &userId) == SignedOut, "sceNpGetUserIdByOnlineId must report NP as signed out"); + Require(sceNpGetUserIdByOnlineId(nullptr, &userId) == InvalidArgument, "sceNpGetUserIdByOnlineId must reject a null online id"); + Require(sceNpGetUserIdByOnlineId(&onlineId, nullptr) == InvalidArgument, "sceNpGetUserIdByOnlineId must reject a null output"); + Require(sceNpCheckPlus(1, output, output + 32) == SignedOut, "sceNpCheckPlus must report NP as signed out"); + Require(sceNpCheckPlus(0, output, output + 32) == InvalidArgument, "sceNpCheckPlus must reject request id 0"); + Require(sceNpCheckPlus(1, nullptr, output + 32) == InvalidArgument, "sceNpCheckPlus must reject a null parameter"); + Require(sceNpCheckPlus(1, output, nullptr) == InvalidArgument, "sceNpCheckPlus must reject a null result"); + Require(std::memcmp(output, untouched, sizeof(output)) == 0 && age == 0x5a && userId == 0x5a5a, "a failed query wrote its output"); + try { + sceNpNotifyPlusFeature(output); + Require(false, "sceNpNotifyPlusFeature must report that it is not implemented"); + } catch (const std::runtime_error& error) { + Require(std::string(error.what()) == "sceNpNotifyPlusFeature not implemented", "sceNpNotifyPlusFeature threw"); + } +} + } int main() { + CheckOnlineIdQueries(); CheckStateCallbacksA(); CheckLegacyStateCallback(); CheckRequestTimeouts(); diff --git a/docs/dev/debt/libSceNpManager.md b/docs/dev/debt/libSceNpManager.md index e9f15ca299..d9eb4a88b1 100644 --- a/docs/dev/debt/libSceNpManager.md +++ b/docs/dev/debt/libSceNpManager.md @@ -7,9 +7,13 @@ - [sceNpUnknown_WxLcN76q19o](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager, NID `WxLcN76q19o`) - the name is not in aerolib. PPSA34015 imports it from `libSceNpManager.prx` as a strong `JUMP_SLOT` with no call found in the eboot; the loader still requires the export. Arguments, result and console errors are unknown, so every call throws. - [sceNpSetTimeout](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the signature follows [orbital](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json), [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Np/NpAccount.cs); the rejected values (all five 0, negative retries, a resolve timeout under 1 s or another timeout under 10 s, 0 meaning the default) are shadPS4's, not checked on a console. An id that was never created or was deleted returns `REQUEST_NOT_FOUND`, as in shadPS4; there is no network, so the timeouts are not stored - [sceNpGetUserIdByAccountId](../../../core/libs/prx/libSceNpManager/Export.cpp) - the `(account id, user id output)` signature and its errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_manager.cpp): account id 0 or a null output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output. NP is always signed out here, so no account id resolves to a user +- [sceNpNotifyPlusFeature](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 titles import it; its parameter block and what the console does with it for a signed-out user are unknown ([shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L1170) returns 0 without reading it), so every call throws. ### Functional +- [sceNpGetAccountCountry, sceNpGetAccountDateOfBirth, sceNpGetAccountLanguage, sceNpGetParentalControlInfo](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms taking an online id, not measured on a console. Signatures and checks follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp): a null pointer is `INVALID_ARGUMENT`; no user is signed in, so no online id matches one and the rest return `USER_NOT_FOUND` without writing, like `sceNpCheckNpAvailability`. Request ids are not checked +- [sceNpGetUserIdByOnlineId](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(online id, user id output)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L703): a null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output, as `sceNpGetUserIdByAccountId` does +- [sceNpCheckPlus](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(request id, parameter, result)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L246): request id 0 or a null parameter or result returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and every other call returns `SCE_NP_ERROR_SIGNED_OUT` without writing the result, as `sceNpCheckPremium` does. The parameter's size and feature fields, which shadPS4 checks, are not read: the errors the console returns for them are not known - [NP reachability callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable. - [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests - [NP state callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the registration rules are [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L857-L901)'s: `sceNpRegisterStateCallbackA` keeps up to 8 callbacks and returns the slot plus 1, a null callback returns `INVALID_ARGUMENT`, a callback already registered `CALLBACK_ALREADY_REGISTERED` and a ninth one `CALLBACK_MAX` (0x8055001D, [np_error.h](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_error.h#L38)); `sceNpUnregisterStateCallbackA` returns `INVALID_ARGUMENT` for an id outside 1..8 and `CALLBACK_NOT_REGISTERED` for an empty slot; the legacy [sceNpRegisterStateCallback](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L981-L1005) keeps one callback with the same errors. Each registration gets `SIGNED_OUT` for the initial user (0x10000000) once, from the next `sceNpCheckCallback` on the calling thread. For the A form this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3959-L3974), whose [sceNpRegisterStateCallbackA](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4021-L4034) marks the registration pending. KytyPS5 never calls the legacy callback ([sceNpRegisterStateCallback](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4009-L4019) stores nothing); it is called as `(user, SIGNED_OUT, nullptr, userdata)`, as in shadPS4, which passes an NpId only for `SIGNED_IN` ([L823](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L823), [L938-L940](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L938-L940)). shadPS4 instead queues `SIGNED_OUT` from the user service login event and delivers it from `sceNpCheckCallback` ([DispatchPendingNpStateCallbacks](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L903-L962)); the reachability callback is not given an initial `UNAVAILABLE`, unlike that function ([L922-L961](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L922-L961)). A callback being delivered when another thread unregisters it is still called once. When and how often the console calls the state callbacks, and the errors, are not measured From 6e4c01f91caf296d3b62faac744d8e084f9f8bb4 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Tue, 20 Oct 2026 03:19:49 +0300 Subject: [PATCH 018/523] feat(libSceNpManager): answer the PS4 account id and presence queries for a signed-out user sceNpGetAccountId and sceNpSetGamePresenceOnline report NP as signed out, and sceNpUnregisterPlusEventCallback returns 0 like the premium form. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceNpManager/Export.cpp | 15 +++++++++++++++ core/libs/tests/GuestNpManager.cpp | 13 ++++++++++++- docs/dev/debt/libSceNpManager.md | 2 ++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceNpManager/Export.cpp b/core/libs/prx/libSceNpManager/Export.cpp index 9498a11598..bc722e640a 100644 --- a/core/libs/prx/libSceNpManager/Export.cpp +++ b/core/libs/prx/libSceNpManager/Export.cpp @@ -161,6 +161,17 @@ int APS5_VABI sceNpNotifyPlusFeature(const void* param) { return 0; } +int APS5_VABI sceNpGetAccountId(const NpOnlineId* online_id, uint64_t* account_id) { + if (!online_id || !account_id) return SCE_NP_ERROR_INVALID_ARGUMENT; + return SCE_NP_ERROR_SIGNED_OUT; +} + +int APS5_VABI sceNpSetGamePresenceOnline(int user_id, uint32_t timeout) { + (void)user_id; + (void)timeout; + return SCE_NP_ERROR_SIGNED_OUT; +} + int APS5_VABI sceNpGetAccountCountryA(int user_id, void* country_code) { (void)user_id; (void)country_code; @@ -330,6 +341,10 @@ int APS5_VABI sceNpUnregisterPremiumEventCallback(void) { return 0; } +int APS5_VABI sceNpUnregisterPlusEventCallback(void) { + return 0; +} + int APS5_VABI sceNpGetUserIdByAccountId(uint64_t account_id, int* user_id) { if (account_id == 0 || !user_id) return SCE_NP_ERROR_INVALID_ARGUMENT; return SCE_NP_ERROR_SIGNED_OUT; diff --git a/core/libs/tests/GuestNpManager.cpp b/core/libs/tests/GuestNpManager.cpp index ede2cb4c68..655caac773 100644 --- a/core/libs/tests/GuestNpManager.cpp +++ b/core/libs/tests/GuestNpManager.cpp @@ -19,6 +19,10 @@ int APS5_VABI sceNpGetParentalControlInfo(int req_id, const NpOnlineId* online_i int APS5_VABI sceNpGetUserIdByOnlineId(const NpOnlineId* online_id, int* user_id); int APS5_VABI sceNpCheckPlus(int req_id, const void* param, void* result); int APS5_VABI sceNpNotifyPlusFeature(const void* param); +int APS5_VABI sceNpGetAccountId(const NpOnlineId* online_id, std::uint64_t* account_id); +int APS5_VABI sceNpSetGamePresenceOnline(int user_id, std::uint32_t timeout); +int APS5_VABI sceNpRegisterPlusEventCallback(void* callback, void* userdata); +int APS5_VABI sceNpUnregisterPlusEventCallback(void); } extern "C" { @@ -237,7 +241,14 @@ void CheckOnlineIdQueries() { Require(sceNpCheckPlus(0, output, output + 32) == InvalidArgument, "sceNpCheckPlus must reject request id 0"); Require(sceNpCheckPlus(1, nullptr, output + 32) == InvalidArgument, "sceNpCheckPlus must reject a null parameter"); Require(sceNpCheckPlus(1, output, nullptr) == InvalidArgument, "sceNpCheckPlus must reject a null result"); - Require(std::memcmp(output, untouched, sizeof(output)) == 0 && age == 0x5a && userId == 0x5a5a, "a failed query wrote its output"); + std::uint64_t accountId = 0x5a5a; + Require(sceNpGetAccountId(&onlineId, &accountId) == SignedOut, "sceNpGetAccountId must report NP as signed out"); + Require(sceNpGetAccountId(nullptr, &accountId) == InvalidArgument, "sceNpGetAccountId must reject a null online id"); + Require(sceNpGetAccountId(&onlineId, nullptr) == InvalidArgument, "sceNpGetAccountId must reject a null output"); + Require(sceNpSetGamePresenceOnline(0x10000000, 0) == SignedOut, "sceNpSetGamePresenceOnline must report NP as signed out"); + Require(sceNpRegisterPlusEventCallback(output, nullptr) == 0 && sceNpUnregisterPlusEventCallback() == 0, "the plus event callback must register and unregister"); + Require(sceNpUnregisterPlusEventCallback() == 0, "unregistering the plus event callback again must succeed like the premium one"); + Require(std::memcmp(output, untouched, sizeof(output)) == 0 && age == 0x5a && userId == 0x5a5a && accountId == 0x5a5a, "a failed query wrote its output"); try { sceNpNotifyPlusFeature(output); Require(false, "sceNpNotifyPlusFeature must report that it is not implemented"); diff --git a/docs/dev/debt/libSceNpManager.md b/docs/dev/debt/libSceNpManager.md index d9eb4a88b1..09d9c6857e 100644 --- a/docs/dev/debt/libSceNpManager.md +++ b/docs/dev/debt/libSceNpManager.md @@ -14,6 +14,8 @@ - [sceNpGetAccountCountry, sceNpGetAccountDateOfBirth, sceNpGetAccountLanguage, sceNpGetParentalControlInfo](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms taking an online id, not measured on a console. Signatures and checks follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp): a null pointer is `INVALID_ARGUMENT`; no user is signed in, so no online id matches one and the rest return `USER_NOT_FOUND` without writing, like `sceNpCheckNpAvailability`. Request ids are not checked - [sceNpGetUserIdByOnlineId](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(online id, user id output)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L703): a null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output, as `sceNpGetUserIdByAccountId` does - [sceNpCheckPlus](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(request id, parameter, result)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L246): request id 0 or a null parameter or result returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and every other call returns `SCE_NP_ERROR_SIGNED_OUT` without writing the result, as `sceNpCheckPremium` does. The parameter's size and feature fields, which shadPS4 checks, are not read: the errors the console returns for them are not known +- [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(user id, timeout)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp). A null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`; otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured +- [sceNpUnregisterPlusEventCallback](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - returns 0 whether or not a callback was registered, as `sceNpUnregisterPremiumEventCallback` does: `sceNpRegisterPlusEventCallback` keeps no callback, and a signed-out user gets no plus events - [NP reachability callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable. - [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests - [NP state callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the registration rules are [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L857-L901)'s: `sceNpRegisterStateCallbackA` keeps up to 8 callbacks and returns the slot plus 1, a null callback returns `INVALID_ARGUMENT`, a callback already registered `CALLBACK_ALREADY_REGISTERED` and a ninth one `CALLBACK_MAX` (0x8055001D, [np_error.h](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_error.h#L38)); `sceNpUnregisterStateCallbackA` returns `INVALID_ARGUMENT` for an id outside 1..8 and `CALLBACK_NOT_REGISTERED` for an empty slot; the legacy [sceNpRegisterStateCallback](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L981-L1005) keeps one callback with the same errors. Each registration gets `SIGNED_OUT` for the initial user (0x10000000) once, from the next `sceNpCheckCallback` on the calling thread. For the A form this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3959-L3974), whose [sceNpRegisterStateCallbackA](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4021-L4034) marks the registration pending. KytyPS5 never calls the legacy callback ([sceNpRegisterStateCallback](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4009-L4019) stores nothing); it is called as `(user, SIGNED_OUT, nullptr, userdata)`, as in shadPS4, which passes an NpId only for `SIGNED_IN` ([L823](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L823), [L938-L940](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L938-L940)). shadPS4 instead queues `SIGNED_OUT` from the user service login event and delivers it from `sceNpCheckCallback` ([DispatchPendingNpStateCallbacks](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L903-L962)); the reachability callback is not given an initial `UNAVAILABLE`, unlike that function ([L922-L961](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L922-L961)). A callback being delivered when another thread unregisters it is still called once. When and how often the console calls the state callbacks, and the errors, are not measured From 45cc02c49ca674ed177bd2f7100fe4f1f601de85 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Tue, 20 Oct 2026 03:57:24 +0300 Subject: [PATCH 019/523] fix(relinker): start Windows runtime modules the executable needs when the title loads them A --module-dir module that only the executable needs was initialized by the Windows entry stub with no start arguments, so a later sceKernelLoadStartModule with arguments found it started and its module_start never saw them. Unity's mono-ps4.sprx keeps those arguments for the plugins it loads. Such a module is now deferred like any other runtime module: mapped at startup and started, with its arguments, when the title loads it. Modules another guest module needs are still started at startup, and Linux and macOS are unchanged. Co-Authored-By: Claude Opus 5.5 --- core/relinker/relinker/src/guest/GuestModuleBuilder.cpp | 2 +- core/relinker/relinker/tests/test_executables.py | 6 ++++-- core/relinker/relinker/tests/test_guest_module_dir.py | 7 +++++-- docs/dev/debt/relinker.md | 1 + docs/user/USAGE.md | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp index 9c59dbccd1..89b3cee2f4 100644 --- a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp +++ b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp @@ -261,7 +261,7 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path }; for (const auto& name : neededNames) { const auto found = findGuest(name); - if (found != guestNames.end()) require(found->second); + if (found != guestNames.end() && !windows) require(found->second); } for (const auto provider : executableProviders) require(provider); for (std::size_t index = 0; index < images.size(); ++index) { diff --git a/core/relinker/relinker/tests/test_executables.py b/core/relinker/relinker/tests/test_executables.py index eb61d2b294..585303d42f 100644 --- a/core/relinker/relinker/tests/test_executables.py +++ b/core/relinker/relinker/tests/test_executables.py @@ -7,6 +7,7 @@ import tempfile from test_guest_host_libc import importing from test_guest_intel_trampolines import main_fixture +from test_guest_module_dir import pe_export_names from test_guest_module_directories import module_with_symbol, needed_libraries from test_guest_needed_modules import executable_with_needed @@ -85,8 +86,9 @@ def main(): (case / "Media" / "Modules" / "needed.prx").write_bytes(module_with_symbol(True)) result, output = convert(case, windows, ["--module-dir", "Media/Modules", "--executable", "picker/picker.bin"], executable_with_needed()) if windows: - assert result.returncode == 2 and "Guest modules convert differently for picker/picker.bin" in result.stderr, result.stderr - assert not output.exists() + assert result.returncode == 0, (result.stdout, result.stderr) + assert (output.parent / "picker.picker.bin.exe").exists() + assert "__aps5_guest_initialize" in pe_export_names((output.parent / "app0" / "Media" / "Modules" / "needed.prx.guest.prx").read_bytes()) else: assert result.returncode == 0, (result.stdout, result.stderr) secondary = output.parent / "picker.picker.bin.elf" diff --git a/core/relinker/relinker/tests/test_guest_module_dir.py b/core/relinker/relinker/tests/test_guest_module_dir.py index 74180910de..a81fd97d0e 100644 --- a/core/relinker/relinker/tests/test_guest_module_dir.py +++ b/core/relinker/relinker/tests/test_guest_module_dir.py @@ -192,7 +192,10 @@ def main(): assert result.returncode == 0, (result.stdout, result.stderr) needed = case / "app0" / "Media" / "Modules" / "needed.prx.guest.prx" if windows: - assert "__aps5_guest_initialize" not in pe_export_names(needed.read_bytes()) + assert "__aps5_guest_initialize" in pe_export_names(needed.read_bytes()) + if os.name == "nt": + run = subprocess.run([str(output)], capture_output=True, text=True, timeout=30) + assert run.returncode == 42, (run.returncode, run.stdout, run.stderr) else: assert "$ORIGIN/app0/Media/Modules/needed.prx.guest.prx" in needed_libraries(output.read_bytes()), needed_libraries(output.read_bytes()) @@ -221,7 +224,7 @@ def main(): assert result.returncode == 0, (result.stdout, result.stderr) needed = case / "app0" / "needed.prx.guest.prx" if windows: - assert "__aps5_guest_initialize" not in pe_export_names(needed.read_bytes()) + assert "__aps5_guest_initialize" in pe_export_names(needed.read_bytes()) else: assert "$ORIGIN/app0/needed.prx.guest.prx" in needed_libraries(output.read_bytes()), needed_libraries(output.read_bytes()) diff --git a/docs/dev/debt/relinker.md b/docs/dev/debt/relinker.md index 280df7ff63..384fbd0d1b 100644 --- a/docs/dev/debt/relinker.md +++ b/docs/dev/debt/relinker.md @@ -6,6 +6,7 @@ - Windows [input and output collision checks](../../../core/relinker/main.cpp) use temporary hard links when MinGW reports different permissions for two multiply linked files, as with `.elf` and `.exe` names. The comparison fails if either file is read-only or either parent directory refuses the temporary link, even when the files are distinct. - [Runtime-loaded modules](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) that another module needs are initialized at startup with no start arguments, before the module that needs them is loaded; the console is assumed to start them when that module is loaded. +- [Runtime-loaded modules the executable needs](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) are mapped at startup on Windows and started when the title loads them with `sceKernelLoadStartModule`, with their start arguments, as a Unity player loads `mono-ps4.sprx`. A title that calls such a module before loading it finds its initializers not run; whether the console starts it earlier is not measured. Linux and macOS still start it at startup. - The executable file that [relinker](../../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) generates opens a console when launched, unless it is relinked with `--windows-gui`, which selects the GUI subsystem. A GUI build started from Explorer has no standard output or error handle; the [entry stub](../../../core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp) gives each missing one `anyps5.log` beside the executable, replacing the previous run's file (#3282). Errors about the executable path or about waiting for the previous LoadExec process are written before the log is opened and are lost when the standard error handle is missing. If the log cannot be created, output to the missing handles is lost. - [Guest import registries](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) - declared host imports of bundled modules are recorded with their module name and relocation target, even when another guest module exports the same stripped NID. No-op relocations are omitted. Registries describe import bindings, not implementation correctness or runtime reachability. - Converted Windows main executables [omit `DYNAMIC_BASE`](../../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) by default, not only with `--windows-fixed-base`: on the console the executable lies inside the 40-bit window that the title's own `sceLibcMspaceCreate` checks (#3043). Relocations are kept, so if `0x140000000` is taken Windows relocates the image, possibly above that window. Verified only on Windows 10. diff --git a/docs/user/USAGE.md b/docs/user/USAGE.md index 8cfd7a1f84..9b5c3d93a6 100644 --- a/docs/user/USAGE.md +++ b/docs/user/USAGE.md @@ -75,7 +75,7 @@ All switches are disabled by default. `unused-filter` defaults to `0`; `--rpath` | `--rpath ` | Set the system library search path. Quote `$ORIGIN` to prevent shell expansion, for example `--rpath '$ORIGIN/libs'` in Bash or PowerShell. Linux guest modules require an absolute path or a path beginning with `$ORIGIN`. Windows requires a nonempty ASCII path and supports `$ORIGIN` as the executable directory. | | `--autorun` | Run the output after conversion, print its exit code, and wait for Enter. Adds executable permissions for Linux output. Requires the target OS and prepared runtime layout. | | `--sce-module-path ` | Parent of the module directories, not the module directory itself. Default: input ELF parent; relative input paths resolve from the process working directory. Explicit absolute path: used directly. Explicit relative path (including `.` and `..`): relative to the process working directory. Output path has no effect. Requires one nonempty value; repeats, nonexistent or nondirectory paths, access failures, and combination with `--skip-sce-module` are errors. | -| `--module-dir ` | Relink the ELF modules of a directory under the module parent directory, besides `sce_module`/`sce_modules`/`prx`; outputs keep the relative path. A module the executable or another module needs is initialized at startup, any other when the title loads it with `sceKernelLoadStartModule`, with its start arguments (for example Unity plugins in `Media/Plugins`). Repeatable; `.` is the parent itself, without the input ELF. Conflicts with `--skip-sce-module`. | +| `--module-dir ` | Relink the ELF modules of a directory under the module parent directory, besides `sce_module`/`sce_modules`/`prx`; outputs keep the relative path. A module another module needs is initialized at startup, and on Linux and macOS also one the executable needs; any other when the title loads it with `sceKernelLoadStartModule`, with its start arguments (for example Unity plugins in `Media/Plugins`, or `mono-ps4.sprx` on Windows). Repeatable; `.` is the parent itself, without the input ELF. Conflicts with `--skip-sce-module`. | | `--executable ` | Also convert another executable of the title, which it starts with `sceSystemServiceLoadExec`; the path is relative to the module parent directory, for example `picker/picker.bin`. It is written beside the output, named after the path with `/` replaced by `.` and given the output's extension (`picker.picker.bin.exe`), and uses the same `libs/`, `app0/` and guest modules. It is an error if a guest module converts differently for it. Repeatable. Conflicts with `--skip-sce-module`. | | `--patches ` | Apply the byte patches of this file during conversion, see [game patches](#game-patches). Overrides `relinker-patches.toml` discovery. Conflicts with `--no-patches`. | | `--no-patches` | Do not apply patches, and do not load `relinker-patches.toml` from the input directory. | From ac6ceb0b55938cfd1950fc3fa7bade7f56344401 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 09:33:53 +0300 Subject: [PATCH 020/523] feat(libSceHttp): keep redirect options per object and implement sceHttpGetAutoRedirect Templates, connections and requests now each keep their options, copied from the parent when they are created, so sceHttpSetAutoRedirect is no longer dropped and sceHttpGetAutoRedirect returns it. --- core/libs/prx/libSceHttp/CMakeLists.txt | 9 +++- core/libs/prx/libSceHttp/Export.cpp | 49 +++++++++--------- core/libs/prx/libSceHttp/src/Options.cpp | 55 ++++++++++++++++++++ core/libs/prx/libSceHttp/src/Options.hpp | 16 ++++++ core/libs/tests/GuestHttpOptions.cpp | 66 ++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 25 deletions(-) create mode 100644 core/libs/prx/libSceHttp/src/Options.cpp create mode 100644 core/libs/prx/libSceHttp/src/Options.hpp create mode 100644 core/libs/tests/GuestHttpOptions.cpp diff --git a/core/libs/prx/libSceHttp/CMakeLists.txt b/core/libs/prx/libSceHttp/CMakeLists.txt index 48a098bec6..701f264762 100644 --- a/core/libs/prx/libSceHttp/CMakeLists.txt +++ b/core/libs/prx/libSceHttp/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(libSceHttp SHARED EXCLUDE_FROM_ALL Export.cpp src/Parse.cpp src/Uri.cpp) +add_library(libSceHttp SHARED EXCLUDE_FROM_ALL Export.cpp src/Options.cpp src/Parse.cpp src/Uri.cpp) target_include_directories(libSceHttp PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceHttp) @@ -13,8 +13,13 @@ if(BUILD_TESTING) target_link_libraries(guest_http_response_header_tests PRIVATE libSceHttp libc) configure_windows_unwind(guest_http_response_header_tests) add_test(NAME guest_http_response_header COMMAND guest_http_response_header_tests) + add_test_executable(guest_http_options_tests ${LIBS_INCLUDE_DIR}/tests/GuestHttpOptions.cpp) + target_include_directories(guest_http_options_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_http_options_tests PRIVATE libSceHttp libc) + configure_windows_unwind(guest_http_options_tests) + add_test(NAME guest_http_options COMMAND guest_http_options_tests) if(WIN32) - set_tests_properties(guest_http guest_http_response_header PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(guest_http guest_http_response_header guest_http_options PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 04e5b51e72..d8f71209a8 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -7,6 +7,7 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "prx/libSceHttp/src/HttpErrors.hpp" +#include "prx/libSceHttp/src/Options.hpp" #include #include #include @@ -146,19 +147,21 @@ int APS5_VABI sceHttpCookieFlush(int httpCtxId) { } int APS5_VABI sceHttpCreateConnection(int tmpl_id, const char* server_name, const char* scheme, uint16_t port, int enable_keep_alive) { - (void)tmpl_id; (void)server_name; (void)scheme; (void)port; (void)enable_keep_alive; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpChild(id, tmpl_id); + return id; } int APS5_VABI sceHttpCreateConnectionWithURL(int tmpl_id, const char* url, int enable_keep_alive) { - (void)tmpl_id; (void)url; (void)enable_keep_alive; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpChild(id, tmpl_id); + return id; } int APS5_VABI sceHttpCreateEpoll(int http_ctx_id, HttpEpollHandle* eh) { @@ -173,19 +176,21 @@ int APS5_VABI sceHttpCreateEpoll(int http_ctx_id, HttpEpollHandle* eh) { } int APS5_VABI sceHttpCreateRequest(int conn_id, int method, const char* path, uint64_t content_length) { - (void)conn_id; (void)method; (void)path; (void)content_length; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpChild(id, conn_id); + return id; } int APS5_VABI sceHttpCreateRequestWithURL2(int conn_id, const char* method, const char* url, uint64_t content_length) { - (void)conn_id; (void)method; (void)url; (void)content_length; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpChild(id, conn_id); + return id; } int APS5_VABI sceHttpCreateTemplate(int http_ctx_id, const char* user_agent, int http_ver, int is_auto_proxy_conf) { @@ -193,22 +198,25 @@ int APS5_VABI sceHttpCreateTemplate(int http_ctx_id, const char* user_agent, int (void)user_agent; (void)http_ver; (void)is_auto_proxy_conf; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpTemplate(id); + return id; } int APS5_VABI sceHttpDeleteConnection(int conn_id) { - (void)conn_id; + RemoveHttpObject(conn_id); return 0; } int APS5_VABI sceHttpDeleteRequest(int req_id) { + RemoveHttpObject(req_id); std::lock_guard lock(g_statusMutex); g_statusCallbacks.erase(req_id); return 0; } int APS5_VABI sceHttpDeleteTemplate(int tmpl_id) { - (void)tmpl_id; + RemoveHttpObject(tmpl_id); return 0; } @@ -291,9 +299,7 @@ int APS5_VABI sceHttpSetCookieEnabled(int id, int enable) { } int APS5_VABI sceHttpSetAutoRedirect(int id, int enable) { - (void)id; - (void)enable; - return 0; + return SetHttpOption(id, &HttpOptions::autoRedirect, enable != 0) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpSetConnectTimeOut(int id, uint32_t usec) { @@ -410,11 +416,12 @@ int APS5_VABI sceHttpWaitRequest(HttpEpollHandle eh, HttpNBEvent* nbev, int maxe } int APS5_VABI sceHttpCreateRequestWithURL(int conn_id, int method, const char* url, uint64_t content_length) { - (void)conn_id; (void)method; (void)url; (void)content_length; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpChild(id, conn_id); + return id; } int APS5_VABI sceHttpReadData(int request_id, void* data, size_t size) { @@ -513,11 +520,12 @@ int APS5_VABI sceHttpParseResponseHeader(const char* header, std::size_t headerL } int APS5_VABI sceHttpCreateRequest2(int conn_id, const char* method, const char* path, uint64_t content_length) { - (void)conn_id; (void)method; (void)path; (void)content_length; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); + const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + AddHttpChild(id, conn_id); + return id; } int APS5_VABI sceHttpsEnableOption(int id, uint32_t ssl_flags) { @@ -685,11 +693,6 @@ int APS5_VABI sceHttpGetAuthEnabled(void) { return 0; } -int APS5_VABI sceHttpGetAutoRedirect(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceHttpTrySetNonblock(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceHttp/src/Options.cpp b/core/libs/prx/libSceHttp/src/Options.cpp new file mode 100644 index 0000000000..9d13227a23 --- /dev/null +++ b/core/libs/prx/libSceHttp/src/Options.cpp @@ -0,0 +1,55 @@ +#include "prx/libSceHttp/src/Options.hpp" +#include "prx/libc/include/General.hpp" +#include "prx/libSceHttp/src/HttpErrors.hpp" +#include +#include + +namespace { + +std::mutex g_optionsMutex; +std::unordered_map g_objectOptions; + +} + +void AddHttpTemplate(int templateId) { + std::lock_guard lock(g_optionsMutex); + g_objectOptions[templateId] = HttpOptions{}; +} + +void AddHttpChild(int id, int parentId) { + std::lock_guard lock(g_optionsMutex); + const auto parent = g_objectOptions.find(parentId); + g_objectOptions[id] = parent != g_objectOptions.end() ? parent->second : HttpOptions{}; +} + +void RemoveHttpObject(int id) { + std::lock_guard lock(g_optionsMutex); + g_objectOptions.erase(id); +} + +std::optional FindHttpOptions(int id) { + std::lock_guard lock(g_optionsMutex); + const auto found = g_objectOptions.find(id); + if (found == g_objectOptions.end()) return std::nullopt; + return found->second; +} + +bool SetHttpOption(int id, bool HttpOptions::*option, bool enable) { + std::lock_guard lock(g_optionsMutex); + const auto found = g_objectOptions.find(id); + if (found == g_objectOptions.end()) return false; + found->second.*option = enable; + return true; +} + +extern "C" { + +int APS5_VABI sceHttpGetAutoRedirect(int id, int* enable) { + if (!enable) return ERROR_INVALID_VALUE; + const auto options = FindHttpOptions(id); + if (!options) return ERROR_INVALID_ID; + *enable = options->autoRedirect ? 1 : 0; + return 0; +} + +} diff --git a/core/libs/prx/libSceHttp/src/Options.hpp b/core/libs/prx/libSceHttp/src/Options.hpp new file mode 100644 index 0000000000..ebb8e141ac --- /dev/null +++ b/core/libs/prx/libSceHttp/src/Options.hpp @@ -0,0 +1,16 @@ +#ifndef CORE_LIBS_PRX_LIBSCEHTTP_SRC_OPTIONS_HPP +#define CORE_LIBS_PRX_LIBSCEHTTP_SRC_OPTIONS_HPP + +#include + +struct HttpOptions { + bool autoRedirect = true; +}; + +void AddHttpTemplate(int templateId); +void AddHttpChild(int id, int parentId); +void RemoveHttpObject(int id); +std::optional FindHttpOptions(int id); +bool SetHttpOption(int id, bool HttpOptions::*option, bool enable); + +#endif diff --git a/core/libs/tests/GuestHttpOptions.cpp b/core/libs/tests/GuestHttpOptions.cpp new file mode 100644 index 0000000000..6cd82b643b --- /dev/null +++ b/core/libs/tests/GuestHttpOptions.cpp @@ -0,0 +1,66 @@ +#include "SceTypes.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include + +extern "C" { +int APS5_VABI sceHttpInit_nid_postfix(int, int, std::uint64_t); +int APS5_VABI sceHttpTerm_nid_postfix(int); +int APS5_VABI sceHttpCreateTemplate(int, const char*, int, int); +int APS5_VABI sceHttpDeleteTemplate(int); +int APS5_VABI sceHttpCreateConnectionWithURL(int, const char*, int); +int APS5_VABI sceHttpDeleteConnection(int); +int APS5_VABI sceHttpCreateRequestWithURL(int, int, const char*, std::uint64_t); +int APS5_VABI sceHttpDeleteRequest(int); +int APS5_VABI sceHttpSetAutoRedirect(int, int); +int APS5_VABI sceHttpGetAutoRedirect(int, int*); +} + +namespace { + +constexpr int InvalidId = static_cast(0x80431100u); +constexpr int InvalidValue = static_cast(0x804311FEu); +constexpr int UnknownId = 0x7fff0000; + +void Require(bool value, const char* what) { + if (value) return; + std::fprintf(stderr, "%s\n", what); + std::abort(); +} + +int Get(int (APS5_VABI *getter)(int, int*), int id) { + int value = -1; + Require(getter(id, &value) == 0, "an option getter failed on a live object"); + return value; +} + +void CheckAutoRedirect(int context) { + const int pattern = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + Require(Get(sceHttpGetAutoRedirect, pattern) == 1, "a template does not follow redirects by default"); + Require(sceHttpSetAutoRedirect(pattern, 0) == 0, "sceHttpSetAutoRedirect failed on a template"); + Require(Get(sceHttpGetAutoRedirect, pattern) == 0, "sceHttpGetAutoRedirect does not return what was set"); + const int connection = sceHttpCreateConnectionWithURL(pattern, "http://example.com/", 1); + const int request = sceHttpCreateRequestWithURL(connection, 0, "http://example.com/a", 0); + Require(Get(sceHttpGetAutoRedirect, connection) == 0 && Get(sceHttpGetAutoRedirect, request) == 0, "a connection or request did not take its parent's redirect option"); + Require(sceHttpSetAutoRedirect(request, 7) == 0 && Get(sceHttpGetAutoRedirect, request) == 1, "a nonzero value did not enable redirects"); + Require(Get(sceHttpGetAutoRedirect, connection) == 0, "setting a request changed its connection"); + Require(sceHttpSetAutoRedirect(pattern, 1) == 0 && Get(sceHttpGetAutoRedirect, connection) == 0, "an existing connection followed a later template change"); + Require(sceHttpGetAutoRedirect(request, nullptr) == InvalidValue, "a null output was not refused"); + Require(sceHttpGetAutoRedirect(UnknownId, nullptr) == InvalidValue, "the output pointer is not checked before the id"); + int value = 0; + Require(sceHttpGetAutoRedirect(UnknownId, &value) == InvalidId && sceHttpSetAutoRedirect(UnknownId, 1) == InvalidId, "an unknown id was not refused"); + Require(sceHttpDeleteRequest(request) == 0 && sceHttpGetAutoRedirect(request, &value) == InvalidId, "a deleted request still has options"); + Require(sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); +} + +} + +int main() { + const int context = sceHttpInit_nid_postfix(1, 1, 0x10000); + Require(context > 0, "sceHttpInit failed"); + CheckAutoRedirect(context); + Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); + std::puts("HTTP option tests passed"); + return 0; +} From 585e42bcab525afd77cbcb94ded991679ec6262e Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 09:34:26 +0300 Subject: [PATCH 021/523] feat(libSceHttp): implement sceHttpGetNonblock and the Try variants of the nonblocking switch --- core/libs/prx/libSceHttp/Export.cpp | 23 +++++------------------ core/libs/prx/libSceHttp/src/Options.cpp | 12 ++++++++++++ core/libs/prx/libSceHttp/src/Options.hpp | 1 + core/libs/tests/GuestHttpOptions.cpp | 20 ++++++++++++++++++++ 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index d8f71209a8..3f3714bbec 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -316,9 +316,11 @@ int APS5_VABI sceHttpSetEpoll(int id, HttpEpollHandle eh, void* user_arg) { } int APS5_VABI sceHttpSetNonblock(int id, int enable) { - (void)id; - (void)enable; - return 0; + return SetHttpOption(id, &HttpOptions::nonblock, enable != 0) ? 0 : ERROR_INVALID_ID; +} + +int APS5_VABI sceHttpTrySetNonblock(int id, int enable) { + return sceHttpSetNonblock(id, enable); } int APS5_VABI sceHttpSetRecvTimeOut(int id, uint32_t usec) { @@ -693,21 +695,6 @@ int APS5_VABI sceHttpGetAuthEnabled(void) { return 0; } -int APS5_VABI sceHttpTrySetNonblock(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttpGetNonblock(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttpTryGetNonblock(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceHttpSetEpollId(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceHttp/src/Options.cpp b/core/libs/prx/libSceHttp/src/Options.cpp index 9d13227a23..5986a20404 100644 --- a/core/libs/prx/libSceHttp/src/Options.cpp +++ b/core/libs/prx/libSceHttp/src/Options.cpp @@ -52,4 +52,16 @@ int APS5_VABI sceHttpGetAutoRedirect(int id, int* enable) { return 0; } +int APS5_VABI sceHttpGetNonblock(int id, int* enable) { + if (!enable) return ERROR_INVALID_VALUE; + const auto options = FindHttpOptions(id); + if (!options) return ERROR_INVALID_ID; + *enable = options->nonblock ? 1 : 0; + return 0; +} + +int APS5_VABI sceHttpTryGetNonblock(int id, int* enable) { + return sceHttpGetNonblock(id, enable); +} + } diff --git a/core/libs/prx/libSceHttp/src/Options.hpp b/core/libs/prx/libSceHttp/src/Options.hpp index ebb8e141ac..e16d745b0b 100644 --- a/core/libs/prx/libSceHttp/src/Options.hpp +++ b/core/libs/prx/libSceHttp/src/Options.hpp @@ -5,6 +5,7 @@ struct HttpOptions { bool autoRedirect = true; + bool nonblock = false; }; void AddHttpTemplate(int templateId); diff --git a/core/libs/tests/GuestHttpOptions.cpp b/core/libs/tests/GuestHttpOptions.cpp index 6cd82b643b..43f6e5e541 100644 --- a/core/libs/tests/GuestHttpOptions.cpp +++ b/core/libs/tests/GuestHttpOptions.cpp @@ -15,6 +15,10 @@ int APS5_VABI sceHttpCreateRequestWithURL(int, int, const char*, std::uint64_t); int APS5_VABI sceHttpDeleteRequest(int); int APS5_VABI sceHttpSetAutoRedirect(int, int); int APS5_VABI sceHttpGetAutoRedirect(int, int*); +int APS5_VABI sceHttpSetNonblock(int, int); +int APS5_VABI sceHttpGetNonblock(int, int*); +int APS5_VABI sceHttpTrySetNonblock(int, int); +int APS5_VABI sceHttpTryGetNonblock(int, int*); } namespace { @@ -54,12 +58,28 @@ void CheckAutoRedirect(int context) { Require(sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); } +void CheckNonblock(int context) { + const int pattern = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + Require(Get(sceHttpGetNonblock, pattern) == 0 && Get(sceHttpTryGetNonblock, pattern) == 0, "a template is nonblocking by default"); + Require(sceHttpSetNonblock(pattern, 1) == 0, "sceHttpSetNonblock failed on a template"); + const int connection = sceHttpCreateConnectionWithURL(pattern, "http://example.com/", 1); + Require(Get(sceHttpGetNonblock, connection) == 1, "a connection did not take its template's nonblocking option"); + Require(sceHttpTrySetNonblock(connection, 0) == 0 && Get(sceHttpTryGetNonblock, connection) == 0 && Get(sceHttpGetNonblock, connection) == 0, "sceHttpTrySetNonblock did not change the option sceHttpGetNonblock reads"); + Require(Get(sceHttpGetNonblock, pattern) == 1, "setting a connection changed its template"); + int value = 0; + Require(sceHttpGetNonblock(connection, nullptr) == InvalidValue && sceHttpTryGetNonblock(connection, nullptr) == InvalidValue, "a null output was not refused"); + Require(sceHttpGetNonblock(UnknownId, &value) == InvalidId && sceHttpTryGetNonblock(UnknownId, &value) == InvalidId, "an unknown id was not refused by the getters"); + Require(sceHttpSetNonblock(UnknownId, 1) == InvalidId && sceHttpTrySetNonblock(UnknownId, 1) == InvalidId, "an unknown id was not refused by the setters"); + Require(sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); +} + } int main() { const int context = sceHttpInit_nid_postfix(1, 1, 0x10000); Require(context > 0, "sceHttpInit failed"); CheckAutoRedirect(context); + CheckNonblock(context); Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); std::puts("HTTP option tests passed"); return 0; From 32f0a809be79b707d0e665ca1320f58b46b4f997 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 09:35:20 +0300 Subject: [PATCH 022/523] feat(libSceHttp): implement the Accept-Encoding gzip switches sceHttpSetDefaultAcceptEncodingGZIPEnabled sets the value templates of that context start with; sceHttpSetAcceptEncodingGZIPEnabled and sceHttpGetAcceptEncodingGZIPEnabled set and read it per object. --- core/libs/prx/libSceHttp/Export.cpp | 22 +++++--------- core/libs/prx/libSceHttp/src/Options.cpp | 37 ++++++++++++++++++++++-- core/libs/prx/libSceHttp/src/Options.hpp | 6 +++- core/libs/tests/GuestHttpOptions.cpp | 28 ++++++++++++++++++ 4 files changed, 75 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 3f3714bbec..797c59bc2b 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -194,12 +194,11 @@ int APS5_VABI sceHttpCreateRequestWithURL2(int conn_id, const char* method, cons } int APS5_VABI sceHttpCreateTemplate(int http_ctx_id, const char* user_agent, int http_ver, int is_auto_proxy_conf) { - (void)http_ctx_id; (void)user_agent; (void)http_ver; (void)is_auto_proxy_conf; const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); - AddHttpTemplate(id); + AddHttpTemplate(id, http_ctx_id); return id; } @@ -262,6 +261,7 @@ int APS5_VABI sceHttpInit_nid_postfix(int memid, int ssl_ctx_id, uint64_t pool_s const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); std::lock_guard lock(g_stateMutex); g_contexts.insert(id); + AddHttpContext(id); return id; } @@ -384,6 +384,7 @@ int APS5_VABI sceHttpTerm_nid_postfix(int http_ctx_id) { std::lock_guard lock(g_stateMutex); if (g_contexts.empty()) return ERROR_BEFORE_INIT; if (g_contexts.erase(http_ctx_id) == 0) return ERROR_INVALID_ID; + RemoveHttpContext(http_ctx_id); if (!g_contexts.empty()) return 0; for (auto& [handle, epoll] : g_epolls) epolls.push_back(std::move(epoll)); g_epolls.clear(); @@ -790,19 +791,10 @@ int APS5_VABI sceHttpRequestGetAllHeaders(void) { return 0; } -int APS5_VABI sceHttpSetAcceptEncodingGZIPEnabled(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttpGetAcceptEncodingGZIPEnabled(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int http_ctx_id, int enable) { + std::lock_guard lock(g_stateMutex); + if (g_contexts.empty()) return ERROR_BEFORE_INIT; + return SetHttpContextAcceptEncodingGzip(http_ctx_id, enable != 0) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpSetProxy(void) { diff --git a/core/libs/prx/libSceHttp/src/Options.cpp b/core/libs/prx/libSceHttp/src/Options.cpp index 5986a20404..0f3ab712fd 100644 --- a/core/libs/prx/libSceHttp/src/Options.cpp +++ b/core/libs/prx/libSceHttp/src/Options.cpp @@ -7,13 +7,26 @@ namespace { std::mutex g_optionsMutex; +std::unordered_map g_contextAcceptEncodingGzip; std::unordered_map g_objectOptions; } -void AddHttpTemplate(int templateId) { +void AddHttpContext(int contextId) { std::lock_guard lock(g_optionsMutex); - g_objectOptions[templateId] = HttpOptions{}; + g_contextAcceptEncodingGzip[contextId] = false; +} + +void RemoveHttpContext(int contextId) { + std::lock_guard lock(g_optionsMutex); + g_contextAcceptEncodingGzip.erase(contextId); +} + +void AddHttpTemplate(int templateId, int contextId) { + std::lock_guard lock(g_optionsMutex); + HttpOptions options; + if (const auto context = g_contextAcceptEncodingGzip.find(contextId); context != g_contextAcceptEncodingGzip.end()) options.acceptEncodingGzip = context->second; + g_objectOptions[templateId] = options; } void AddHttpChild(int id, int parentId) { @@ -42,6 +55,14 @@ bool SetHttpOption(int id, bool HttpOptions::*option, bool enable) { return true; } +bool SetHttpContextAcceptEncodingGzip(int contextId, bool enable) { + std::lock_guard lock(g_optionsMutex); + const auto found = g_contextAcceptEncodingGzip.find(contextId); + if (found == g_contextAcceptEncodingGzip.end()) return false; + found->second = enable; + return true; +} + extern "C" { int APS5_VABI sceHttpGetAutoRedirect(int id, int* enable) { @@ -64,4 +85,16 @@ int APS5_VABI sceHttpTryGetNonblock(int id, int* enable) { return sceHttpGetNonblock(id, enable); } +int APS5_VABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* enable) { + if (!enable) return ERROR_INVALID_VALUE; + const auto options = FindHttpOptions(id); + if (!options) return ERROR_INVALID_ID; + *enable = options->acceptEncodingGzip ? 1 : 0; + return 0; +} + +int APS5_VABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int enable) { + return SetHttpOption(id, &HttpOptions::acceptEncodingGzip, enable != 0) ? 0 : ERROR_INVALID_ID; +} + } diff --git a/core/libs/prx/libSceHttp/src/Options.hpp b/core/libs/prx/libSceHttp/src/Options.hpp index e16d745b0b..65bc8f2877 100644 --- a/core/libs/prx/libSceHttp/src/Options.hpp +++ b/core/libs/prx/libSceHttp/src/Options.hpp @@ -6,12 +6,16 @@ struct HttpOptions { bool autoRedirect = true; bool nonblock = false; + bool acceptEncodingGzip = false; }; -void AddHttpTemplate(int templateId); +void AddHttpContext(int contextId); +void RemoveHttpContext(int contextId); +void AddHttpTemplate(int templateId, int contextId); void AddHttpChild(int id, int parentId); void RemoveHttpObject(int id); std::optional FindHttpOptions(int id); bool SetHttpOption(int id, bool HttpOptions::*option, bool enable); +bool SetHttpContextAcceptEncodingGzip(int contextId, bool enable); #endif diff --git a/core/libs/tests/GuestHttpOptions.cpp b/core/libs/tests/GuestHttpOptions.cpp index 43f6e5e541..baad611a7e 100644 --- a/core/libs/tests/GuestHttpOptions.cpp +++ b/core/libs/tests/GuestHttpOptions.cpp @@ -19,6 +19,9 @@ int APS5_VABI sceHttpSetNonblock(int, int); int APS5_VABI sceHttpGetNonblock(int, int*); int APS5_VABI sceHttpTrySetNonblock(int, int); int APS5_VABI sceHttpTryGetNonblock(int, int*); +int APS5_VABI sceHttpSetAcceptEncodingGZIPEnabled(int, int); +int APS5_VABI sceHttpGetAcceptEncodingGZIPEnabled(int, int*); +int APS5_VABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int, int); } namespace { @@ -73,6 +76,29 @@ void CheckNonblock(int context) { Require(sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); } +void CheckAcceptEncodingGzip(int context) { + const int plain = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + Require(Get(sceHttpGetAcceptEncodingGZIPEnabled, plain) == 0, "a template asks for gzip by default"); + Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 1) == 0, "sceHttpSetDefaultAcceptEncodingGZIPEnabled failed"); + const int compressed = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + Require(Get(sceHttpGetAcceptEncodingGZIPEnabled, compressed) == 1, "a new template did not take its context's gzip default"); + Require(Get(sceHttpGetAcceptEncodingGZIPEnabled, plain) == 0, "the context default changed an existing template"); + const int connection = sceHttpCreateConnectionWithURL(compressed, "http://example.com/", 1); + Require(sceHttpSetAcceptEncodingGZIPEnabled(connection, 0) == 0 && Get(sceHttpGetAcceptEncodingGZIPEnabled, connection) == 0, "sceHttpGetAcceptEncodingGZIPEnabled does not return what was set"); + Require(Get(sceHttpGetAcceptEncodingGZIPEnabled, compressed) == 1, "setting a connection changed its template"); + const int other = sceHttpInit_nid_postfix(1, 1, 0x10000); + const int otherTemplate = sceHttpCreateTemplate(other, "anyps5-test", 2, 0); + Require(Get(sceHttpGetAcceptEncodingGZIPEnabled, otherTemplate) == 0, "one context's gzip default reached another context"); + int value = 0; + Require(sceHttpGetAcceptEncodingGZIPEnabled(connection, nullptr) == InvalidValue, "a null output was not refused"); + Require(sceHttpGetAcceptEncodingGZIPEnabled(UnknownId, &value) == InvalidId && sceHttpSetAcceptEncodingGZIPEnabled(UnknownId, 1) == InvalidId, "an unknown id was not refused"); + Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(UnknownId, 1) == InvalidId, "an unknown context was not refused"); + Require(sceHttpDeleteTemplate(otherTemplate) == 0 && sceHttpTerm_nid_postfix(other) == 0, "the second context did not close"); + Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(other, 1) == InvalidId, "a closed context still takes a gzip default"); + Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 0) == 0, "the gzip default could not be reset"); + Require(sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(compressed) == 0 && sceHttpDeleteTemplate(plain) == 0, "delete failed"); +} + } int main() { @@ -80,7 +106,9 @@ int main() { Require(context > 0, "sceHttpInit failed"); CheckAutoRedirect(context); CheckNonblock(context); + CheckAcceptEncodingGzip(context); Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); + Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 1) == static_cast(0x80431001u), "a gzip default was taken before sceHttpInit"); std::puts("HTTP option tests passed"); return 0; } From 25255781796d68738207519b3bd90e6b95fc636b Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 09:36:03 +0300 Subject: [PATCH 023/523] feat(libSceHttp): implement sceHttpGetEpoll sceHttpSetEpoll and sceHttpUnsetEpoll now keep and clear the object's epoll and argument, which sceHttpGetEpoll returns. --- core/libs/prx/libSceHttp/Export.cpp | 13 ++--------- core/libs/prx/libSceHttp/src/Options.cpp | 18 +++++++++++++++ core/libs/prx/libSceHttp/src/Options.hpp | 4 ++++ core/libs/tests/GuestHttpOptions.cpp | 28 ++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 797c59bc2b..e1e0dff780 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -309,10 +309,7 @@ int APS5_VABI sceHttpSetConnectTimeOut(int id, uint32_t usec) { } int APS5_VABI sceHttpSetEpoll(int id, HttpEpollHandle eh, void* user_arg) { - (void)id; - (void)eh; - (void)user_arg; - return 0; + return SetHttpEpollBinding(id, eh, user_arg) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpSetNonblock(int id, int enable) { @@ -394,8 +391,7 @@ int APS5_VABI sceHttpTerm_nid_postfix(int http_ctx_id) { } int APS5_VABI sceHttpUnsetEpoll(int id) { - (void)id; - return 0; + return SetHttpEpollBinding(id, nullptr, nullptr) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpWaitRequest(HttpEpollHandle eh, HttpNBEvent* nbev, int maxevents, int timeout) { @@ -706,11 +702,6 @@ int APS5_VABI sceHttpGetEpollId(void) { return 0; } -int APS5_VABI sceHttpGetEpoll(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceHttpGetCookie(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceHttp/src/Options.cpp b/core/libs/prx/libSceHttp/src/Options.cpp index 0f3ab712fd..69444d2f62 100644 --- a/core/libs/prx/libSceHttp/src/Options.cpp +++ b/core/libs/prx/libSceHttp/src/Options.cpp @@ -63,6 +63,15 @@ bool SetHttpContextAcceptEncodingGzip(int contextId, bool enable) { return true; } +bool SetHttpEpollBinding(int id, HttpEpollHandle epoll, void* argument) { + std::lock_guard lock(g_optionsMutex); + const auto found = g_objectOptions.find(id); + if (found == g_objectOptions.end()) return false; + found->second.epoll = epoll; + found->second.epollArgument = argument; + return true; +} + extern "C" { int APS5_VABI sceHttpGetAutoRedirect(int id, int* enable) { @@ -97,4 +106,13 @@ int APS5_VABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int enable) { return SetHttpOption(id, &HttpOptions::acceptEncodingGzip, enable != 0) ? 0 : ERROR_INVALID_ID; } +int APS5_VABI sceHttpGetEpoll(int id, HttpEpollHandle* epoll, void** argument) { + if (!epoll) return ERROR_INVALID_VALUE; + const auto options = FindHttpOptions(id); + if (!options) return ERROR_INVALID_ID; + *epoll = options->epoll; + if (argument) *argument = options->epollArgument; + return 0; +} + } diff --git a/core/libs/prx/libSceHttp/src/Options.hpp b/core/libs/prx/libSceHttp/src/Options.hpp index 65bc8f2877..998fbd022a 100644 --- a/core/libs/prx/libSceHttp/src/Options.hpp +++ b/core/libs/prx/libSceHttp/src/Options.hpp @@ -1,12 +1,15 @@ #ifndef CORE_LIBS_PRX_LIBSCEHTTP_SRC_OPTIONS_HPP #define CORE_LIBS_PRX_LIBSCEHTTP_SRC_OPTIONS_HPP +#include "SceTypes.hpp" #include struct HttpOptions { bool autoRedirect = true; bool nonblock = false; bool acceptEncodingGzip = false; + HttpEpollHandle epoll = nullptr; + void* epollArgument = nullptr; }; void AddHttpContext(int contextId); @@ -17,5 +20,6 @@ void RemoveHttpObject(int id); std::optional FindHttpOptions(int id); bool SetHttpOption(int id, bool HttpOptions::*option, bool enable); bool SetHttpContextAcceptEncodingGzip(int contextId, bool enable); +bool SetHttpEpollBinding(int id, HttpEpollHandle epoll, void* argument); #endif diff --git a/core/libs/tests/GuestHttpOptions.cpp b/core/libs/tests/GuestHttpOptions.cpp index baad611a7e..57be226ca3 100644 --- a/core/libs/tests/GuestHttpOptions.cpp +++ b/core/libs/tests/GuestHttpOptions.cpp @@ -22,6 +22,11 @@ int APS5_VABI sceHttpTryGetNonblock(int, int*); int APS5_VABI sceHttpSetAcceptEncodingGZIPEnabled(int, int); int APS5_VABI sceHttpGetAcceptEncodingGZIPEnabled(int, int*); int APS5_VABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int, int); +int APS5_VABI sceHttpCreateEpoll(int, HttpEpollHandle*); +int APS5_VABI sceHttpDestroyEpoll(int, HttpEpollHandle); +int APS5_VABI sceHttpSetEpoll(int, HttpEpollHandle, void*); +int APS5_VABI sceHttpUnsetEpoll(int); +int APS5_VABI sceHttpGetEpoll(int, HttpEpollHandle*, void**); } namespace { @@ -99,6 +104,28 @@ void CheckAcceptEncodingGzip(int context) { Require(sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(compressed) == 0 && sceHttpDeleteTemplate(plain) == 0, "delete failed"); } +void CheckEpoll(int context) { + HttpEpollHandle epoll = nullptr; + Require(sceHttpCreateEpoll(context, &epoll) == 0 && epoll != nullptr, "sceHttpCreateEpoll failed"); + const int pattern = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + const int connection = sceHttpCreateConnectionWithURL(pattern, "http://example.com/", 1); + const int request = sceHttpCreateRequestWithURL(connection, 0, "http://example.com/a", 0); + int marker = 0; + HttpEpollHandle bound = epoll; + void* argument = ▮ + Require(sceHttpGetEpoll(request, &bound, &argument) == 0 && bound == nullptr && argument == nullptr, "a new request is bound to an epoll"); + Require(sceHttpSetEpoll(request, epoll, &marker) == 0, "sceHttpSetEpoll failed"); + Require(sceHttpGetEpoll(request, &bound, &argument) == 0 && bound == epoll && argument == &marker, "sceHttpGetEpoll does not return the binding that was set"); + bound = nullptr; + Require(sceHttpGetEpoll(request, &bound, nullptr) == 0 && bound == epoll, "sceHttpGetEpoll needs the argument output"); + Require(sceHttpGetEpoll(connection, &bound, &argument) == 0 && bound == nullptr, "binding a request bound its connection"); + Require(sceHttpUnsetEpoll(request) == 0 && sceHttpGetEpoll(request, &bound, &argument) == 0 && bound == nullptr && argument == nullptr, "sceHttpUnsetEpoll left the binding"); + Require(sceHttpGetEpoll(request, nullptr, &argument) == InvalidValue, "a null epoll output was not refused"); + Require(sceHttpGetEpoll(UnknownId, &bound, &argument) == InvalidId && sceHttpSetEpoll(UnknownId, epoll, nullptr) == InvalidId && sceHttpUnsetEpoll(UnknownId) == InvalidId, "an unknown id was not refused"); + Require(sceHttpDeleteRequest(request) == 0 && sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); + Require(sceHttpDestroyEpoll(context, epoll) == 0, "sceHttpDestroyEpoll failed"); +} + } int main() { @@ -107,6 +134,7 @@ int main() { CheckAutoRedirect(context); CheckNonblock(context); CheckAcceptEncodingGzip(context); + CheckEpoll(context); Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 1) == static_cast(0x80431001u), "a gzip default was taken before sceHttpInit"); std::puts("HTTP option tests passed"); From c0f8251a4b0420e169b9feec2888d36488c166c3 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 09:37:17 +0300 Subject: [PATCH 024/523] feat(libSceHttp): accept the redirect cache, private SSL and forced abort calls Without a network these change nothing, so they check their arguments and return 0, and the debt file lists them as silent stubs. --- core/libs/prx/libSceHttp/Export.cpp | 24 ++++-------------------- core/libs/prx/libSceHttp/src/Options.cpp | 17 +++++++++++++++++ core/libs/tests/GuestHttpOptions.cpp | 23 +++++++++++++++++++++++ docs/dev/debt/libSceHttp.md | 1 + 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index e1e0dff780..2781b783c0 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -124,6 +124,10 @@ int APS5_VABI sceHttpAbortRequest(int request_id) { return 0; } +int APS5_VABI sceHttpAbortRequestForce(int request_id) { + return sceHttpAbortRequest(request_id); +} + int APS5_VABI sceHttpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) { (void)id; (void)name; @@ -672,11 +676,6 @@ int APS5_VABI sceHttpDbgGetRequestStat(void) { return 0; } -int APS5_VABI sceHttpAbortRequestForce(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceHttpAddRequestHeaderRaw(void) { NotImplemented_nid_no_patch(__func__); return 0; @@ -757,16 +756,6 @@ int APS5_VABI sceHttpGetCookieStats(void) { return 0; } -int APS5_VABI sceHttpsEnableOptionPrivate(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttpsDisableOptionPrivate(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceHttpSetPriorityOption(void) { NotImplemented_nid_no_patch(__func__); return 0; @@ -823,9 +812,4 @@ int APS5_VABI sceHttpSetDelayBuildRequestEnabled(void) { return 0; } -int APS5_VABI sceHttpCacheRedirectedConnectionEnabled(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - } diff --git a/core/libs/prx/libSceHttp/src/Options.cpp b/core/libs/prx/libSceHttp/src/Options.cpp index 69444d2f62..6889908fa4 100644 --- a/core/libs/prx/libSceHttp/src/Options.cpp +++ b/core/libs/prx/libSceHttp/src/Options.cpp @@ -1,11 +1,14 @@ #include "prx/libSceHttp/src/Options.hpp" #include "prx/libc/include/General.hpp" #include "prx/libSceHttp/src/HttpErrors.hpp" +#include #include #include namespace { +constexpr std::uint32_t PrivateSslFlags = 0x2dffu; + std::mutex g_optionsMutex; std::unordered_map g_contextAcceptEncodingGzip; std::unordered_map g_objectOptions; @@ -115,4 +118,18 @@ int APS5_VABI sceHttpGetEpoll(int id, HttpEpollHandle* epoll, void** argument) { return 0; } +int APS5_VABI sceHttpCacheRedirectedConnectionEnabled(int id, int enable) { + (void)enable; + return FindHttpOptions(id) ? 0 : ERROR_INVALID_ID; +} + +int APS5_VABI sceHttpsEnableOptionPrivate(int id, std::uint32_t sslFlags) { + if ((sslFlags & ~PrivateSslFlags) != 0) return ERROR_INVALID_VALUE; + return FindHttpOptions(id) ? 0 : ERROR_INVALID_ID; +} + +int APS5_VABI sceHttpsDisableOptionPrivate(int id, std::uint32_t sslFlags) { + return sceHttpsEnableOptionPrivate(id, sslFlags); +} + } diff --git a/core/libs/tests/GuestHttpOptions.cpp b/core/libs/tests/GuestHttpOptions.cpp index 57be226ca3..775e0701fa 100644 --- a/core/libs/tests/GuestHttpOptions.cpp +++ b/core/libs/tests/GuestHttpOptions.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { int APS5_VABI sceHttpInit_nid_postfix(int, int, std::uint64_t); @@ -27,6 +28,10 @@ int APS5_VABI sceHttpDestroyEpoll(int, HttpEpollHandle); int APS5_VABI sceHttpSetEpoll(int, HttpEpollHandle, void*); int APS5_VABI sceHttpUnsetEpoll(int); int APS5_VABI sceHttpGetEpoll(int, HttpEpollHandle*, void**); +int APS5_VABI sceHttpCacheRedirectedConnectionEnabled(int, int); +int APS5_VABI sceHttpsEnableOptionPrivate(int, std::uint32_t); +int APS5_VABI sceHttpsDisableOptionPrivate(int, std::uint32_t); +int APS5_VABI sceHttpAbortRequestForce(int); } namespace { @@ -126,6 +131,23 @@ void CheckEpoll(int context) { Require(sceHttpDestroyEpoll(context, epoll) == 0, "sceHttpDestroyEpoll failed"); } +void CheckUnobservedOptions(int context) { + const int pattern = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + const int connection = sceHttpCreateConnectionWithURL(pattern, "http://example.com/", 1); + const int request = sceHttpCreateRequestWithURL(connection, 0, "http://example.com/a", 0); + Require(sceHttpCacheRedirectedConnectionEnabled(pattern, 0) == 0 && sceHttpCacheRedirectedConnectionEnabled(request, 1) == 0, "sceHttpCacheRedirectedConnectionEnabled refused a live object"); + Require(sceHttpCacheRedirectedConnectionEnabled(UnknownId, 1) == InvalidId, "sceHttpCacheRedirectedConnectionEnabled took an unknown id"); + for (const auto option : {sceHttpsEnableOptionPrivate, sceHttpsDisableOptionPrivate}) { + Require(option(connection, 0x2dffu) == 0 && option(connection, 0) == 0, "a private SSL option within the mask was refused"); + Require(option(connection, 0x0100u) == 0 && option(connection, 0x2000u) == 0, "a private-only SSL flag was refused"); + Require(option(connection, 0x0200u) == InvalidValue && option(connection, 0x4000u) == InvalidValue, "an SSL flag outside the private mask was taken"); + Require(option(UnknownId, 0x0200u) == InvalidValue, "the SSL flags are not checked before the id"); + Require(option(UnknownId, 0x01u) == InvalidId, "a private SSL option took an unknown id"); + } + Require(sceHttpAbortRequestForce(request) == 0, "sceHttpAbortRequestForce failed on a live request"); + Require(sceHttpDeleteRequest(request) == 0 && sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); +} + } int main() { @@ -135,6 +157,7 @@ int main() { CheckNonblock(context); CheckAcceptEncodingGzip(context); CheckEpoll(context); + CheckUnobservedOptions(context); Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 1) == static_cast(0x80431001u), "a gzip default was taken before sceHttpInit"); std::puts("HTTP option tests passed"); diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 60c619625d..0df90f562a 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -3,6 +3,7 @@ ### Silent stubs - [libSceHttp](../../../core/libs/prx/libSceHttp/Export.cpp) - no request reaches the network, so `sceHttpSetResponseHeaderMaxSize` has no response header to limit, `sceHttpSetRecvBlockSize` (`(int id, u32 blockSize)` as in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.h)) no response body to read in blocks and `sceHttpRedirectCacheFlush` no redirect to forget; `sceHttpsUnloadCert` returns success like `sceHttpsLoadCert`, which keeps no certificate; `sceHttpSetRedirectCallback` and `sceHttpSetCookieRecvCallback` accept callbacks that are never called, `sceHttpsSetSslVersion` has no TLS connection to configure and `sceHttpsGetSslError` reports error 0 with detail 0; `sceHttpSetAuthInfoCallback` accepts a callback that is never called, as no server asks for authentication; `sceHttpsGetCaList` and `sceHttpsFreeCaList` report an empty CA list (`certs` null, `certsNum` 0) and return 0, as no CA store is kept, and a null list returns `ERROR_INVALID_VALUE`; unlike shadPS4 they do not check that `http_ctx_id` is a live context nor that the library is initialised, and `sceHttpsFreeCaList` also nulls `certs`, which shadPS4 leaves untouched. The signatures and the `{ void* certs; int certsNum; }` layout follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.h) (PS4), not confirmed on a PS5 title +- [HTTP options without a network](../../../core/libs/prx/libSceHttp/src/Options.cpp) (libSceHttp) - `sceHttpCacheRedirectedConnectionEnabled`, `sceHttpsEnableOptionPrivate` and `sceHttpsDisableOptionPrivate` return 0 for a live template, connection or request and keep nothing, since no request reaches the network; the private options refuse flags outside `0x2dff` with `ERROR_INVALID_VALUE` before they look at the id, and `sceHttpAbortRequestForce` does what `sceHttpAbortRequest` does. Signatures, the mask and the order of the checks follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/network/http.cpp) (PS4), not measured on a PS5 ### Unknown function info From 0a69b383d11a2a89b56d9226f1f0f4a80b820a28 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 09:37:28 +0300 Subject: [PATCH 025/523] docs(debt): note where the HTTP option defaults come from --- docs/dev/debt/libSceHttp.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 0df90f562a..afb4cdf0cc 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -7,6 +7,7 @@ ### Unknown function info +- [HTTP object options](../../../core/libs/prx/libSceHttp/src/Options.cpp) (libSceHttp) - a template starts with redirects followed, blocking calls and no `Accept-Encoding: gzip`, a connection or request copies its parent's options when it is created, and `sceHttpGetAutoRedirect`, `sceHttpGetNonblock`, `sceHttpGetAcceptEncodingGZIPEnabled` and `sceHttpGetEpoll` read them back; these defaults and the copying follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/network/http.cpp) (PS4) and are not measured on a PS5. `sceHttpSetDefaultAcceptEncodingGZIPEnabled` sets the gzip value for templates created later in that context, where shadPS4 keeps one value for the library, and the option setters now return `ERROR_INVALID_ID` for an id that is not a live template, connection or request. The options change no request, as none reaches the network - [sceHttpUriBuild](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) retains `//` for a non-opaque URI independently of the selected fields, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a8a70da595da2a96e0410309472ba4a7eb558144/src/core/libraries/network/http.cpp#L3551-L3558). Black Myth: Wukong builds its `Scheme` header with option 1 and expects `http://` or `https://`. Other partial field combinations follow the PS4 reference and remain unverified on PS5 hardware. - [sceHttpSetCookieEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) - the two-argument signature and 0/1 enable values follow [VitaSDK](https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h) and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp); PPSA01521 passes 0 to disable cookies. Disabling succeeds because the offline HTTP backend has no cookie engine; enabling throws. Invalid enable values use the same error as `sceHttpSetInflateGZIPEnabled`. Like the existing offline HTTP setters, the handle is not validated; requests still fail with the network error. Console behavior is unverified - [sceHttpParseResponseHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); parsing and error codes follow [shadPS4's reverse-engineered PS4 library](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp), not confirmed on a PS5 title or hardware. Folded values retain their line breaks and whitespace; an empty first line starts the value scan after its line feed. A consumed byte count above `INT_MAX` throws instead of wrapping. From fa916ae7bcfc5d03ccf4527c7d3b205637d8d503 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 10:11:23 +0300 Subject: [PATCH 026/523] feat(libSceHmd): return the distortion and reprojection buffer sizes and alignments --- core/libs/prx/libSceHmd/Export.cpp | 18 ++++++------------ core/libs/prx/libSceHmd/Hmd.hpp | 6 ++++++ core/libs/tests/GuestHmd.cpp | 10 ++++++++++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 0112025f52..e90bf60b5b 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -120,8 +120,7 @@ int APS5_VABI sceHmdInternalGetDebugTextMode(void) { } int APS5_VABI sceHmdDistortionGetWorkMemoryAlign(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 0x400; } int APS5_VABI sceHmdInternalRegisterEventCallback(void) { @@ -130,8 +129,7 @@ int APS5_VABI sceHmdInternalRegisterEventCallback(void) { } int APS5_VABI sceHmdDistortionGetWorkMemorySize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 0x20000; } int APS5_VABI sceHmdReprojectionStartWideNear(void) { @@ -340,8 +338,7 @@ int APS5_VABI sceHmdDistortionGetCorrectionCommand(void) { } int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 0x100; } int APS5_VABI sceHmdInternalGetDebugMode(void) { @@ -450,8 +447,7 @@ int APS5_VABI sceHmdInternalDfuClose(void) { } int APS5_VABI sceHmdReprojectionQueryGarlicBuffAlign(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 0x100; } int APS5_VABI sceHmdInternalSetHmuPowerControl(void) { @@ -680,8 +676,7 @@ int APS5_VABI sceHmdInternalResetLedForVrTracker(void) { } int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 0x810; } int APS5_VABI sceHmdReprojectionStartWithOverlay(void) { @@ -855,8 +850,7 @@ int APS5_VABI sceHmdInternalGetPuRevision(void) { } int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return 0x100000; } int APS5_VABI sceHmdGet2dVrCommand(void) { diff --git a/core/libs/prx/libSceHmd/Hmd.hpp b/core/libs/prx/libSceHmd/Hmd.hpp index cfbb52bff6..2daaf92e9b 100644 --- a/core/libs/prx/libSceHmd/Hmd.hpp +++ b/core/libs/prx/libSceHmd/Hmd.hpp @@ -63,6 +63,12 @@ std::int32_t APS5_VABI sceHmdGetDeviceInformationByHandle(std::int32_t handle, Hmd::DeviceInformation* info); std::int32_t APS5_VABI sceHmdInternalGetDeviceStatus(Hmd::DeviceStatus* status); std::int32_t APS5_VABI sceHmdInternalMmapIsConnect(); +int APS5_VABI sceHmdDistortionGetWorkMemoryAlign(void); +int APS5_VABI sceHmdDistortionGetWorkMemorySize(void); +int APS5_VABI sceHmdReprojectionQueryGarlicBuffAlign(void); +int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void); +int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void); +int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void); } #endif diff --git a/core/libs/tests/GuestHmd.cpp b/core/libs/tests/GuestHmd.cpp index 6b97bea9cc..6cdaa32c4e 100644 --- a/core/libs/tests/GuestHmd.cpp +++ b/core/libs/tests/GuestHmd.cpp @@ -22,6 +22,15 @@ void CheckDisconnected() { Require(sceHmdInternalMmapIsConnect() == 0, "connection reported present"); } +void CheckBufferQueries() { + Require(sceHmdDistortionGetWorkMemoryAlign() == 0x400, "distortion work memory alignment"); + Require(sceHmdDistortionGetWorkMemorySize() == 0x20000, "distortion work memory size"); + Require(sceHmdReprojectionQueryGarlicBuffAlign() == 0x100, "reprojection garlic buffer alignment"); + Require(sceHmdReprojectionQueryGarlicBuffSize() == 0x100000, "reprojection garlic buffer size"); + Require(sceHmdReprojectionQueryOnionBuffAlign() == 0x100, "reprojection onion buffer alignment"); + Require(sceHmdReprojectionQueryOnionBuffSize() == 0x810, "reprojection onion buffer size"); +} + } int main() { @@ -31,6 +40,7 @@ int main() { const auto untouched = info; CheckDisconnected(); + CheckBufferQueries(); Require(sceHmdTerminate() == Hmd::NotInitialized, "terminate before initialize"); Require(sceHmdOpen(1, 0, 0, nullptr) == Hmd::NotInitialized, "open before initialize"); Require(sceHmdClose(0) == Hmd::NotInitialized, "close before initialize"); From fe6aba89a827d1829384c9cd2fbcd57826e34f2d Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 10:12:03 +0300 Subject: [PATCH 027/523] feat(libSceHmd): refuse the field of view, eye offset and sensor queries without a headset No handle is ever open, as sceHmdOpen reports the headset disconnected, so they return HANDLE_INVALID after initialization and NOT_INITIALIZED before it, without writing their outputs. --- core/libs/prx/libSceHmd/Export.cpp | 23 ++++++++++++++--------- core/libs/prx/libSceHmd/Hmd.hpp | 3 +++ core/libs/tests/GuestHmd.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index e90bf60b5b..6b533528b0 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -262,9 +262,11 @@ int APS5_VABI sceHmdReprojectionInitializeCapture(void) { return 0; } -int APS5_VABI sceHmdGet2DEyeOffset(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset) { + (void)handle; + (void)leftOffset; + (void)rightOffset; + return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } int APS5_VABI sceHmdInternalSetDebugSocialScreenMode(void) { @@ -376,9 +378,10 @@ int APS5_VABI sceHmdInternalSetM2LedOn(void) { return 0; } -int APS5_VABI sceHmdGetFieldOfView(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView) { + (void)handle; + if (!fieldOfView) return Hmd::ParameterNull; + return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } int APS5_VABI sceHmdReprojectionAddDisplayBuffer(void) { @@ -764,9 +767,11 @@ int APS5_VABI sceHmdInternalSetDefaultLedData(void) { return 0; } -int APS5_VABI sceHmdGetInertialSensorData(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count) { + (void)handle; + (void)data; + (void)count; + return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } int APS5_VABI sceHmdInternal3dAudioClose(void) { diff --git a/core/libs/prx/libSceHmd/Hmd.hpp b/core/libs/prx/libSceHmd/Hmd.hpp index 2daaf92e9b..ee11c14431 100644 --- a/core/libs/prx/libSceHmd/Hmd.hpp +++ b/core/libs/prx/libSceHmd/Hmd.hpp @@ -69,6 +69,9 @@ int APS5_VABI sceHmdReprojectionQueryGarlicBuffAlign(void); int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void); int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void); int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void); +int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView); +int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset); +int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count); } #endif diff --git a/core/libs/tests/GuestHmd.cpp b/core/libs/tests/GuestHmd.cpp index 6cdaa32c4e..a7bbe47dcd 100644 --- a/core/libs/tests/GuestHmd.cpp +++ b/core/libs/tests/GuestHmd.cpp @@ -31,6 +31,28 @@ void CheckBufferQueries() { Require(sceHmdReprojectionQueryOnionBuffSize() == 0x810, "reprojection onion buffer size"); } +void CheckHandleQueries(std::int32_t expected) { + unsigned char left[16]; + unsigned char right[16]; + unsigned char fieldOfView[16]; + unsigned char sensor[64]; + std::memset(left, 0x5a, sizeof(left)); + std::memset(right, 0x5a, sizeof(right)); + std::memset(fieldOfView, 0x5a, sizeof(fieldOfView)); + std::memset(sensor, 0x5a, sizeof(sensor)); + for (const auto handle : {0, -1, 1, 0x0f000000}) { + Require(sceHmdGetFieldOfView(handle, nullptr) == Hmd::ParameterNull, "null field of view accepted"); + Require(sceHmdGetFieldOfView(handle, fieldOfView) == expected, "field of view without a headset"); + Require(sceHmdGet2DEyeOffset(handle, left, right) == expected, "eye offset without a headset"); + Require(sceHmdGet2DEyeOffset(handle, nullptr, nullptr) == expected, "eye offset with null outputs"); + Require(sceHmdGetInertialSensorData(handle, sensor, 1) == expected, "sensor data without a headset"); + } + for (const auto* bytes : {left, right, fieldOfView}) { + for (std::size_t index = 0; index < 16; ++index) Require(bytes[index] == 0x5a, "a refused query wrote its output"); + } + for (const auto byte : sensor) Require(byte == 0x5a, "a refused sensor query wrote its output"); +} + } int main() { @@ -47,6 +69,7 @@ int main() { Require(sceHmdGetDeviceInformation(nullptr) == Hmd::ParameterNull, "null information"); Require(sceHmdGetDeviceInformation(&info) == Hmd::NotInitialized, "query before initialize"); Require(std::memcmp(&info, &untouched, sizeof(info)) == 0, "failed query modified output"); + CheckHandleQueries(Hmd::NotInitialized); Require(sceHmdGetDeviceInformationByHandle(0, &info) == Hmd::NotInitialized, "handle query before initialize"); Require(sceHmdInitialize(nullptr) == Hmd::ParameterNull, "null initialize"); @@ -83,6 +106,7 @@ int main() { "query accepted invalid handle"); Require(std::memcmp(&info, &expected, sizeof(info)) == 0, "invalid handle modified output"); } + CheckHandleQueries(Hmd::InvalidHandle); Require(sceHmdGetDeviceInformationByHandle(0, nullptr) == Hmd::InvalidHandle, "null query accepted invalid handle"); Require(sceHmdTerminate() == 0, "terminate failed"); From a569d075ecf0b12acea7d6f5a212cb33c66dad2c Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 10:12:19 +0300 Subject: [PATCH 028/523] feat(libSceHmd): report the headset disconnected from sceHmdGetAssyError --- core/libs/prx/libSceHmd/Export.cpp | 6 +++--- core/libs/prx/libSceHmd/Hmd.hpp | 1 + core/libs/tests/GuestHmd.cpp | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 6b533528b0..bf85522dfe 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -498,9 +498,9 @@ int APS5_VABI sceHmdInternalIsGameVr2d(void) { return 0; } -int APS5_VABI sceHmdGetAssyError(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceHmdGetAssyError(void* data) { + if (!data) return Hmd::ParameterNull; + return initialized.load() ? Hmd::DeviceDisconnected : Hmd::NotInitialized; } int APS5_VABI sceHmdReprojectionFinalize(void) { diff --git a/core/libs/prx/libSceHmd/Hmd.hpp b/core/libs/prx/libSceHmd/Hmd.hpp index ee11c14431..eb2f77625f 100644 --- a/core/libs/prx/libSceHmd/Hmd.hpp +++ b/core/libs/prx/libSceHmd/Hmd.hpp @@ -72,6 +72,7 @@ int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void); int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView); int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset); int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count); +int APS5_VABI sceHmdGetAssyError(void* data); } #endif diff --git a/core/libs/tests/GuestHmd.cpp b/core/libs/tests/GuestHmd.cpp index a7bbe47dcd..203775d1fd 100644 --- a/core/libs/tests/GuestHmd.cpp +++ b/core/libs/tests/GuestHmd.cpp @@ -53,6 +53,14 @@ void CheckHandleQueries(std::int32_t expected) { for (const auto byte : sensor) Require(byte == 0x5a, "a refused sensor query wrote its output"); } +void CheckAssyError(std::int32_t expected) { + unsigned char data[32]; + std::memset(data, 0x5a, sizeof(data)); + Require(sceHmdGetAssyError(nullptr) == Hmd::ParameterNull, "null assembly error output accepted"); + Require(sceHmdGetAssyError(data) == expected, "assembly error query without a headset"); + for (const auto byte : data) Require(byte == 0x5a, "a refused assembly error query wrote its output"); +} + } int main() { @@ -70,6 +78,7 @@ int main() { Require(sceHmdGetDeviceInformation(&info) == Hmd::NotInitialized, "query before initialize"); Require(std::memcmp(&info, &untouched, sizeof(info)) == 0, "failed query modified output"); CheckHandleQueries(Hmd::NotInitialized); + CheckAssyError(Hmd::NotInitialized); Require(sceHmdGetDeviceInformationByHandle(0, &info) == Hmd::NotInitialized, "handle query before initialize"); Require(sceHmdInitialize(nullptr) == Hmd::ParameterNull, "null initialize"); @@ -107,6 +116,7 @@ int main() { Require(std::memcmp(&info, &expected, sizeof(info)) == 0, "invalid handle modified output"); } CheckHandleQueries(Hmd::InvalidHandle); + CheckAssyError(Hmd::DeviceDisconnected); Require(sceHmdGetDeviceInformationByHandle(0, nullptr) == Hmd::InvalidHandle, "null query accepted invalid handle"); Require(sceHmdTerminate() == 0, "terminate failed"); From 6cedb6a5fda4a2b7ad79e15044c177182cda0841 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Tue, 20 Oct 2026 10:12:31 +0300 Subject: [PATCH 029/523] docs(debt): note where the headset-less Hmd results come from --- docs/dev/debt/libSceHmd.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 54a6bf362d..2a7c96d0ff 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -6,6 +6,7 @@ ### Unknown function info +- [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count - [sceHmdInternalGetPUVersion](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalEventInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetVirtualDisplaySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -16,9 +17,7 @@ - [sceHmdReprojectionUnsetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetCrashReportCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDebugTextMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionGetWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalRegisterEventCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionGetWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetPUSerialNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDebugSocialScreenMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -45,7 +44,6 @@ - [sceHmdInternalAnotherScreenSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetPuPositionParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitializeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGet2DEyeOffset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDebugSocialScreenMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuPowerStatusForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -60,7 +58,6 @@ - [sceHmdInternalIsCommonDlgMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetSensorCalibrationData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionQueryOnionBuffAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDebugMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsCommonDlgVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -68,7 +65,6 @@ - [sceHmdInternalSetPortConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetM2LedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetFieldOfView](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetIpdSettingEnableForSystemService](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetLedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -82,7 +78,6 @@ - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionQueryGarlicBuffAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetHmuPowerControl](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalBindDeviceWithUserId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuSerialNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -92,7 +87,6 @@ - [sceHmdInternalSeparateModeGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsGameVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetAssyError](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -128,7 +122,6 @@ - [sceHmdInternalGetHmuOpticalParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVrTracker](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionQueryOnionBuffSize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetUserEventEnd](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -146,7 +139,6 @@ - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetCount](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetInertialSensorData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetBrightness](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenGetFadeState](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -163,5 +155,4 @@ - [sceHmdInternalGetIpdSettingEnableForSystemService](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetPuRevision](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionQueryGarlicBuffSize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From e04cd443479f9d64c0ab57d8adc8d1bd69de1860 Mon Sep 17 00:00:00 2001 From: thuanquang Date: Tue, 20 Oct 2026 14:54:30 +0700 Subject: [PATCH 030/523] test(libc): cover wcsncat_s exact fit, unterminated destination and overlapping source Co-Authored-By: Claude Opus 5.5 --- core/libs/tests/GuestWideStrings.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/libs/tests/GuestWideStrings.cpp b/core/libs/tests/GuestWideStrings.cpp index abbba3196d..711cc31a6d 100644 --- a/core/libs/tests/GuestWideStrings.cpp +++ b/core/libs/tests/GuestWideStrings.cpp @@ -217,6 +217,14 @@ int main() { require(wcsncat_s_nid_postfix(strBuf, 16, u" world", 6) == 0 && same(strBuf, u"hello world", 12)); wcsncpy_s_nid_postfix(strBuf, 16, u"foo", 3); require(wcsncat_s_nid_postfix(strBuf, 16, u"barbaz", 3) == 0 && same(strBuf, u"foobar", 7)); + wcsncpy_s_nid_postfix(strBuf, 8, u"abc", 3); + require(wcsncat_s_nid_postfix(strBuf, 8, u"1234", 4) == 0 && same(strBuf, u"abc1234", 8)); + wcsncpy_s_nid_postfix(strBuf, 8, u"abc", 3); + require(wcsncat_s_nid_postfix(strBuf, 8, u"12345", 5) == 34 && strBuf[0] == 0); + wmemset_nid_postfix(strBuf, u'x', 4); + require(wcsncat_s_nid_postfix(strBuf, 4, u"a", 1) == 34 && strBuf[0] == 0); + wcsncpy_s_nid_postfix(strBuf, 16, u"abc", 3); + require(wcsncat_s_nid_postfix(strBuf, 16, strBuf + 1, 2) == 22 && strBuf[0] == 0); wcsncpy_s_nid_postfix(strBuf, 16, u"abc", 3); require(wcscat_s_nid_postfix(strBuf, 16, u"def") == 0 && same(strBuf, u"abcdef", 7)); wcsncpy_s_nid_postfix(strBuf, 8, u"12345", 5); From 632c0f773763c8b3ef219e2ee245fd3922b09956 Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Tue, 20 Oct 2026 14:33:30 +0530 Subject: [PATCH 031/523] feat(libScePsml): implement the MFSR query exports from the APR-EMU fakelib scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture and scePsmlMfsrIsCaptureInProgress gain real signatures and semantics reversed from the fakelib/libScePsml.sprx bundled with PPSA20397, which imports the first three. The context records the input dimensions at creation so the mipmap bias can be computed. --- core/libs/prx/libScePsml_debug/Export.cpp | 71 +++++++++++++++++----- core/libs/tests/GuestPsml.cpp | 74 +++++++++++++++++++++++ docs/dev/debt/libScePsml_debug.md | 1 + 3 files changed, 132 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libScePsml_debug/Export.cpp b/core/libs/prx/libScePsml_debug/Export.cpp index e8cc8f8b7a..aba81b67da 100644 --- a/core/libs/prx/libScePsml_debug/Export.cpp +++ b/core/libs/prx/libScePsml_debug/Export.cpp @@ -1,7 +1,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -11,8 +14,11 @@ namespace { constexpr std::int32_t SCE_PSML_ERROR_NOT_INITIALIZED = static_cast(0x8A810001); +constexpr std::int32_t SCE_PSML_ERROR_INVALID_ARGUMENT = static_cast(0x8A810005); +constexpr std::int32_t SCE_PSML_ERROR_NULL_OUTPUT = static_cast(0x8A810009); constexpr std::uint64_t MfsrBlockBytes = 0x200000; constexpr std::uint64_t MfsrBlockCount = 4; +constexpr std::uint32_t MfsrDispatchPacketDwords = 0x600; struct MfsrRequirement { std::uint64_t blockBytes; @@ -37,7 +43,12 @@ struct MfsrContextParam { std::uint32_t reserved0[2]; const MfsrBlock* blocks; std::uint64_t blockCount; - std::uint32_t reserved1[8]; + std::uint16_t inputWidth; + std::uint16_t inputHeight; + std::uint32_t reserved1; + const void* descriptor0; + const void* descriptor1; + std::uint32_t reserved2[2]; void* sharedResources; }; @@ -45,6 +56,9 @@ static_assert(sizeof(MfsrRequirement) == 0x18); static_assert(sizeof(MfsrSharedResourcesParam) == 0x20); static_assert(offsetof(MfsrContextParam, blocks) == 0x08); static_assert(offsetof(MfsrContextParam, blockCount) == 0x10); +static_assert(offsetof(MfsrContextParam, inputWidth) == 0x18); +static_assert(offsetof(MfsrContextParam, descriptor0) == 0x20); +static_assert(offsetof(MfsrContextParam, descriptor1) == 0x28); static_assert(offsetof(MfsrContextParam, sharedResources) == 0x38); enum class MfsrObjectKind { SharedResources, Context }; @@ -52,6 +66,8 @@ enum class MfsrObjectKind { SharedResources, Context }; struct MfsrObject { MfsrObjectKind kind; void* sharedResources; + std::uint32_t inputWidth; + std::uint32_t inputHeight; }; std::atomic g_mfsrInitialized{false}; @@ -70,17 +86,32 @@ void RequireBlocks(const MfsrBlock* blocks, std::uint64_t blockCount, const char } } -void* CreateMfsrObject(MfsrObjectKind kind, void* sharedResources, const char* function) { +void* CreateMfsrObject(MfsrObjectKind kind, void* sharedResources, const char* function, std::uint32_t inputWidth = 0, std::uint32_t inputHeight = 0) { std::lock_guard lock(g_mfsrMutex); if (kind == MfsrObjectKind::Context) { const auto found = g_mfsrObjects.find(sharedResources); RequireMfsr(found != g_mfsrObjects.end() && found->second->kind == MfsrObjectKind::SharedResources, function, "shared resources handle was not created by this library"); } - auto* object = new MfsrObject{kind, sharedResources}; + auto* object = new MfsrObject{kind, sharedResources, inputWidth, inputHeight}; g_mfsrObjects.emplace(object, object); return object; } +const MfsrObject* FindMfsrObject(void* handle) { + std::lock_guard lock(g_mfsrMutex); + const auto found = g_mfsrObjects.find(handle); + return found != g_mfsrObjects.end() ? found->second : nullptr; +} + +std::pair MfsrInputDimensions(const MfsrContextParam* param) { + if (param->inputWidth != 0) return {param->inputWidth, param->inputHeight}; + if (param->descriptor0 == nullptr) return {0, 0}; + std::uint16_t width = 0, height = 0; + std::memcpy(&width, static_cast(param->descriptor0) + 0x20, sizeof(width)); + std::memcpy(&height, static_cast(param->descriptor0) + 0x22, sizeof(height)); + return {width, height}; +} + void ReleaseMfsrObject(void* handle, MfsrObjectKind kind, const char* function) { std::lock_guard lock(g_mfsrMutex); const auto found = g_mfsrObjects.find(handle); @@ -159,7 +190,8 @@ std::int32_t APS5_VABI scePsmlMfsrCreateContext800M3_2(void** context, const Mfs if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; RequireMfsr(context != nullptr && param != nullptr, __func__, "null handle or parameter"); RequireBlocks(param->blocks, param->blockCount, __func__); - *context = CreateMfsrObject(MfsrObjectKind::Context, param->sharedResources, __func__); + const auto [inputWidth, inputHeight] = MfsrInputDimensions(param); + *context = CreateMfsrObject(MfsrObjectKind::Context, param->sharedResources, __func__, inputWidth, inputHeight); return 0; } @@ -184,28 +216,39 @@ std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacket1300(void* context, void* return SCE_PSML_ERROR_NOT_INITIALIZED; } -int APS5_VABI scePsmlMfsrGetDispatchMfsrPacketSizeInDwords() { - NotImplemented_nid_no_patch(__func__); +std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(void* context, std::uint32_t* packetSizeInDwords) { + if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; + if (FindMfsrObject(context) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; + if (packetSizeInDwords == nullptr) return SCE_PSML_ERROR_NULL_OUTPUT; + *packetSizeInDwords = MfsrDispatchPacketDwords; return 0; } -int APS5_VABI scePsmlMfsrSelectConfig() { - NotImplemented_nid_no_patch(__func__); +std::int32_t APS5_VABI scePsmlMfsrSelectConfig(void* config) { + if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; + if (FindMfsrObject(config) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; return 0; } -int APS5_VABI scePsmlMfsrGetMipmapBias() { - NotImplemented_nid_no_patch(__func__); +std::int32_t APS5_VABI scePsmlMfsrGetMipmapBias(void* context, float* bias, std::uint32_t dimension) { + if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; + const MfsrObject* object = FindMfsrObject(context); + if (object == nullptr || object->kind != MfsrObjectKind::Context) return SCE_PSML_ERROR_INVALID_ARGUMENT; + if (bias == nullptr) return SCE_PSML_ERROR_NULL_OUTPUT; + if (object->inputWidth == 0) return SCE_PSML_ERROR_INVALID_ARGUMENT; + *bias = std::log2f(static_cast(dimension) / static_cast(object->inputWidth)) - 1.0f; return 0; } -int APS5_VABI scePsmlMfsrRequestCapture() { - NotImplemented_nid_no_patch(__func__); +std::int32_t APS5_VABI scePsmlMfsrRequestCapture(void* context) { + if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; + if (FindMfsrObject(context) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; return 0; } -int APS5_VABI scePsmlMfsrIsCaptureInProgress() { - NotImplemented_nid_no_patch(__func__); +std::int32_t APS5_VABI scePsmlMfsrIsCaptureInProgress(void* context) { + if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; + if (FindMfsrObject(context) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; return 0; } diff --git a/core/libs/tests/GuestPsml.cpp b/core/libs/tests/GuestPsml.cpp index c545232c9b..f82ec1744a 100644 --- a/core/libs/tests/GuestPsml.cpp +++ b/core/libs/tests/GuestPsml.cpp @@ -21,6 +21,11 @@ std::int32_t APS5_VABI scePsmlMfsrGetSharedResourcesInitRequirement(void* requir std::int32_t APS5_VABI scePsmlMfsrCreateSharedResources(void** sharedResources, const void* param); std::int32_t APS5_VABI scePsmlMfsrReleaseSharedResources(void* sharedResources); std::int32_t APS5_VABI scePsmlMfsrReleaseContext(void* context); +std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(void* context, std::uint32_t* packetSizeInDwords); +std::int32_t APS5_VABI scePsmlMfsrSelectConfig(void* config); +std::int32_t APS5_VABI scePsmlMfsrGetMipmapBias(void* context, float* bias, std::uint32_t dimension); +std::int32_t APS5_VABI scePsmlMfsrRequestCapture(void* context); +std::int32_t APS5_VABI scePsmlMfsrIsCaptureInProgress(void* context); } namespace { @@ -28,6 +33,8 @@ namespace { void Require(bool value) { if (!value) std::abort(); } constexpr std::int32_t kErrNotInitialized = static_cast(0x8A810001); +constexpr std::int32_t kErrInvalidArgument = static_cast(0x8A810005); +constexpr std::int32_t kErrNullOutput = static_cast(0x8A810009); struct CommandBuffer { std::uint32_t* cursor; @@ -98,6 +105,15 @@ struct ContextParam { std::memcpy(bytes.data() + 0x10, &count, sizeof(count)); std::memcpy(bytes.data() + 0x38, &shared, sizeof(shared)); } + + void SetInputDimensions(std::uint16_t width, std::uint16_t height) { + std::memcpy(bytes.data() + 0x18, &width, sizeof(width)); + std::memcpy(bytes.data() + 0x1a, &height, sizeof(height)); + } + + void SetDescriptor0(const void* descriptor) { + std::memcpy(bytes.data() + 0x20, &descriptor, sizeof(descriptor)); + } }; std::array Blocks(const Requirement& requirement, std::uint64_t first) { @@ -118,6 +134,16 @@ void SetupBeforeInit() { Require(handle == sentinel); Require(scePsmlMfsrReleaseContext(sentinel) == kErrNotInitialized); Require(scePsmlMfsrReleaseSharedResources(sentinel) == kErrNotInitialized); + + std::uint32_t packetDwords = 0xa5a5a5a5; + Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(sentinel, &packetDwords) == kErrNotInitialized); + Require(packetDwords == 0xa5a5a5a5); + float bias = 0.5f; + Require(scePsmlMfsrGetMipmapBias(sentinel, &bias, 3840) == kErrNotInitialized); + Require(bias == 0.5f); + Require(scePsmlMfsrSelectConfig(sentinel) == kErrNotInitialized); + Require(scePsmlMfsrRequestCapture(sentinel) == kErrNotInitialized); + Require(scePsmlMfsrIsCaptureInProgress(sentinel) == kErrNotInitialized); } void SetupAfterInit() { @@ -148,6 +174,54 @@ void SetupAfterInit() { Require(scePsmlMfsrCreateContext800M3_2(&mfsrContext, contextParam.bytes.data()) == 0); Require(mfsrContext != nullptr && mfsrContext != sharedResources); + std::uint32_t packetDwords = 0; + Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(mfsrContext, &packetDwords) == 0); + Require(packetDwords == 0x600); + Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(mfsrContext, nullptr) == kErrNullOutput); + Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(sharedResources, &packetDwords) == 0); + + std::uint64_t foreign = 0; + Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(&foreign, &packetDwords) == kErrInvalidArgument); + Require(scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(nullptr, &packetDwords) == kErrInvalidArgument); + + float bias = 42.0f; + Require(scePsmlMfsrGetMipmapBias(mfsrContext, &bias, 3840) == kErrInvalidArgument); + Require(bias == 42.0f); + Require(scePsmlMfsrGetMipmapBias(&foreign, &bias, 3840) == kErrInvalidArgument); + Require(scePsmlMfsrGetMipmapBias(sharedResources, &bias, 3840) == kErrInvalidArgument); + + Require(scePsmlMfsrSelectConfig(mfsrContext) == 0); + Require(scePsmlMfsrSelectConfig(&foreign) == kErrInvalidArgument); + Require(scePsmlMfsrRequestCapture(mfsrContext) == 0); + Require(scePsmlMfsrRequestCapture(nullptr) == kErrInvalidArgument); + Require(scePsmlMfsrIsCaptureInProgress(mfsrContext) == 0); + Require(scePsmlMfsrIsCaptureInProgress(&foreign) == kErrInvalidArgument); + + ContextParam dimensionsParam; + dimensionsParam.Set(contextBlocks.data(), contextBlocks.size(), sharedResources); + dimensionsParam.SetInputDimensions(1920, 1080); + void* dimensionContext = nullptr; + Require(scePsmlMfsrCreateContext800M3_2(&dimensionContext, dimensionsParam.bytes.data()) == 0); + Require(scePsmlMfsrGetMipmapBias(dimensionContext, &bias, 3840) == 0); + Require(bias == 0.0f); + Require(scePsmlMfsrGetMipmapBias(dimensionContext, &bias, 1920) == 0); + Require(bias == -1.0f); + Require(scePsmlMfsrGetMipmapBias(dimensionContext, nullptr, 1920) == kErrNullOutput); + Require(scePsmlMfsrReleaseContext(dimensionContext) == 0); + + std::array descriptor{}; + const std::uint16_t descriptorWidth = 1280, descriptorHeight = 720; + std::memcpy(descriptor.data() + 0x20, &descriptorWidth, sizeof(descriptorWidth)); + std::memcpy(descriptor.data() + 0x22, &descriptorHeight, sizeof(descriptorHeight)); + ContextParam descriptorParam; + descriptorParam.Set(contextBlocks.data(), contextBlocks.size(), sharedResources); + descriptorParam.SetDescriptor0(descriptor.data()); + void* descriptorContext = nullptr; + Require(scePsmlMfsrCreateContext800M3_2(&descriptorContext, descriptorParam.bytes.data()) == 0); + Require(scePsmlMfsrGetMipmapBias(descriptorContext, &bias, 2560) == 0); + Require(bias == 0.0f); + Require(scePsmlMfsrReleaseContext(descriptorContext) == 0); + Require(Throws([&] { scePsmlMfsrReleaseSharedResources(sharedResources); }, "still used by a context")); Require(Throws([&] { scePsmlMfsrReleaseContext(sharedResources); }, "not created by this library")); Require(scePsmlMfsrReleaseContext(mfsrContext) == 0); diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index d30a44b812..52edced2f2 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -11,4 +11,5 @@ ### Functional +- [scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture, scePsmlMfsrIsCaptureInProgress](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - semantics from the APR-EMU `fakelib/libScePsml.sprx` bundled with PPSA20397. Guards: `0x8A810001` uninitialized, `0x8A810005` for a null/foreign handle (the fakelib checks a leading engine id `0xA9C4`/`0x9231`; our map check is the equivalent), `0x8A810009` for a null output. `GetDispatchMfsrPacketSizeInDwords` writes `0x600`; `GetMipmapBias(context, bias, dimension)` writes `log2f(dimension / context->inputWidth) - 1.0f`, with the width recorded at `CreateContext800M3_2` from the parameter pair at 0x18 or `descriptor0 + 0x20` (the fakelib's indirection is inferred). `SelectConfig`, `RequestCapture` and `IsCaptureInProgress` validate then return 0. The `GetDispatchMfsrPacket*` calls still throw: the 0x158-byte template's packet-construction routine is not understood - [scePsmlMfsrInit](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - succeeds and only sets the initialized state; the shared resources and contexts are bookkeeping objects that use none of the guest blocks. Whether Sony's library fails on hardware without PSSR is unknown. PPSA08330 calls the setup unconditionally and only uses a context when its cached `sceKernelIsTrinityMode` result is set (0x3c643d, 0x567f97; otherwise it stores a -0.25 mip bias itself at 0x567fe2), which AnyPS5 reports as 0, so the dispatch, mip bias and capture calls still throw From aaa6ae312bfe172350e37ede513197c6c114658a Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Tue, 20 Oct 2026 14:36:46 +0530 Subject: [PATCH 032/523] docs(debt): trim the libScePsml_debug entry under the length limit --- docs/dev/debt/libScePsml_debug.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index 52edced2f2..7a25620b4c 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -11,5 +11,5 @@ ### Functional -- [scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture, scePsmlMfsrIsCaptureInProgress](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - semantics from the APR-EMU `fakelib/libScePsml.sprx` bundled with PPSA20397. Guards: `0x8A810001` uninitialized, `0x8A810005` for a null/foreign handle (the fakelib checks a leading engine id `0xA9C4`/`0x9231`; our map check is the equivalent), `0x8A810009` for a null output. `GetDispatchMfsrPacketSizeInDwords` writes `0x600`; `GetMipmapBias(context, bias, dimension)` writes `log2f(dimension / context->inputWidth) - 1.0f`, with the width recorded at `CreateContext800M3_2` from the parameter pair at 0x18 or `descriptor0 + 0x20` (the fakelib's indirection is inferred). `SelectConfig`, `RequestCapture` and `IsCaptureInProgress` validate then return 0. The `GetDispatchMfsrPacket*` calls still throw: the 0x158-byte template's packet-construction routine is not understood +- [scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture, scePsmlMfsrIsCaptureInProgress](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - semantics from the APR-EMU `fakelib/libScePsml.sprx` in PPSA20397. Guards: `0x8A810001` uninitialized, `0x8A810005` null/foreign handle (fakelib checks an engine id `0xA9C4`/`0x9231`; our map check is the equivalent), `0x8A810009` null output. `GetDispatchMfsrPacketSizeInDwords` writes `0x600`; `GetMipmapBias(context, bias, dimension)` writes `log2f(dimension / context->inputWidth) - 1.0f`, width recorded at `CreateContext800M3_2` from the parameter pair at 0x18 or `descriptor0 + 0x20` (indirection inferred). `SelectConfig`, `RequestCapture`, `IsCaptureInProgress` validate then return 0. The `GetDispatchMfsrPacket*` calls still throw: the 0x158-byte template's packet routine is not understood - [scePsmlMfsrInit](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - succeeds and only sets the initialized state; the shared resources and contexts are bookkeeping objects that use none of the guest blocks. Whether Sony's library fails on hardware without PSSR is unknown. PPSA08330 calls the setup unconditionally and only uses a context when its cached `sceKernelIsTrinityMode` result is set (0x3c643d, 0x567f97; otherwise it stores a -0.25 mip bias itself at 0x567fe2), which AnyPS5 reports as 0, so the dispatch, mip bias and capture calls still throw From 7dbe27c8daa7b9410d4e701ed5d7c6e1c1ae1cfb Mon Sep 17 00:00:00 2001 From: KernelPanicNT <76663133+KernelPanicNT@users.noreply.github.com> Date: Mon, 19 Oct 2026 20:11:40 +0000 Subject: [PATCH 033/523] feat(libSceNgs2): map waveform blocks of linear PCM and float types --- .../libs/prx/libSceNgs2.native/src/Atrac9.cpp | 30 ++++++++++++++-- .../libSceNgs2.native/tests/Ngs2Atrac9.cpp | 35 +++++++++++++++++++ docs/dev/debt/libSceNgs2.native.md | 1 + 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp b/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp index b508ac56a3..6c08a090c1 100644 --- a/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp +++ b/core/libs/prx/libSceNgs2.native/src/Atrac9.cpp @@ -23,6 +23,25 @@ static constexpr std::size_t FMT_GUID_OFFSET = 24; static constexpr std::size_t FMT_CONFIG_OFFSET = 44; static constexpr std::size_t FACT_ATRAC9_SIZE = 12; static constexpr std::uint32_t MAX_SAMPLE_RATE = 192000; +static constexpr std::uint32_t WAVEFORM_TYPE_PCM_FIRST = 0x10; +static constexpr std::uint32_t WAVEFORM_TYPE_PCM_LAST = 0x1b; +static constexpr std::uint32_t WAVEFORM_TYPE_VAG = 0x1c; +static constexpr std::uint32_t PCM_BYTES_PER_SAMPLE[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 8, 8}; + +static bool IsPcmType(std::uint32_t type) { + return type >= WAVEFORM_TYPE_PCM_FIRST && type <= WAVEFORM_TYPE_PCM_LAST; +} + +static Ngs2WaveformBlock PcmBlock(std::uint32_t type, std::uint32_t channels, std::uint32_t pos, std::uint32_t count) { + const std::uint64_t stride = static_cast(channels) * PCM_BYTES_PER_SAMPLE[type - WAVEFORM_TYPE_PCM_FIRST]; + const auto offset = static_cast(pos * stride); + const auto end = (pos + count) * static_cast(stride); + Ngs2WaveformBlock block{}; + block.data_offset = offset; + block.data_size = static_cast(end - offset); + block.num_samples = count; + return block; +} void Ngs2Atrac9DecoderDeleter::operator()(void* handle) const { Atrac9ReleaseHandle(handle); @@ -407,10 +426,17 @@ int APS5_VABI sceNgs2ParseWaveformFile(const char* path, uint32_t offset, Ngs2Wa int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_t sample_pos, uint32_t num_samples, Ngs2WaveformBlock* block) { if (block == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS; *block = {}; - if (format == nullptr || format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) { + if (format == nullptr) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT; + const auto type = format->waveform_type; + if (IsPcmType(type)) { + *block = PcmBlock(type, format->num_channels, sample_pos, num_samples); + return SCE_NGS2_OK; + } + if (type == WAVEFORM_TYPE_VAG) throw std::runtime_error("NGS2: waveform blocks of type " + Ngs2Hex(type) + " are not implemented"); + if (type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT; + if (format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) { return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT; } - if (format->waveform_type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) throw std::runtime_error("NGS2: waveform blocks of type " + Ngs2Hex(format->waveform_type) + " are not implemented"); Atrac9CodecInfo codec{}; if (!MatchingCodecInfo(*format, codec)) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT; const std::uint64_t superframeSamples = static_cast(codec.frameSamples) * codec.framesInSuperframe; diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp index b9278520ac..eed5eaf1f1 100644 --- a/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp @@ -195,6 +195,40 @@ static void TestCalcBlock() { Require(sceNgs2CalcWaveformBlock(&silent, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT); } +static void RequirePcmBlock(std::uint32_t type, std::uint32_t channels, std::uint32_t pos, std::uint32_t count, std::uint64_t offset, std::uint64_t size) { + const Ngs2WaveformFormat format{type, channels, 48000, 0, 0, 0}; + Ngs2WaveformBlock block; + std::memset(&block, 0xab, sizeof(block)); + Require(sceNgs2CalcWaveformBlock(&format, pos, count, &block) == SCE_NGS2_OK); + Require(block.data_offset == offset && block.data_size == size && block.num_samples == count); + Require(block.num_repeats == 0 && block.num_skip_samples == 0 && block.reserved == 0 && block.user_data == 0); +} + +static void TestCalcPcmBlock() { + const std::uint32_t bytesPerSample[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 8, 8}; + for (std::uint32_t type = 0x10; type <= 0x1b; ++type) { + const auto stride = 100u * bytesPerSample[type - 0x10]; + RequirePcmBlock(type, 100, 3, 5, 3u * stride, 5u * stride); + } + RequirePcmBlock(SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 100, 50, 400, 200); + RequirePcmBlock(SCE_NGS2_WAVEFORM_TYPE_PCM_F32L, 1, 10, 0, 40, 0); + RequirePcmBlock(SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 0, 7, 9, 0, 0); + RequirePcmBlock(0x1a, 8, 0x4000000, 1, 0, 64); + RequirePcmBlock(0x1a, 2, 0xffffffffu, 2, 0xfffffff0u, 32); + const Ngs2WaveformFormat noRate{SCE_NGS2_WAVEFORM_TYPE_PCM_I16L, 2, 0, 0, 0, 0}; + Ngs2WaveformBlock block{}; + Require(sceNgs2CalcWaveformBlock(&noRate, 1, 1, &block) == SCE_NGS2_OK && block.data_offset == 4 && block.data_size == 4); + for (std::uint32_t type : {0x0u, 0xfu, 0x1du, 0x20u, 0x30u, 0x43u}) { + const Ngs2WaveformFormat unknown{type, 2, 48000, 0, 0, 0}; + std::memset(&block, 0xab, sizeof(block)); + Require(sceNgs2CalcWaveformBlock(&unknown, 0, 1, &block) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT); + const auto* bytes = reinterpret_cast(&block); + Require(std::all_of(bytes, bytes + sizeof(block), [](std::uint8_t byte) { return byte == 0; })); + } + std::memset(&block, 0xab, sizeof(block)); + Require(sceNgs2CalcWaveformBlock(nullptr, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT && block.data_size == 0 && block.num_samples == 0); +} + static void Set32(std::vector& bytes, std::size_t offset, std::uint32_t value) { for (int i = 0; i < 4; ++i) bytes[offset + i] = static_cast(value >> (i * 8)); } @@ -504,6 +538,7 @@ int main() { TestParseLoops(); TestMalformedLoops(); TestCalcBlock(); + TestCalcPcmBlock(); TestCorruptSuperframeAtPageEnd(); TestStreamPartitions(); TestRefill(reference); diff --git a/docs/dev/debt/libSceNgs2.native.md b/docs/dev/debt/libSceNgs2.native.md index 361ee333a9..3b884b2f20 100644 --- a/docs/dev/debt/libSceNgs2.native.md +++ b/docs/dev/debt/libSceNgs2.native.md @@ -34,6 +34,7 @@ - [sceNgs2GeomCalcListener](../../../core/libs/prx/libSceNgs2.native/src/Geom.cpp) builds an orthonormal world-to-listener transform. The row-major matrix, front-axis priority and flag 1 for right-handed input follow [PS5PCEM](https://github.com/iStark/PS5PCEM/blob/d2534bac6325a2c61c0fa88977e5321074304484/src/hle/ngs2_geom.zig); they have not been measured on PS5 hardware. Velocity stays in world coordinates and positive finite sound speed is preserved. Orientation vectors are normalised and up is made perpendicular to front, so non-unit and slightly tilted vectors are accepted. When front and up are parallel, up is replaced by `(0,1,0)` or `(1,0,0)` so the basis stays defined: PPSA15552 (Dead Cells) passes `orient_front={0,0,1}` with `orient_up={0,0,1}` after moving the listener, which PS5PCEM rejects; the console error code is unknown. Unknown flags, zero orientation vectors, nonfinite input and an unrepresentable translation still throw without changing the output; console error codes are unknown, and a nonpositive sound speed throws where PS5PCEM and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/ngs2.cpp) default it to 343. KytyPS5 always writes the identity matrix, so the transform rests on PS5PCEM plus that Dead Cells case. Dead Cells (PPSA15552) reaches this call after the custom sampler rack in #4334. This does not implement source attenuation, panning or Doppler in `sceNgs2GeomApply`. - [NGS2 custom sampler rack](../../../core/libs/prx/libSceNgs2.native/src/Custom.cpp) (libSceNgs2.native, rack 0x4001) - implements bounded support for the custom sampler rack and its built-in module 0x13 (Custom Voice Filter), unblocking PPSA15552 (Dead Cells) startup. Structure layouts come from [shadPS4's ngs2_custom.h](https://raw.githubusercontent.com/shadps4-emu/shadPS4/main/src/core/libraries/ngs2/ngs2_custom.h) (OrbisNgs2CustomVoiceFilterParam, 40 bytes) and live trace correlation from [prosper's hle_audio.cpp](https://raw.githubusercontent.com/mattias800/prosper/main/prosper/src/hle/audio/hle_audio.cpp) (0x40001300 command and parameters like 10000.0f and 0.707f). Routes parameter updates via the 0x40001300 command family into the Biquad filter engine. Filter type 0 clears the destination filter; type 1 configures the RBJ low-pass biquad frequency, Q and level. Other filter types and unmodeled configurations throw. - [sceNgs2VoiceGetState custom sampler](../../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native, rack 0x4001) - only the size and one field come from a title. PPSA15552 (Dead Cells) calls it on custom sampler voices with a 48-byte buffer at two sites (eboot `0x1740f90`, `0x1741009`) and reads only the 64-bit value at offset 16: the first site keeps it and the second subtracts the kept value, so it is filled with the voice's decoded sample count. Other sizes return `INVALID_OUT_SIZE` as for the other racks. The state flags fill the common voice state; the remaining fields stay zero. shadPS4's `OrbisNgs2CustomSamplerVoiceState` is about 80 bytes and does not match the size this title passes, so the field names and the console's value are unverified +- [sceNgs2CalcWaveformBlock](../../../core/libs/prx/libSceNgs2.native/src/Atrac9.cpp) (libSceNgs2.native) - VAG (0x1c) throws. ATRAC9 keeps AnyPS5's own mapping: it checks the config against the channels and rate (0x804A8431), leaves out the decoder delay from the skip and size, leaves both 0 for zero samples and computes the size in 64 bits, while [prosper reads the PS5 module](https://github.com/mattias800/prosper/blob/40d95db32efe1ad00106a5b9a84e018fffa9ecff/prosper/src/hle/audio/ngs2_waveform.cpp#L348-L396) without the check, with the delay, filling both and modulo 2^32. Linear PCM and float (0x10 to 0x1b), the null checks and the 0x804A8432 of other types follow that reading and [its entry point](https://github.com/mattias800/prosper/blob/40d95db32efe1ad00106a5b9a84e018fffa9ecff/prosper/src/hle/audio/ngs2_waveform.cpp#L444-L466). Closes when: VAG is mapped and ATRAC9 follows the module ### Functional From b3a38791f9812b1c62c1b2f038c05c2d30b19729 Mon Sep 17 00:00:00 2001 From: Gruzzberry Date: Tue, 20 Oct 2026 10:47:24 +0100 Subject: [PATCH 034/523] docs: add trademark and non-affiliation notice to README Co-Authored-By: Claude Sonnet 5.5 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5777022fb4..c06b74b83c 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ SDL-mapped game controllers are supported, including analog sticks and triggers. This project is intended for interoperability, research, preservation, and compatibility purposes. It does not include, distribute, or require copyrighted software, firmware, cryptographic keys, or proprietary libraries. Users are responsible for ensuring that any binaries used with this project are obtained and used in accordance with applicable laws and their respective license terms. +AnyPS5 is an independent project and is not affiliated with, authorized or endorsed by Sony Interactive Entertainment. "PlayStation" and "PS5" are trademarks of Sony Interactive Entertainment Inc., used here only to describe compatibility. + ## License This project is licensed under the GNU General Public License version 2 only. From fec563d92cd91eca2d174972bdd4167dacf29956 Mon Sep 17 00:00:00 2001 From: hypermist Date: Tue, 20 Oct 2026 22:52:52 +1300 Subject: [PATCH 035/523] feat(libSceAgc): implement static buffers and qwords sceAgcSetStaticBuffer, sceAgcGetStaticBuffer, sceAgcSetStaticQword and sceAgcGetStaticQword read and write the six 16-byte slots at 0xfe0040000 that the driver already maps for the fetch shader table, through a new AgcDriverStaticTable_nid_postfix accessor. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- core/libs/prx/libSceAgc/CMakeLists.txt | 14 ++++ .../libSceAgc/Misc/include/StaticBuffer.hpp | 14 ++++ .../prx/libSceAgc/Misc/src/StaticBuffer.cpp | 45 +++++++++++++ .../libs/prx/libSceAgc/tests/StaticBuffer.cpp | 64 +++++++++++++++++++ .../Execution/include/Driver.hpp | 1 + .../Execution/src/Driver/Lifecycle.cpp | 5 ++ 6 files changed, 143 insertions(+) create mode 100644 core/libs/prx/libSceAgc/Misc/include/StaticBuffer.hpp create mode 100644 core/libs/prx/libSceAgc/Misc/src/StaticBuffer.cpp create mode 100644 core/libs/prx/libSceAgc/tests/StaticBuffer.cpp diff --git a/core/libs/prx/libSceAgc/CMakeLists.txt b/core/libs/prx/libSceAgc/CMakeLists.txt index a411cacdb2..faf498b571 100644 --- a/core/libs/prx/libSceAgc/CMakeLists.txt +++ b/core/libs/prx/libSceAgc/CMakeLists.txt @@ -35,6 +35,7 @@ add_library(libSceAgc SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/RegisterDefaults.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/Semaphore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/ShaderFusion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/StaticBuffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/Suspend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Patch/src/CondExec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Patch/src/Dma.cpp @@ -96,6 +97,19 @@ if(BUILD_TESTING) endif() configure_windows_unwind(libSceAgc) +add_test_executable(agc_static_buffer_tests EXCLUDE_FROM_ALL tests/StaticBuffer.cpp) +target_include_directories(agc_static_buffer_tests PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(agc_static_buffer_tests PRIVATE libSceAgc libSceAgcDriver libc) +configure_windows_unwind(agc_static_buffer_tests) +if(BUILD_TESTING) + set_target_properties(agc_static_buffer_tests PROPERTIES EXCLUDE_FROM_ALL OFF) + add_test(NAME agc_static_buffer COMMAND agc_static_buffer_tests) + if(WIN32) + set_tests_properties(agc_static_buffer PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() + add_test_executable(agc_platform_tests EXCLUDE_FROM_ALL tests/Platform.cpp) target_include_directories(agc_platform_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(agc_platform_tests PRIVATE libSceAgc libc) diff --git a/core/libs/prx/libSceAgc/Misc/include/StaticBuffer.hpp b/core/libs/prx/libSceAgc/Misc/include/StaticBuffer.hpp new file mode 100644 index 0000000000..2bd49c82aa --- /dev/null +++ b/core/libs/prx/libSceAgc/Misc/include/StaticBuffer.hpp @@ -0,0 +1,14 @@ +#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_STATICBUFFER_HPP +#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_STATICBUFFER_HPP + +#include +#include "SceTypes.hpp" + +extern "C" { +int APS5_VABI sceAgcSetStaticBuffer(std::uint32_t index, const void* buffer); +int APS5_VABI sceAgcGetStaticBuffer(std::uint32_t index, void* buffer); +int APS5_VABI sceAgcSetStaticQword(std::uint32_t index, std::uint64_t value); +int APS5_VABI sceAgcGetStaticQword(std::uint32_t index, std::uint64_t* value); +} + +#endif diff --git a/core/libs/prx/libSceAgc/Misc/src/StaticBuffer.cpp b/core/libs/prx/libSceAgc/Misc/src/StaticBuffer.cpp new file mode 100644 index 0000000000..212715e35f --- /dev/null +++ b/core/libs/prx/libSceAgc/Misc/src/StaticBuffer.cpp @@ -0,0 +1,45 @@ +#include "prx/libSceAgc/Misc/include/StaticBuffer.hpp" + +#include +#include +#include "SceTypes.hpp" +#include "prx/libSceAgcDriver/Execution/include/Driver.hpp" + +namespace { + +constexpr int GRAPHICS5_ERROR_INVALID_STATIC_BUFFER = static_cast(0x8a6c000bu); +constexpr std::uint32_t StaticBufferCount = 6; +constexpr std::size_t StaticBufferBytes = 16; +constexpr std::size_t StaticQwordOffset = 16; + +std::uint8_t* StaticTable() { + return static_cast(AgcDriverStaticTable_nid_postfix()); +} + +} + +extern "C" { + +int APS5_VABI sceAgcSetStaticBuffer(std::uint32_t index, const void* buffer) { + if (index >= StaticBufferCount) return GRAPHICS5_ERROR_INVALID_STATIC_BUFFER; + std::memcpy(StaticTable() + std::size_t{index} * StaticBufferBytes, buffer, StaticBufferBytes); + return 0; +} + +int APS5_VABI sceAgcGetStaticBuffer(std::uint32_t index, void* buffer) { + if (index >= StaticBufferCount) return GRAPHICS5_ERROR_INVALID_STATIC_BUFFER; + std::memcpy(buffer, StaticTable() + std::size_t{index} * StaticBufferBytes, StaticBufferBytes); + return 0; +} + +int APS5_VABI sceAgcSetStaticQword(std::uint32_t index, std::uint64_t value) { + std::memcpy(StaticTable() + StaticQwordOffset + std::size_t{index} * sizeof(value), &value, sizeof(value)); + return 0; +} + +int APS5_VABI sceAgcGetStaticQword(std::uint32_t index, std::uint64_t* value) { + std::memcpy(value, StaticTable() + StaticQwordOffset + std::size_t{index} * sizeof(*value), sizeof(*value)); + return 0; +} + +} diff --git a/core/libs/prx/libSceAgc/tests/StaticBuffer.cpp b/core/libs/prx/libSceAgc/tests/StaticBuffer.cpp new file mode 100644 index 0000000000..c7a3c162d6 --- /dev/null +++ b/core/libs/prx/libSceAgc/tests/StaticBuffer.cpp @@ -0,0 +1,64 @@ +#include "prx/libSceAgc/Misc/include/StaticBuffer.hpp" +#include "prx/libc/include/GuestArena.hpp" + +#include +#include +#include +#include +#include + +namespace { + +constexpr std::uintptr_t StaticTable = 0xfe0040000ull + GuestArena::GuestAddressOffset; +constexpr int InvalidStaticBuffer = static_cast(0x8a6c000bu); + +void check(bool condition, const char* message) { + if (!condition) throw std::runtime_error(message); +} + +std::array slot(std::uint32_t index) { + std::array words{}; + std::memcpy(words.data(), reinterpret_cast(StaticTable + index * 16u), sizeof(words)); + return words; +} + +void testBuffers() { + std::array fetchShaderTable{}; + check(sceAgcGetStaticBuffer(0, fetchShaderTable.data()) == 0, "reading static buffer 0 failed"); + check(fetchShaderTable == slot(0) && fetchShaderTable[0] >> 48u == 16u, "static buffer 0 is not the driver's fetch shader table"); + const std::array written{0x1122334455667788ull, 0x99aabbccddeeff00ull}; + check(sceAgcSetStaticBuffer(5, written.data()) == 0, "writing static buffer 5 failed"); + check(slot(5) == written, "static buffer 5 was not stored at the sixth 16-byte slot"); + std::array read{}; + check(sceAgcGetStaticBuffer(5, read.data()) == 0 && read == written, "static buffer 5 did not read back"); + check(sceAgcSetStaticBuffer(6, written.data()) == InvalidStaticBuffer, "static buffer index 6 was accepted"); + check(sceAgcGetStaticBuffer(6, read.data()) == InvalidStaticBuffer, "static buffer index 6 was read"); + check(slot(0) == fetchShaderTable, "static buffer writes changed slot 0"); +} + +void testQwords() { + check(sceAgcSetStaticQword(0, 0x0123456789abcdefull) == 0, "writing static qword 0 failed"); + check(sceAgcSetStaticQword(3, 0xfedcba9876543210ull) == 0, "writing static qword 3 failed"); + check(slot(1)[0] == 0x0123456789abcdefull && slot(2)[1] == 0xfedcba9876543210ull, "static qwords are not stored after the first 16-byte slot"); + std::uint64_t value = 0; + check(sceAgcGetStaticQword(3, &value) == 0 && value == 0xfedcba9876543210ull, "static qword 3 did not read back"); + std::array buffer{}; + check(sceAgcGetStaticBuffer(1, buffer.data()) == 0 && buffer[0] == 0x0123456789abcdefull, "static qword 0 is not the first half of static buffer 1"); +} + +} + +int main() { +#ifdef ANYPS5_ENABLE_ASAN + std::puts("skipped: the static table lives at the PS5's fixed 0xfe0040000, which an ANYPS5_ENABLE_ASAN build cannot map"); + return 0; +#endif + try { + testBuffers(); + testQwords(); + } catch (const std::exception& error) { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } + return 0; +} diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/Driver.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/Driver.hpp index 52582c1143..b599b88f95 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/include/Driver.hpp +++ b/core/libs/prx/libSceAgcDriver/Execution/include/Driver.hpp @@ -24,5 +24,6 @@ extern "C" void AgcDriverRegisterShader_nid_postfix(const Shader* shader); extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std::span context, std::span primitive); extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span stages, std::span context, std::span primitive); extern "C" void AgcDriverResolveGraphicsAbi_nid_postfix(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType); +extern "C" void* AgcDriverStaticTable_nid_postfix(); #endif diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp index 55bff4e2ac..27de9b5c40 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp @@ -287,3 +287,8 @@ std::unique_lock HoldAsyncLog() { } } + +extern "C" void* AgcDriverStaticTable_nid_postfix() { + static_cast(AgcDriver::DriverDetail::Driver::Get()); + return reinterpret_cast(AgcDriver::DriverDetail::FsTableAddress); +} From bd1cbb90de9c22748a7475f41285b18a249194a3 Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Tue, 20 Oct 2026 12:10:02 +0200 Subject: [PATCH 036/523] feat(libSceNgs2): report the rack and index that own a voice in sceNgs2VoiceGetOwner --- core/libs/prx/libSceNgs2.native/src/Voice.cpp | 10 ++++++++ .../prx/libSceNgs2.native/tests/Ngs2Test.hpp | 1 + .../tests/Ngs2VoiceHandles.cpp | 23 ++++++++++++++++++- core/libs/prx/libSceNgs2/PendingExports.cpp | 4 ---- docs/dev/debt/libSceNgs2.md | 2 +- docs/dev/debt/libSceNgs2.native.md | 1 + 6 files changed, 35 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceNgs2.native/src/Voice.cpp b/core/libs/prx/libSceNgs2.native/src/Voice.cpp index 7aa4ea0ef6..30d8bc5668 100644 --- a/core/libs/prx/libSceNgs2.native/src/Voice.cpp +++ b/core/libs/prx/libSceNgs2.native/src/Voice.cpp @@ -516,6 +516,16 @@ int APS5_VABI sceNgs2VoiceGetStateFlags(uintptr_t voice_handle, uint32_t* state_ return SCE_NGS2_OK; } +int APS5_VABI sceNgs2VoiceGetOwner(uintptr_t voice_handle, uintptr_t* owner_rack, uint32_t* voice_index) { + std::lock_guard lock(Ngs2Mutex()); + const auto* found = Ngs2FindVoice(voice_handle); + if (found == nullptr) return SCE_NGS2_ERROR_INVALID_VOICE_HANDLE; + if (owner_rack == nullptr || voice_index == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS; + *owner_rack = reinterpret_cast(found->rack); + *voice_index = static_cast(found - found->rack->voices.data()); + return SCE_NGS2_OK; +} + int APS5_VABI sceNgs2PanInit(Ngs2PanWork* work, const float* speaker_angles, float unit_angle, uint32_t num_speakers) { return Ngs2PanInit(work, speaker_angles, unit_angle, num_speakers); } diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp index 0f0838c354..4c10525b5e 100644 --- a/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp @@ -37,6 +37,7 @@ int APS5_VABI sceNgs2VoiceControl(uintptr_t, const Ngs2VoiceParamHeader*); int APS5_VABI sceNgs2VoiceRunCommands(uintptr_t, const Ngs2VoiceCommand*, size_t); int APS5_VABI sceNgs2VoiceGetState(uintptr_t, Ngs2VoiceState*, size_t); int APS5_VABI sceNgs2VoiceGetStateFlags(uintptr_t, uint32_t*); +int APS5_VABI sceNgs2VoiceGetOwner(uintptr_t, uintptr_t*, uint32_t*); int APS5_VABI sceNgs2VoiceGetPortInfo(uintptr_t, uint32_t, Ngs2VoicePortInfo*, size_t); int APS5_VABI sceNgs2VoiceQueryInfo(uintptr_t, uint32_t, void*, size_t); int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam*); diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2VoiceHandles.cpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2VoiceHandles.cpp index 5937b3f763..c5fed18835 100644 --- a/core/libs/prx/libSceNgs2.native/tests/Ngs2VoiceHandles.cpp +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2VoiceHandles.cpp @@ -3,12 +3,13 @@ #include #include #include +#include #include #include #include #include -static constexpr std::array Names{"control", "commands", "state", "port", "query", "flags"}; +static constexpr std::array Names{"control", "commands", "state", "port", "query", "flags", "owner"}; static constexpr int InvalidVoice = static_cast(0x804A8203u); static constexpr int NullList = static_cast(0x804A8360u); @@ -28,6 +29,7 @@ static void Invalid(std::string_view api, uintptr_t handle) { else if (api == "port") result = sceNgs2VoiceGetPortInfo(handle, 0, reinterpret_cast(output.data()), sizeof(Ngs2VoicePortInfo)); else if (api == "query") result = sceNgs2VoiceQueryInfo(handle, SCE_NGS2_VOICE_INFO_CHANNELS, output.data(), sizeof(Ngs2VoiceChannelsInfo)); else if (api == "flags") result = sceNgs2VoiceGetStateFlags(handle, reinterpret_cast(output.data())); + else if (api == "owner") result = sceNgs2VoiceGetOwner(handle, reinterpret_cast(output.data()), reinterpret_cast(output.data() + 16)); else throw std::runtime_error("unknown voice API test selection"); Status(result, InvalidVoice, std::string(api) + " returned the wrong invalid-voice error"); Require(std::all_of(output.begin(), output.end(), [](auto byte) { return byte == 0xa5; })); @@ -40,6 +42,7 @@ int main(int argc, char** argv) { for (auto name : Names) if (selected(name)) { Invalid(name, 0); Invalid(name, 0x1234); } Status(sceNgs2VoiceControl(0x1234, nullptr), NullList, "Control parameter-list precedence differs from PS5"); Status(sceNgs2VoiceGetState(0x1234, nullptr, 0), InvalidVoice, "GetState checked output before the voice"); + Status(sceNgs2VoiceGetOwner(0x1234, nullptr, nullptr), InvalidVoice, "GetOwner checked output before the voice"); const auto system = CreateSystem(); const auto rack = CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER); const auto voice = Voice(rack); @@ -50,6 +53,24 @@ int main(int argc, char** argv) { Status(sceNgs2VoiceGetState(voice, nullptr, sizeof(Ngs2VoiceState)), SCE_NGS2_ERROR_INVALID_OUT_ADDRESS, "GetState rejected its minimum-size boundary before null output"); Status(sceNgs2VoiceGetState(voice, nullptr, sizeof(Ngs2SamplerVoiceState)), SCE_NGS2_ERROR_INVALID_OUT_ADDRESS, "GetState lost null-output validation"); Status(sceNgs2VoiceGetStateFlags(voice, nullptr), SCE_NGS2_ERROR_INVALID_OUT_ADDRESS, "GetStateFlags lost null-output validation"); + uintptr_t owner = 0x5a5a; + std::uint32_t index = 0x5a5a; + Status(sceNgs2VoiceGetOwner(voice, nullptr, &index), SCE_NGS2_ERROR_INVALID_OUT_ADDRESS, "GetOwner lost null-rack validation"); + Status(sceNgs2VoiceGetOwner(voice, &owner, nullptr), SCE_NGS2_ERROR_INVALID_OUT_ADDRESS, "GetOwner lost null-index validation"); + Require(owner == 0x5a5a && index == 0x5a5a); + const auto other = CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER); + for (const auto owned : {rack, other}) { + Ngs2RackInfo info{}; + Status(sceNgs2RackGetInfo(owned, &info, sizeof(info)), SCE_NGS2_OK, "rack info lookup failed"); + Require(info.max_voices > 1); + for (std::uint32_t voiceId = 0; voiceId < info.max_voices; voiceId++) { + uintptr_t handle = 0; + Status(sceNgs2RackGetVoiceHandle(owned, voiceId, &handle), SCE_NGS2_OK, "voice handle lookup failed"); + Status(sceNgs2VoiceGetOwner(handle, &owner, &index), SCE_NGS2_OK, "valid owner lookup failed"); + Require(owner == owned && index == voiceId); + } + } + Require(sceNgs2RackDestroy(other, nullptr) == SCE_NGS2_OK); std::uint32_t flags = 0; Status(sceNgs2VoiceGetStateFlags(voice, &flags), SCE_NGS2_OK, "valid state-flags lookup failed"); Ngs2SamplerVoiceState state{}; diff --git a/core/libs/prx/libSceNgs2/PendingExports.cpp b/core/libs/prx/libSceNgs2/PendingExports.cpp index 992e9b2449..15cb207243 100644 --- a/core/libs/prx/libSceNgs2/PendingExports.cpp +++ b/core/libs/prx/libSceNgs2/PendingExports.cpp @@ -49,9 +49,5 @@ int APS5_VABI sceNgs2VoiceGetMatrixInfo(std::uintptr_t voiceHandle, std::uint32_ NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNgs2VoiceGetOwner(std::uintptr_t voiceHandle, std::uintptr_t* outOwnerRack, std::uint32_t* outVoiceIndex) { - NotImplemented_nid_no_patch(__func__); - return 0; -} } diff --git a/docs/dev/debt/libSceNgs2.md b/docs/dev/debt/libSceNgs2.md index 548dfd5035..56f610e569 100644 --- a/docs/dev/debt/libSceNgs2.md +++ b/docs/dev/debt/libSceNgs2.md @@ -3,4 +3,4 @@ ### Unknown function info - [sceNgs2FftInit, sceNgs2FftProcess, sceNgs2FftQuerySize](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) (libSceNgs2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNgs2.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. -- [sceNgs2CustomRackGetModuleInfo, sceNgs2GetWaveformFrameInfo, sceNgs2ParseWaveformUser, sceNgs2RackGetUserData, sceNgs2RackSetUserData, sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles, sceNgs2VoiceGetMatrixInfo, sceNgs2VoiceGetOwner](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) (libSceNgs2) - names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNgs2.c); signatures follow [SharpProspero's Ngs2 bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/Ngs2.cs), not measured on a console. Opaque structure pointers use `void*`; these stubs throw for import resolution. +- [sceNgs2CustomRackGetModuleInfo, sceNgs2GetWaveformFrameInfo, sceNgs2ParseWaveformUser, sceNgs2RackGetUserData, sceNgs2RackSetUserData, sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles, sceNgs2VoiceGetMatrixInfo](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) (libSceNgs2) - names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNgs2.c); signatures follow [SharpProspero's Ngs2 bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/Ngs2.cs), not measured on a console. Opaque structure pointers use `void*`; these stubs throw for import resolution. diff --git a/docs/dev/debt/libSceNgs2.native.md b/docs/dev/debt/libSceNgs2.native.md index 361ee333a9..0e8a4e4b3e 100644 --- a/docs/dev/debt/libSceNgs2.native.md +++ b/docs/dev/debt/libSceNgs2.native.md @@ -26,6 +26,7 @@ - [sceNgs2SystemSetSampleRate, sceNgs2SystemSetUserData, sceNgs2SystemGetUserData, sceNgs2SystemLock, sceNgs2SystemUnlock](../../../core/libs/prx/libSceNgs2.native/src/System.cpp) (libSceNgs2.native) - shadPS4 only checks the system handle (`INVALID_SYSTEM_HANDLE`) and SharpProspero names what each call does; neither gives the other limits. A sample rate of 0 and a null user data pointer throw, any other rate is accepted, and changing the rate while a sampler filter is enabled throws because the filter coefficients were computed for the old rate. `sceNgs2SystemLock` and `sceNgs2SystemUnlock` follow [prosper's notes](https://github.com/mattias800/prosper/blob/d61eb2c0ad2de879ef74df9c4aa050c512804a18/prosper/src/hle/audio/hle_audio.cpp#L4367-L4384) on the PS5 module: Lock takes a per-system lock and counts nested holds, Unlock releases one hold from any thread and returns 0 without a hold, and `sceNgs2SystemRender` only tries the lock: while another thread holds it, Render clears one grain of each output, leaves the render count and voices unchanged and returns 0. The other calls do not wait for the system lock. Peace Walker (PPSA18216) holds it while it spins on a flag that its audio thread clears only after `sceNgs2SystemRender` and eight `sceNgs2VoiceGetStateFlags` calls, so on the console neither can wait for it. Whether the console's voice and rack calls take the lock at all is not known. `sceNgs2RackLock` still holds the library's lock until `sceNgs2RackUnlock`, as before - [sceNgs2RackGetInfo](../../../core/libs/prx/libSceNgs2.native/src/Rack.cpp) (libSceNgs2.native) - field order from [shadPS4](https://github.com/shadps4-emu/shadPS4)'s `OrbisNgs2RackInfo` with the 64-byte name of the other info structures (216 bytes); `type` is assumed to be the rack id divided by 0x1000, `min_grain_samples` 64 (the system minimum) and `state_flags` 1; `uid` comes from a per-process rack counter, `render_count` is the owning system's render count and `active_voice_count` counts voices that are not empty (paused and stopped voices included). The last process ratio and tick and `active_channel_work_count` are left zero - [sceNgs2VoiceQueryInfo](../../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - only info id 0x4001 is known: PPSA22520 passes an 8-byte buffer and branches on the first word being 1 or 2, so it is assumed to be the voice channel count (0 before the voice is set up); the second word is zeroed and other ids throw +- [sceNgs2VoiceGetOwner](../../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - the signature `(voiceHandle, outOwnerRack, outVoiceIndex)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/Ngs2.cs#L439-L440) and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/ngs2/ngs2.cpp#L322-L326), which only logs it; neither gives an error. The errors are unmeasured: a handle that is not a live voice answers `INVALID_VOICE_HANDLE` before the outputs are checked, as in `sceNgs2RackGetVoiceHandle`, and either output pointer null answers `INVALID_OUT_ADDRESS` without writing the other - [sceNgs2VoiceControl patch](../../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - a destination rack handle with input id 0 resolves to that rack's first voice; this matches the Dead Cells call in #4334, but selection for racks with multiple voices has not been checked against a console. Nonzero destination input ids still throw. `sceNgs2VoiceGetPortInfo` reports the resolved voice handle and zero delay. - [sceNgs2ParseWaveformFile](../../../core/libs/prx/libSceNgs2.native/src/Atrac9.cpp) (libSceNgs2.native) - the signature `(path, offset, info)` is taken from the call site of PPSA21402, which sets only three argument registers; the file is read from `offset` to its end, the data and block offsets are taken to be relative to the start of the file, not of `offset`, and an unreadable file throws. The audio unit fields of a 16-bit PCM waveform are assumed to be one sample frame per unit and per frame; `INVALID_WAVEFORM_DATA` for a `data` chunk that runs past the buffer or ends inside a frame is assumed, and looped PCM and other PCM formats throw - [sceNgs2VoiceControl sampler filter](../../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - param `0x1000000a`, its layout and meaning follow KytyPS5 only: location 1 type 1 is an RBJ low-pass biquad at the system sample rate after resampling, scaled by `level`, a set `channel_mask` bit bypasses that channel, type 0 turns the filter off. Other locations and types throw. The voice keeps playing a filter tail after its last block until the history is zero, with outputs below 1e-20 flushed to zero (a KytyPS5 choice); the console's tail length is unknown From 3553a1dfc10809cb21f380eabdca028aa2a903d3 Mon Sep 17 00:00:00 2001 From: Yuri Olive Date: Tue, 20 Oct 2026 07:10:18 -0300 Subject: [PATCH 037/523] feat(libSceRegMgr): export missing firmware interfaces --- core/libs/prx/libSceRegMgr/Export.cpp | 290 ++++++++++++++++++++++++++ docs/dev/debt/libSceRegMgr.md | 58 ++++++ 2 files changed, 348 insertions(+) diff --git a/core/libs/prx/libSceRegMgr/Export.cpp b/core/libs/prx/libSceRegMgr/Export.cpp index 4626f92e6b..e1e4aad2c1 100644 --- a/core/libs/prx/libSceRegMgr/Export.cpp +++ b/core/libs/prx/libSceRegMgr/Export.cpp @@ -37,4 +37,294 @@ std::int32_t APS5_VABI sceRegMgrNonSysGetInt(std::int64_t key, std::int32_t unk, return 0; } +int APS5_VABI sceRegMgrCheckError(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCheckIntVal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrGetBinInitVal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrGetIntInitVal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrGetStrInitVal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrGetVersion(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrIsChange(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrEvtGetCnt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrEvtGetRegId(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrResetVal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSetInitLevel(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvCnvRegionInt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvCnvRegionStr(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvGetMachineType(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvGetQAFforReg(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvGetRealMachineType(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvGetRegion(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSrvGetRegionStr(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupNeedMem(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupPullData(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupPushData(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupPullDataGkp(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupPushDataGkp(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCntlStart(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCntlDeleteReg(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCntlGetFilesCount(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCntlGetFileName(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCntSetInitReinst(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrIsInitOK(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrGetInitError(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrRecoverRegNvs(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrNonSysCheckError(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrNonSysGetBin(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrNonSysGetStr(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrNonSysSetBin(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrNonSysSetInt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrNonSysSetStr(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrGetVersionForPS4(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupNeedMemForPS4(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupPullDataForPS4(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrBackupPushDataForPS4(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrEvtGetCntForPS4(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrEvtGetRegIdForPS4(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrStrncpy(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrMemset(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrSetbin(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrToolGetUpdateCnt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrToolGetEntryCnt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrToolDataCheckGet(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrDrvGetUpdateCnt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrDrvGetEntCnt(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrDrvDataOpen(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrDrvDataCheckGet(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrDrvDataClose(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrToolGetInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrLogPull(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrCntlStopHdd(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRegMgrPrintInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/docs/dev/debt/libSceRegMgr.md b/docs/dev/debt/libSceRegMgr.md index e599cc296d..9b6dbd6441 100644 --- a/docs/dev/debt/libSceRegMgr.md +++ b/docs/dev/debt/libSceRegMgr.md @@ -4,3 +4,61 @@ - [sceRegMgrNonSysGetInt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr, NID `dKeshzt29G4`) - observed as an import needed for module loading (issue 4720); its `(int64_t key, int32_t unk, int32_t* data)` signature follows [ChonkyStation4](https://github.com/liuk7071/ChonkyStation4/blob/c101a23ecbc15e56c6e30d5dd24d3ed8880a957a/ChonkyStation4/OS/Libraries/SceRegMgr/SceRegMgr.cpp#L38-L47), not measured on a console, so every call throws - [sceRegMgrGetBin and sceRegMgrSetBin](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRegMgr.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. The string and integer prototypes follow [SharpProspero's SystemSettings bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Platform/SystemSettings.cs) +- [sceRegMgrCheckError](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCheckIntVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrGetBinInitVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrGetIntInitVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrGetStrInitVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrGetVersion](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrIsChange](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrEvtGetCnt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrEvtGetRegId](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrResetVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSetInitLevel](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvCnvRegionInt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvCnvRegionStr](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvGetMachineType](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvGetQAFforReg](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvGetRealMachineType](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvGetRegion](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSrvGetRegionStr](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupNeedMem](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupPullData](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupPushData](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupPullDataGkp](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupPushDataGkp](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCntlStart](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCntlDeleteReg](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCntlGetFilesCount](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCntlGetFileName](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCntSetInitReinst](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrIsInitOK](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrGetInitError](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrRecoverRegNvs](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrNonSysCheckError](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrNonSysGetBin](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrNonSysGetStr](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrNonSysSetBin](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrNonSysSetInt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrNonSysSetStr](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrGetVersionForPS4](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupNeedMemForPS4](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupPullDataForPS4](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrBackupPushDataForPS4](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrEvtGetCntForPS4](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrEvtGetRegIdForPS4](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrStrncpy](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrMemset](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrSetbin](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrToolGetUpdateCnt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrToolGetEntryCnt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrToolDataCheckGet](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrDrvGetUpdateCnt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrDrvGetEntCnt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrDrvDataOpen](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrDrvDataCheckGet](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrDrvDataClose](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrToolGetInfo](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrLogPull](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrCntlStopHdd](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature +- [sceRegMgrPrintInfo](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature From f3497e153e87c9a5d3e2316429960e1dd0cb78f5 Mon Sep 17 00:00:00 2001 From: Yuri Olive Date: Tue, 20 Oct 2026 07:10:36 -0300 Subject: [PATCH 038/523] feat(libSceAvSetting,libSceHidControl): export missing firmware interfaces --- core/libs/prx/libSceAvSetting/Export.cpp | 120 +++++++++++++ core/libs/prx/libSceHidControl/Export.cpp | 200 ++++++++++++++++++++++ docs/dev/debt/libSceAvSetting.md | 28 +++ docs/dev/debt/libSceHidControl.md | 44 +++++ 4 files changed, 392 insertions(+) create mode 100644 docs/dev/debt/libSceAvSetting.md create mode 100644 docs/dev/debt/libSceHidControl.md diff --git a/core/libs/prx/libSceAvSetting/Export.cpp b/core/libs/prx/libSceAvSetting/Export.cpp index fb34206c21..e53788bea3 100644 --- a/core/libs/prx/libSceAvSetting/Export.cpp +++ b/core/libs/prx/libSceAvSetting/Export.cpp @@ -7,4 +7,124 @@ extern "C" { APS5_DUMMY_FUN +int APS5_VABI sceAvControlGetPuTypeByMonitorInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlIsVideoOutModeSupportedByMonitorInfoWithOptions(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlSetDitherModeForDiag(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlSetLayoutModeForDiag(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlSetMonitorFakeMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlGetCurrentOutputSysMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlGetNativeHdmiMonitorInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlGetRawMonitorInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlGetKsvList(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlGetRawEdid(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlRemoveProcessOutputMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlUpdateDriverStatus(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlNotifyUmdEvent(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlAddCallbacksForLnc(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlAddCallbacksForResourceArbitrator(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlRemoveCallbacks(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlCheckCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlWaitCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlSetColorMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlCheckMonitorInfoCapability(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlCheckVideoOutMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlSetCheckoutputMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlSetSystemVideoMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAvControlDebugAddCallbacks(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceHidControl/Export.cpp b/core/libs/prx/libSceHidControl/Export.cpp index fb34206c21..c3bfd0d3ed 100644 --- a/core/libs/prx/libSceHidControl/Export.cpp +++ b/core/libs/prx/libSceHidControl/Export.cpp @@ -7,4 +7,204 @@ extern "C" { APS5_DUMMY_FUN +int APS5_VABI sceHidControlEnableMicBeamForming(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetAllVibrationMute(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetVibrationTriggerEffectWeakForce(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlIsForceUpdateNeeded(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetRemoteMergeProcess(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetCollaboInputSettingOverrideProcess(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetForceAdvancedVibrationMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateIsUpdateNeeded(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateIsUpdateNeededForMultiDevice(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateIsReady(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateStart(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateGetStatus(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateSendData(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateSendAppendixData(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateGetReconnectStatus(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlPswUpdateGetDeviceFirmwareUpdateTelemetryData(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswState(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswBatteryState(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswDeviceName(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswDeviceName(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswConsoleMute(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswSpeakerVolume(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswSidetoneLevel(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswSidetoneLevel(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswEqPresetSelection(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswEqPresetSelection(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswEqPresetName(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswEqPresetName(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswEqBandValues(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswEqBandValues(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswAllEqBandValues(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswResetSettings(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswNetworkLocale(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswNetworkLocale(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswDeviceNameForNearby(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlConnectPswDeviceForNearby(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswDeviceInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlSetPswCommand(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswCommand(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHidControlGetPswDeviceIdFromIdxAdd(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/docs/dev/debt/libSceAvSetting.md b/docs/dev/debt/libSceAvSetting.md new file mode 100644 index 0000000000..6bbd62abd0 --- /dev/null +++ b/docs/dev/debt/libSceAvSetting.md @@ -0,0 +1,28 @@ +# libSceAvSetting technical debt + +### Unknown function info + +- [sceAvControlGetPuTypeByMonitorInfo](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlIsVideoOutModeSupportedByMonitorInfoWithOptions](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlSetDitherModeForDiag](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlSetLayoutModeForDiag](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlSetMonitorFakeMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlGetCurrentOutputSysMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlGetNativeHdmiMonitorInfo](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlGetRawMonitorInfo](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlGetKsvList](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlGetRawEdid](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlRemoveProcessOutputMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlUpdateDriverStatus](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlNotifyUmdEvent](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlAddCallbacksForLnc](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlAddCallbacksForResourceArbitrator](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlRemoveCallbacks](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlCheckCallback](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlWaitCallback](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlSetColorMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlCheckMonitorInfoCapability](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlCheckVideoOutMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlSetCheckoutputMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlSetSystemVideoMode](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature +- [sceAvControlDebugAddCallbacks](../../../core/libs/prx/libSceAvSetting/Export.cpp) (libSceAvSetting) - unknown signature diff --git a/docs/dev/debt/libSceHidControl.md b/docs/dev/debt/libSceHidControl.md new file mode 100644 index 0000000000..fc779520a0 --- /dev/null +++ b/docs/dev/debt/libSceHidControl.md @@ -0,0 +1,44 @@ +# libSceHidControl technical debt + +### Unknown function info + +- [sceHidControlEnableMicBeamForming](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetAllVibrationMute](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetVibrationTriggerEffectWeakForce](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlIsForceUpdateNeeded](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetRemoteMergeProcess](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetCollaboInputSettingOverrideProcess](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetForceAdvancedVibrationMode](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateIsUpdateNeeded](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateIsUpdateNeededForMultiDevice](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateIsReady](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateStart](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateGetStatus](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateSendData](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateSendAppendixData](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateGetReconnectStatus](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlPswUpdateGetDeviceFirmwareUpdateTelemetryData](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswState](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswBatteryState](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswDeviceName](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswDeviceName](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswConsoleMute](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswSpeakerVolume](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswSidetoneLevel](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswSidetoneLevel](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswEqPresetSelection](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswEqPresetSelection](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswEqPresetName](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswEqPresetName](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswEqBandValues](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswEqBandValues](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswAllEqBandValues](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswResetSettings](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswNetworkLocale](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswNetworkLocale](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswDeviceNameForNearby](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlConnectPswDeviceForNearby](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswDeviceInfo](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlSetPswCommand](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswCommand](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature +- [sceHidControlGetPswDeviceIdFromIdxAdd](../../../core/libs/prx/libSceHidControl/Export.cpp) (libSceHidControl) - unknown signature From afd20388a9bf561e31dd9b1cb7f8d30e4f8bf36c Mon Sep 17 00:00:00 2001 From: Yuri Olive Date: Tue, 20 Oct 2026 07:10:42 -0300 Subject: [PATCH 039/523] feat(libSceGameLiveStreaming): export missing firmware interfaces --- .../prx/libSceGameLiveStreaming/Export.cpp | 210 ++++++++++++++++++ docs/dev/debt/libSceGameLiveStreaming.md | 42 ++++ 2 files changed, 252 insertions(+) diff --git a/core/libs/prx/libSceGameLiveStreaming/Export.cpp b/core/libs/prx/libSceGameLiveStreaming/Export.cpp index b28b535c8e..d1edfb1af9 100644 --- a/core/libs/prx/libSceGameLiveStreaming/Export.cpp +++ b/core/libs/prx/libSceGameLiveStreaming/Export.cpp @@ -40,4 +40,214 @@ int APS5_VABI sceGameLiveStreamingGetProgramInfo() { return 0; } +int APS5_VABI sceGameLiveStreamingScreenInitialize(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingScreenInitializeSeparateModeParameter(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingScreenTerminate(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingScreenCloseSeparateMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingScreenConfigureSeparateMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingScreenOpenSeparateMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingScreenSetMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingPermitLiveStreaming(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetMaxBitrate(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingGetCurrentStatus(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingEnableSocialFeedback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingGetSocialFeedbackMessagesCount(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingGetSocialFeedbackMessages(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingPermitServerSideRecording(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetMetadata(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetSpoilerTag(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingClearSpoilerTag(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetPresetSocialFeedbackCommands(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingClearPresetSocialFeedbackCommands(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingClearSocialFeedbackMessages(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingPostSocialMessage(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetCameraFrameSetting(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetServiceProviderPermission(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetDefaultServiceProviderPermission(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetInvitationSessionId(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingStartGenerateStandbyScreenResource(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingStopGenerateStandbyScreenResource(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingRegisterCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingUnregisterCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingCheckCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetStandbyScreenResource(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingLaunchLiveViewer(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingLaunchLiveViewerA(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetGuardAreas(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingApplySocialFeedbackMessageFilter(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingSetLinkCommentPreset(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingStartDebugBroadcast(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceGameLiveStreamingStopDebugBroadcast(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/docs/dev/debt/libSceGameLiveStreaming.md b/docs/dev/debt/libSceGameLiveStreaming.md index b30576336c..f372120201 100644 --- a/docs/dev/debt/libSceGameLiveStreaming.md +++ b/docs/dev/debt/libSceGameLiveStreaming.md @@ -3,3 +3,45 @@ ### Unknown function info - [sceGameLiveStreamingGetCurrentStatus2](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - the status layout (user id, on-air flag, spectator, text and command message counts, broadcast resolution, 48 reserved bytes) is taken from shadPS4's `OrbisGameLiveStreamingStatus2`; the call reports that no stream is on air, with an invalid user id +- [sceGameLiveStreamingScreenInitialize](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingScreenInitializeSeparateModeParameter](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingScreenTerminate](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingScreenCloseSeparateMode](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingScreenConfigureSeparateMode](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingScreenOpenSeparateMode](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingScreenSetMode](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingPermitLiveStreaming](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetMaxBitrate](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingGetCurrentStatus](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingEnableSocialFeedback](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingGetSocialFeedbackMessagesCount](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingGetSocialFeedbackMessages](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingPermitServerSideRecording](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetMetadata](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetSpoilerTag](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingClearSpoilerTag](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetPresetSocialFeedbackCommands](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingClearPresetSocialFeedbackCommands](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingClearSocialFeedbackMessages](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingPostSocialMessage](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetCameraFrameSetting](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetServiceProviderPermission](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetDefaultServiceProviderPermission](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetInvitationSessionId](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingStartGenerateStandbyScreenResource](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingStopGenerateStandbyScreenResource](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingRegisterCallback](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingUnregisterCallback](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingCheckCallback](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetStandbyScreenResource](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingGetCurrentBroadcastScreenLayout](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingLaunchLiveViewer](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingLaunchLiveViewerA](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetGuardAreas](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingStartSocialFeedbackMessageFiltering](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingStopSocialFeedbackMessageFiltering](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingApplySocialFeedbackMessageFilter](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingSetLinkCommentPreset](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingStartDebugBroadcast](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature +- [sceGameLiveStreamingStopDebugBroadcast](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature From 213d68cfeeeaa152c0203f141d614a2c653a1a1a Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Tue, 20 Oct 2026 12:45:30 +0200 Subject: [PATCH 040/523] feat(libSceMbus): report a device set that never changes in sceDeviceServiceGetGeneration --- core/libs/prx/libSceMbus/Export.cpp | 2 +- core/libs/tests/GuestMbus.cpp | 3 +++ docs/dev/debt/libSceMbus.md | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceMbus/Export.cpp b/core/libs/prx/libSceMbus/Export.cpp index 2107420e01..10a101f22c 100644 --- a/core/libs/prx/libSceMbus/Export.cpp +++ b/core/libs/prx/libSceMbus/Export.cpp @@ -35,7 +35,7 @@ std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass } std::int32_t APS5_VABI sceDeviceServiceGetGeneration(void) { - NotImplemented_nid_no_patch(__func__); + return 0; } } diff --git a/core/libs/tests/GuestMbus.cpp b/core/libs/tests/GuestMbus.cpp index bf57e10121..c4f97b1141 100644 --- a/core/libs/tests/GuestMbus.cpp +++ b/core/libs/tests/GuestMbus.cpp @@ -10,6 +10,7 @@ extern "C" { std::int32_t APS5_VABI sceDeviceServiceInitialize(std::int32_t mode, std::int32_t flags); std::int32_t APS5_VABI sceDeviceServiceTerminate(); std::int32_t APS5_VABI sceDeviceServiceGetEventState(std::int32_t deviceType); +std::int32_t APS5_VABI sceDeviceServiceGetGeneration(); std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass, std::int32_t arg1, std::int32_t arg2, void* infos, std::int32_t maxInfos, std::int32_t* count, std::int32_t* reserved, std::uint64_t infoSize); } @@ -26,6 +27,7 @@ void Require(bool condition, const char* message) { int main() { Require(sceDeviceServiceInitialize(3, 0) == 0, "initialization failed"); + Require(sceDeviceServiceGetGeneration() == 0, "the device set started at another generation"); Require(sceDeviceServiceGetEventState(1) == 0, "a device event was reported"); std::array info{}; std::int32_t count = 7; @@ -38,6 +40,7 @@ int main() { threw = true; } Require(threw, "a null count was accepted"); + Require(sceDeviceServiceGetGeneration() == 0, "the generation advanced without a device change"); Require(sceDeviceServiceTerminate() == 0, "termination failed"); std::puts("DeviceService tests passed"); return 0; diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index 8c7cb7dd7e..1ae13da251 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libSceMbus](../../../core/libs/prx/libSceMbus/Export.cpp) implements only the no-device path: initialization and termination succeed, no device events are reported and device queries return no entries +- [libSceMbus](../../../core/libs/prx/libSceMbus/Export.cpp) implements only the no-device path: initialization and termination succeed, no device events are reported, device queries return no entries and `sceDeviceServiceGetGeneration` always returns 0, as the device set never changes. The generation a console starts at is unmeasured; [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Device/DeviceService.cs#L63-L65) documents `int sceDeviceServiceGetGeneration()` as a counter that advances when the device set changes ### Unknown function info From 8cae300f8df857b28e365a16141597f881687dc9 Mon Sep 17 00:00:00 2001 From: Gruzzberry Date: Tue, 20 Oct 2026 11:53:13 +0100 Subject: [PATCH 041/523] docs: drop the compatibility clause from the trademark notice Co-Authored-By: Claude Sonnet 5.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c06b74b83c..3e7fc3ccf4 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ SDL-mapped game controllers are supported, including analog sticks and triggers. This project is intended for interoperability, research, preservation, and compatibility purposes. It does not include, distribute, or require copyrighted software, firmware, cryptographic keys, or proprietary libraries. Users are responsible for ensuring that any binaries used with this project are obtained and used in accordance with applicable laws and their respective license terms. -AnyPS5 is an independent project and is not affiliated with, authorized or endorsed by Sony Interactive Entertainment. "PlayStation" and "PS5" are trademarks of Sony Interactive Entertainment Inc., used here only to describe compatibility. +AnyPS5 is an independent project and is not affiliated with, authorized or endorsed by Sony Interactive Entertainment. "PlayStation" and "PS5" are trademarks of Sony Interactive Entertainment Inc. ## License From d3629b034b9333e4d1df4b26e887b25af0afe99a Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Tue, 20 Oct 2026 13:00:41 +0200 Subject: [PATCH 042/523] feat(libSceVdecsw): report VP9 picture info in sceVdecswGetVp9PictureInfo --- core/libs/prx/libSceVdecsw/Export.cpp | 60 +++++++- core/libs/tests/GuestVdecsw.cpp | 197 ++++++++++++++++++++++++++ core/libs/tests/GuestVideodec2.cpp | 97 +------------ core/libs/tests/Vp9Fixture.hpp | 108 ++++++++++++++ docs/dev/debt/libSceVdecsw.md | 2 + 5 files changed, 363 insertions(+), 101 deletions(-) create mode 100644 core/libs/tests/Vp9Fixture.hpp diff --git a/core/libs/prx/libSceVdecsw/Export.cpp b/core/libs/prx/libSceVdecsw/Export.cpp index cab091bb32..4c70113346 100644 --- a/core/libs/prx/libSceVdecsw/Export.cpp +++ b/core/libs/prx/libSceVdecsw/Export.cpp @@ -17,6 +17,7 @@ namespace { constexpr int VdecswErrorOutputPending = static_cast(0x81510115u); constexpr int VdecswErrorInputQueueEmpty = static_cast(0x81510116u); constexpr int VdecswErrorDecodePending = static_cast(0x81510117u); +constexpr std::uint32_t CodecVp9 = 2382845; constexpr std::size_t MaxQueuedPictures = 4; struct InputData { @@ -58,6 +59,26 @@ struct OutputInfo { std::uint32_t framePitchInBytes; }; +struct Vp9PictureInfo { + std::uint64_t thisSize; + bool isValid; + std::uint64_t ptsData; + std::uint64_t dtsData; + std::uint64_t attachedData; + std::uint32_t profile; + std::uint32_t level; + std::uint32_t keyFrameFlag; + std::uint32_t intraOnly; + std::uint8_t bitDepth; + std::uint8_t colorRange; + std::uint32_t colorSpace; + std::uint32_t numFrames; + std::uint32_t frameWidth; + std::uint32_t frameHeight; + std::uint32_t renderWidth; + std::uint32_t renderHeight; +}; + struct AvcPictureInfo { std::uint64_t thisSize; bool isValid; @@ -79,6 +100,7 @@ struct AvcPictureInfo { }; static_assert(sizeof(InputData) == 0x30 && sizeof(InputResult) == 0x18 && sizeof(FrameBuffer) == 0x18); +static_assert(sizeof(Vp9PictureInfo) == 0x58 && offsetof(Vp9PictureInfo, ptsData) == 0x10 && offsetof(Vp9PictureInfo, profile) == 0x28 && offsetof(Vp9PictureInfo, bitDepth) == 0x38 && offsetof(Vp9PictureInfo, colorSpace) == 0x3c && offsetof(Vp9PictureInfo, renderHeight) == 0x50); static_assert(sizeof(OutputInfo) == 0x38 && offsetof(OutputInfo, isLastFrame) == 9 && offsetof(OutputInfo, pictureCount) == 0xb && offsetof(OutputInfo, isDiscardedFrame) == 0x1c); static_assert(sizeof(AvcPictureInfo) == 0x78 && offsetof(AvcPictureInfo, picWidthInMbsMinus1) == 0x2c && offsetof(AvcPictureInfo, frameCropBottomOffset) == 0x44); @@ -147,6 +169,7 @@ int APS5_VABI sceVideodec2CreateDecoder_nid_postfix(const void* config, const vo int APS5_VABI sceVideodec2DeleteDecoder_nid_postfix(std::uint64_t handle); int APS5_VABI sceVideodec2Decode_nid_postfix(std::uint64_t handle, const InputData* input, Videodec2FrameBuffer* frame, Videodec2OutputInfo* output); int APS5_VABI sceVideodec2Flush_nid_postfix(std::uint64_t handle, Videodec2FrameBuffer* frame, Videodec2OutputInfo* output); +int APS5_VABI sceVideodec2GetVp9PictureInfo_nid_postfix(const Videodec2OutputInfo* output, Vp9PictureInfo* info); int APS5_VABI sceVideodec2Reset_nid_postfix(std::uint64_t handle); int APS5_VABI sceVideodec2GetAvcPictureInfo_nid_postfix(const Videodec2OutputInfo* output, Videodec2AvcPictureInfo* first, Videodec2AvcPictureInfo* second); void Videodec2PictureCrop_nid_no_patch(std::uint64_t handle, const void* frameBuffer, Videodec2Crop* crop); @@ -180,6 +203,7 @@ struct Decoder { std::map delivered; bool finalized = false; bool drained = false; + std::map deliveredVp9; }; std::mutex decodersMutex; @@ -220,6 +244,13 @@ AvcPictureInfo pictureInfo(std::uint64_t handle, const Videodec2OutputInfo& outp return info; } +Vp9PictureInfo vp9PictureInfo(const Videodec2OutputInfo& output) { + Vp9PictureInfo info{sizeof(Vp9PictureInfo)}; + sceVideodec2GetVp9PictureInfo_nid_postfix(&output, &info); + if (!info.isValid) throw std::runtime_error("Vdecsw: decoded picture has no VP9 picture info"); + return info; +} + void advance(std::uint64_t handle, Decoder& decoder) { while (decoder.pictures.size() < MaxQueuedPictures) { std::vector pixels(decoder.frameBytes); @@ -239,7 +270,7 @@ void advance(std::uint64_t handle, Decoder& decoder) { return; } if (output.isValid) { - const auto info = pictureInfo(handle, output); + const auto info = output.codecType == CodecVp9 ? AvcPictureInfo{} : pictureInfo(handle, output); decoder.pictures.push_back({std::move(pixels), output, info}); } } @@ -361,7 +392,8 @@ int APS5_VABI sceVdecswTrySyncDecodeOutput(std::uint64_t handle, OutputInfo* out output->frameBufferSize = picture.output.frameBufferSize; output->frameFormat = picture.output.frameFormat; output->framePitchInBytes = picture.output.framePitchInBytes; - decoder->delivered[target.frameBuffer] = picture.info; + if (picture.output.codecType == CodecVp9) decoder->deliveredVp9[target.frameBuffer] = vp9PictureInfo(picture.output); + else decoder->delivered[target.frameBuffer] = picture.info; return 0; } @@ -409,11 +441,24 @@ int APS5_VABI sceVdecswGetHevcPictureInfo(const OutputInfo* output, void* pictur return 0; } -int APS5_VABI sceVdecswGetVp9PictureInfo(const OutputInfo* output, void* picture) { - (void)output; - (void)picture; - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceVdecswGetVp9PictureInfo(const OutputInfo* output, Vp9PictureInfo* picture) { + if (output == nullptr || picture == nullptr || picture->thisSize < sizeof(std::uint64_t)) throw std::runtime_error("Vdecsw: invalid argument pointer"); + if (output->pictureCount > 1) throw std::runtime_error("Vdecsw: more than one VP9 picture info is not implemented"); + if (output->pictureCount == 0) return 0; + std::vector> all; + { + std::lock_guard lock(decodersMutex); + for (const auto& [handle, decoder] : decoders) all.push_back(decoder); + } + for (const auto& decoder : all) { + std::lock_guard lock(decoder->mutex); + const auto found = decoder->deliveredVp9.find(output->frameBuffer); + if (found == decoder->deliveredVp9.end()) continue; + const auto bytes = std::min(picture->thisSize, sizeof(Vp9PictureInfo)) - sizeof(std::uint64_t); + std::memcpy(reinterpret_cast(picture) + sizeof(std::uint64_t), reinterpret_cast(&found->second) + sizeof(std::uint64_t), bytes); + return 0; + } + throw std::runtime_error("Vdecsw: VP9 picture info requested for a frame buffer no decoder returned"); } int APS5_VABI sceVdecswFinalizeDecodeSequence(std::uint64_t handle) { @@ -432,6 +477,7 @@ int APS5_VABI sceVdecswResetDecoder(std::uint64_t handle) { decoder->outputs.clear(); decoder->pictures.clear(); decoder->delivered.clear(); + decoder->deliveredVp9.clear(); decoder->finalized = false; decoder->drained = false; return sceVideodec2Reset_nid_postfix(handle); diff --git a/core/libs/tests/GuestVdecsw.cpp b/core/libs/tests/GuestVdecsw.cpp index 72f76ec270..14fdca44a9 100644 --- a/core/libs/tests/GuestVdecsw.cpp +++ b/core/libs/tests/GuestVdecsw.cpp @@ -2,10 +2,12 @@ #include #include #include +#include #include #include #include #include "prx/libc/include/General.hpp" +#include "Vp9Fixture.hpp" #include "H264Fixture.hpp" struct ComputeMemoryInfo { @@ -89,6 +91,28 @@ struct OutputInfo { std::uint32_t framePitchInBytes; }; +struct Vp9PictureInfo { + std::uint64_t thisSize; + bool isValid; + std::uint64_t ptsData; + std::uint64_t dtsData; + std::uint64_t attachedData; + std::uint32_t profile; + std::uint32_t level; + std::uint32_t keyFrameFlag; + std::uint32_t intraOnly; + std::uint8_t bitDepth; + std::uint8_t colorRange; + std::uint32_t colorSpace; + std::uint32_t numFrames; + std::uint32_t frameWidth; + std::uint32_t frameHeight; + std::uint32_t renderWidth; + std::uint32_t renderHeight; +}; + +static_assert(sizeof(Vp9PictureInfo) == 0x58 && offsetof(Vp9PictureInfo, ptsData) == 0x10 && offsetof(Vp9PictureInfo, profile) == 0x28 && offsetof(Vp9PictureInfo, bitDepth) == 0x38 && offsetof(Vp9PictureInfo, colorSpace) == 0x3c && offsetof(Vp9PictureInfo, renderHeight) == 0x50); + struct AvcPictureInfo { std::uint64_t thisSize; bool isValid; @@ -124,6 +148,7 @@ int APS5_VABI sceVdecswSyncDecodeInput(std::uint64_t, InputResult*); int APS5_VABI sceVdecswSetDecodeOutput(std::uint64_t, const FrameBuffer*); int APS5_VABI sceVdecswTrySyncDecodeOutput(std::uint64_t, OutputInfo*); int APS5_VABI sceVdecswSyncDecodeOutput(std::uint64_t, OutputInfo*); +int APS5_VABI sceVdecswGetVp9PictureInfo(const OutputInfo*, Vp9PictureInfo*); int APS5_VABI sceVdecswGetAvcPictureInfo(const OutputInfo*, AvcPictureInfo*, AvcPictureInfo*); int APS5_VABI sceVdecswGetHevcPictureInfo(const OutputInfo*, void*); int APS5_VABI sceVdecswFinalizeDecodeSequence(std::uint64_t); @@ -133,9 +158,11 @@ int APS5_VABI sceVdecswResetDecoder(std::uint64_t); namespace { using namespace H264Fixture; +using namespace Vp9Fixture; constexpr int OutputPending = static_cast(0x81510115u); constexpr int InputQueueEmpty = static_cast(0x81510116u); +constexpr std::uint32_t CodecVp9 = 2382845; void check(bool condition, const std::string& message) { if (!condition) throw std::runtime_error(message); @@ -260,6 +287,174 @@ void testSyncWrappers() { check(hevcThrows, "GetHevcPictureInfo must still throw"); } +struct Vp9Session { + std::uint64_t queue = 0; + std::uint64_t decoder = 0; + DecoderMemoryInfo memory{sizeof(DecoderMemoryInfo)}; + + Vp9Session() { + ComputeMemoryInfo compute{sizeof(ComputeMemoryInfo)}; + check(sceVdecswQueryComputeMemoryInfo(&compute) == 0 && compute.cpuGpuMemorySize != 0, "compute memory query failed"); + const ComputeConfigInfo computeConfig{sizeof(ComputeConfigInfo), 3, 3, true}; + check(sceVdecswAllocateComputeQueue(&computeConfig, &compute, &queue) == 0, "compute queue allocation failed"); + DecoderConfigInfo config{sizeof(DecoderConfigInfo), 1, CodecVp9, 0, 0, static_cast(Width), static_cast(Height), 4, 3, queue, 0x3f, 0, true, {}}; + check(sceVdecswQueryDecoderMemoryInfo(&config, &memory) == 0 && memory.maxFrameBufferSize != 0, "VP9 decoder memory query failed"); + check(sceVdecswCreateDecoder(&config, &memory, &decoder) == 0, "VP9 decoder creation failed"); + } + + ~Vp9Session() { + sceVdecswDeleteDecoder(decoder); + sceVdecswReleaseComputeQueue(queue); + } +}; + +template +bool throws(TAction action) { + try { + action(); + } catch (const std::exception&) { + return true; + } + return false; +} + +Vp9PictureInfo sentinelVp9Info(std::uint64_t size = sizeof(Vp9PictureInfo)) { + Vp9PictureInfo info; + std::memset(&info, 0xcc, sizeof(info)); + info.thisSize = size; + return info; +} + +bool sameBytes(const Vp9PictureInfo& left, const Vp9PictureInfo& right) { + return std::memcmp(&left, &right, sizeof(Vp9PictureInfo)) == 0; +} + +void checkVp9Info(const Vp9PictureInfo& info, std::size_t picture) { + const std::string name = "VP9 picture " + std::to_string(picture); + check(info.thisSize == sizeof(Vp9PictureInfo) && info.isValid, name + " has no picture info"); + check(info.ptsData == 1000 + picture && info.dtsData == picture && info.attachedData == 0xa0 + picture, name + " timestamps differ"); + check(info.profile == 0 && info.bitDepth == 8 && info.colorRange == 0 && info.colorSpace == 0 && info.numFrames == 1 && info.intraOnly == 0, name + " coded header fields differ"); + check(info.keyFrameFlag == (picture == 0 ? 1u : 0u), name + " key frame flag differs"); + check(info.frameWidth == Width && info.frameHeight == Height && info.renderWidth == Width && info.renderHeight == Height, name + " dimensions differ"); +} + +void testVp9PictureInfo() { + Vp9Session session; + const auto units = ivfFrames(); + check(units.size() == Vp9Hashes.size(), "unexpected VP9 access unit count"); + std::array, Vp9Hashes.size()> buffers; + for (auto& buffer : buffers) buffer.resize(session.memory.maxFrameBufferSize); + std::array outputs{}; + std::size_t pictures = 0; + bool outputSet = false; + bool ended = false; + const auto drain = [&] { + for (;;) { + auto& target = buffers[pictures % buffers.size()]; + if (!outputSet) { + const FrameBuffer frame{sizeof(FrameBuffer), target.data(), target.size()}; + check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "VP9 output buffer was rejected"); + outputSet = true; + } + OutputInfo output{sizeof(OutputInfo)}; + const auto status = sceVdecswTrySyncDecodeOutput(session.decoder, &output); + if (status == OutputPending) return; + check(status == 0, "VP9 output sync failed"); + outputSet = false; + if (!output.isValid) { + ended = true; + return; + } + check(pictures < Vp9Hashes.size(), "more VP9 pictures than access units"); + check(output.codecType == CodecVp9 && output.pictureCount == 1 && output.frameWidth == Width && output.frameHeight == Height && output.frameBuffer == target.data(), "unexpected VP9 picture geometry"); + auto info = sentinelVp9Info(); + check(sceVdecswGetVp9PictureInfo(&output, &info) == 0, "VP9 picture info query failed"); + checkVp9Info(info, pictures); + check(hashNv12(target.data(), output.framePitch, output.frameHeight) == Vp9Hashes[pictures], "VP9 picture " + std::to_string(pictures) + " differs from the reference"); + outputs[pictures] = output; + ++pictures; + } + }; + for (std::size_t unit = 0; unit < units.size(); ++unit) { + const InputData input{sizeof(InputData), units[unit].data(), units[unit].size(), 1000 + unit, unit, 0xa0 + unit}; + check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "VP9 decode input was rejected"); + InputResult consumed{sizeof(InputResult)}; + check(sceVdecswTrySyncDecodeInput(session.decoder, &consumed) == 0 && consumed.decodedAu == units[unit].data(), "a VP9 access unit was not consumed"); + drain(); + } + check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "VP9 finalize failed"); + while (!ended) drain(); + check(pictures == Vp9Hashes.size(), "missing VP9 pictures after finalizing the sequence"); + + for (std::size_t picture = 0; picture < outputs.size(); ++picture) { + auto info = sentinelVp9Info(); + check(sceVdecswGetVp9PictureInfo(&outputs[picture], &info) == 0, "VP9 picture info of an earlier picture was lost"); + checkVp9Info(info, picture); + } + + const auto unchanged = sentinelVp9Info(); + auto info = unchanged; + check(throws([&] { sceVdecswGetVp9PictureInfo(nullptr, &info); }) && sameBytes(info, unchanged), "a null output info was accepted"); + check(throws([&] { sceVdecswGetVp9PictureInfo(&outputs[0], nullptr); }), "a null VP9 picture info was accepted"); + auto tooSmall = sentinelVp9Info(7); + const auto tooSmallBefore = tooSmall; + check(throws([&] { sceVdecswGetVp9PictureInfo(&outputs[0], &tooSmall); }) && sameBytes(tooSmall, tooSmallBefore), "a VP9 picture info smaller than its size field was accepted"); + auto doubled = outputs[0]; + doubled.pictureCount = 2; + check(throws([&] { sceVdecswGetVp9PictureInfo(&doubled, &info); }) && sameBytes(info, unchanged), "an output with two pictures was accepted"); + auto empty = outputs[0]; + empty.pictureCount = 0; + check(sceVdecswGetVp9PictureInfo(&empty, &info) == 0 && sameBytes(info, unchanged), "an output without a picture wrote a VP9 picture info"); + std::uint8_t foreign = 0; + auto unknown = outputs[0]; + unknown.frameBuffer = &foreign; + check(throws([&] { sceVdecswGetVp9PictureInfo(&unknown, &info); }) && sameBytes(info, unchanged), "a frame buffer no decoder returned was accepted"); + unknown.frameBuffer = nullptr; + check(throws([&] { sceVdecswGetVp9PictureInfo(&unknown, &info); }) && sameBytes(info, unchanged), "a null frame buffer was accepted"); + + const auto head = offsetof(Vp9PictureInfo, profile); + auto partial = sentinelVp9Info(head); + check(sceVdecswGetVp9PictureInfo(&outputs[2], &partial) == 0, "a VP9 picture info with a short size field was rejected"); + check(partial.thisSize == head && partial.isValid && partial.ptsData == 1002 && partial.dtsData == 2 && partial.attachedData == 0xa2, "the leading VP9 picture info fields were not written"); + check(partial.profile == unchanged.profile && partial.frameWidth == unchanged.frameWidth && partial.renderHeight == unchanged.renderHeight, "a VP9 picture info with a short size field was written beyond it"); + + struct Oversized { + Vp9PictureInfo info; + std::uint8_t tail[64]; + } oversized; + std::memset(&oversized, 0xcc, sizeof(oversized)); + oversized.info.thisSize = sizeof(Oversized); + check(sceVdecswGetVp9PictureInfo(&outputs[3], &oversized.info) == 0, "a VP9 picture info with a long size field was rejected"); + check(oversized.info.thisSize == sizeof(Oversized), "a long VP9 picture info size field was changed"); + oversized.info.thisSize = sizeof(Vp9PictureInfo); + checkVp9Info(oversized.info, 3); + for (const auto byte : oversized.tail) check(byte == 0xcc, "a VP9 picture info with a long size field was written beyond the structure"); + + AvcPictureInfo avc{sizeof(AvcPictureInfo)}; + check(throws([&] { sceVdecswGetAvcPictureInfo(&outputs[0], &avc, nullptr); }), "AVC picture info was returned for a VP9 picture"); + + check(sceVdecswResetDecoder(session.decoder) == 0, "VP9 reset failed"); + check(throws([&] { sceVdecswGetVp9PictureInfo(&outputs[0], &info); }) && sameBytes(info, unchanged), "VP9 picture info survived a decoder reset"); +} + +void testVp9PictureInfoOfAvcPicture() { + Session session; + const auto units = accessUnits(false); + const InputData input{sizeof(InputData), units[0].data(), units[0].size(), 1000, 0, 0xa0}; + check(sceVdecswSetDecodeInput(session.decoder, &input) == 0, "decode input was rejected"); + InputResult result{sizeof(InputResult)}; + check(sceVdecswSyncDecodeInput(session.decoder, &result) == 0, "the access unit was not consumed"); + std::vector buffer(session.memory.maxFrameBufferSize); + const FrameBuffer frame{sizeof(FrameBuffer), buffer.data(), buffer.size()}; + check(sceVdecswSetDecodeOutput(session.decoder, &frame) == 0, "output buffer was rejected"); + check(sceVdecswFinalizeDecodeSequence(session.decoder) == 0, "finalize failed"); + OutputInfo output{sizeof(OutputInfo)}; + check(sceVdecswSyncDecodeOutput(session.decoder, &output) == 0 && output.isValid && output.pictureCount == 1, "sync output did not return the finalized picture"); + const auto unchanged = sentinelVp9Info(); + auto info = unchanged; + check(throws([&] { sceVdecswGetVp9PictureInfo(&output, &info); }) && sameBytes(info, unchanged), "VP9 picture info was returned for an H.264 picture"); +} + } int main() { @@ -267,6 +462,8 @@ int main() { testDecode(); testWithoutOutput(); testSyncWrappers(); + testVp9PictureInfo(); + testVp9PictureInfoOfAvcPicture(); std::puts("Vdecsw tests passed"); return 0; } catch (const std::exception& error) { diff --git a/core/libs/tests/GuestVideodec2.cpp b/core/libs/tests/GuestVideodec2.cpp index 91ee70f9c7..477c09481c 100644 --- a/core/libs/tests/GuestVideodec2.cpp +++ b/core/libs/tests/GuestVideodec2.cpp @@ -11,6 +11,7 @@ #include #include "prx/libc/include/General.hpp" #include "H264Fixture.hpp" +#include "Vp9Fixture.hpp" struct DecoderConfigInfo { std::uint64_t thisSize; @@ -190,90 +191,6 @@ constexpr std::array HevcHashes{ 0xe39bc891f8286267ull, 0x734caf1ba961b3c2ull, 0x7deb44bd1ed30531ull, }; -constexpr std::array Vp9Stream{ - 0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x40, 0x00, 0x28, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x49, 0x83, 0x42, - 0x00, 0x03, 0xf0, 0x02, 0x76, 0x08, 0x38, 0x24, 0x1c, 0x18, 0x0e, 0x00, 0x01, 0xc0, 0x6f, 0xb1, - 0xfb, 0x17, 0xa6, 0xd2, 0x0f, 0x5f, 0xee, 0xf7, 0xec, 0xbf, 0xb4, 0x0c, 0x93, 0xbe, 0x87, 0x11, - 0x35, 0x1c, 0x51, 0x84, 0x8b, 0x70, 0xad, 0xe4, 0x00, 0x00, 0x79, 0x3b, 0xff, 0xff, 0xf5, 0x94, - 0xad, 0x57, 0x6e, 0xae, 0x8e, 0x1b, 0xfe, 0x64, 0xff, 0xc9, 0x64, 0xba, 0xca, 0x1d, 0x7f, 0x45, - 0x16, 0x95, 0x13, 0x8d, 0xd0, 0x61, 0x19, 0x80, 0xb7, 0xdf, 0xff, 0xff, 0xd6, 0x52, 0x1e, 0x7b, - 0x3c, 0x16, 0x3f, 0xff, 0xfb, 0x52, 0xe7, 0x26, 0xab, 0xd6, 0x3a, 0x6a, 0xff, 0xf8, 0x1b, 0x47, - 0xcd, 0xe2, 0xf5, 0x48, 0x65, 0x1e, 0x9f, 0x5c, 0x1a, 0x74, 0x0a, 0x99, 0xc0, 0x04, 0x41, 0x02, - 0xa9, 0x86, 0x36, 0x1f, 0xff, 0xf3, 0x4f, 0x71, 0xdf, 0x8a, 0xcd, 0x8a, 0x88, 0xdd, 0x37, 0xa2, - 0x27, 0x3a, 0x20, 0xb8, 0xf0, 0x4f, 0x67, 0x59, 0x8a, 0x82, 0xa2, 0xde, 0x08, 0xe3, 0xae, 0x87, - 0xff, 0xf0, 0x36, 0x8f, 0x9b, 0xc5, 0xea, 0x90, 0xca, 0x3d, 0x3e, 0xb8, 0x34, 0xe8, 0x15, 0x33, - 0x80, 0x08, 0x82, 0x05, 0x53, 0x0c, 0x6c, 0x3f, 0xff, 0xe6, 0x9e, 0xe3, 0xbf, 0x15, 0x9d, 0xec, - 0xdf, 0xe9, 0xce, 0x60, 0x88, 0x09, 0x48, 0x81, 0xa2, 0xb6, 0x27, 0x3e, 0xf9, 0xa9, 0x7e, 0xa7, - 0xe1, 0x9d, 0xdd, 0x3a, 0xa0, 0x39, 0x3f, 0x8a, 0xba, 0x73, 0x50, 0xde, 0xcf, 0x29, 0x5c, 0x2a, - 0x92, 0x01, 0xec, 0x06, 0xbe, 0x8b, 0x1e, 0x14, 0x34, 0x6b, 0xb1, 0x3a, 0x01, 0x47, 0x08, 0xe7, - 0xbe, 0xb0, 0xa7, 0x03, 0x77, 0xeb, 0xf0, 0xed, 0xd8, 0xa1, 0x4e, 0x7c, 0xd9, 0xe4, 0x7c, 0x9d, - 0x67, 0xfe, 0x36, 0xab, 0x5e, 0xa9, 0x5e, 0x81, 0x74, 0x68, 0xa1, 0x4b, 0x61, 0x81, 0xe2, 0xe4, - 0x4c, 0x6a, 0x4f, 0xe5, 0x65, 0x3f, 0x4c, 0x78, 0xea, 0x68, 0x5c, 0xf9, 0xc6, 0x58, 0x1f, 0x58, - 0xe0, 0x40, 0x4b, 0x88, 0x6d, 0x15, 0x94, 0x8c, 0x83, 0x4a, 0xa3, 0xf3, 0xd5, 0x80, 0x7f, 0x83, - 0x52, 0x7d, 0xbe, 0xa6, 0x9f, 0xb4, 0x89, 0xda, 0xb6, 0x5a, 0xdf, 0x1f, 0x78, 0xd8, 0xbf, 0xfe, - 0x7f, 0x52, 0xfa, 0xfd, 0xa4, 0xeb, 0xda, 0x91, 0x9c, 0xb6, 0xd0, 0xc3, 0xfd, 0x8e, 0x04, 0x04, - 0xb8, 0x86, 0xd1, 0x56, 0xa2, 0x24, 0xe6, 0xbc, 0x5b, 0xbf, 0x82, 0x36, 0xb5, 0xa7, 0x87, 0x73, - 0xd8, 0xbd, 0x30, 0x33, 0x3f, 0x7c, 0xbb, 0x4f, 0x70, 0xed, 0x3f, 0x8d, 0x2b, 0xe2, 0xce, 0x62, - 0x09, 0xfa, 0x04, 0x31, 0x67, 0xff, 0x09, 0xc1, 0x5c, 0x29, 0xf8, 0x77, 0xfa, 0xb6, 0xa7, 0xde, - 0xaa, 0xc1, 0x87, 0xd9, 0xb4, 0x10, 0x0d, 0xda, 0x5d, 0xf7, 0xdd, 0x7d, 0x08, 0x7a, 0xbe, 0x60, - 0xeb, 0x85, 0x00, 0x08, 0x21, 0x8e, 0xa0, 0x4d, 0xff, 0x73, 0x66, 0x55, 0xb8, 0x61, 0x69, 0xc5, - 0xf2, 0xef, 0x96, 0x27, 0x16, 0x2f, 0x0d, 0x93, 0xcd, 0xcd, 0xba, 0x2a, 0x80, 0x59, 0x90, 0x2b, - 0xc5, 0x43, 0x49, 0x25, 0x49, 0x01, 0x15, 0x9f, 0xf4, 0xdd, 0x02, 0x31, 0x8b, 0xf0, 0xbe, 0x28, - 0x87, 0xbd, 0x3f, 0xce, 0xb1, 0x6e, 0xd2, 0x7f, 0x77, 0x41, 0x09, 0x8a, 0x04, 0x17, 0x87, 0x57, - 0x1b, 0x72, 0x79, 0x69, 0x41, 0x7d, 0xc5, 0x78, 0xb7, 0x53, 0xe7, 0x80, 0xd2, 0x7f, 0x1f, 0xb9, - 0xd5, 0x0f, 0xff, 0xf5, 0xb8, 0xd0, 0xc5, 0x47, 0x30, 0x09, 0x36, 0x6e, 0xb1, 0xfd, 0xe1, 0xe9, - 0xf9, 0x81, 0x1d, 0x98, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x46, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x76, 0x21, - 0x2d, 0x37, 0xff, 0xff, 0xc3, 0x7f, 0xff, 0xff, 0xff, 0xd8, 0x59, 0x6b, 0xff, 0xa9, 0xff, 0xff, - 0xff, 0xda, 0x3d, 0xca, 0x5b, 0xc7, 0xff, 0xfd, 0x52, 0xf6, 0x2e, 0x02, 0xe0, 0x37, 0xe3, 0x5f, - 0xff, 0xea, 0x82, 0x5b, 0xff, 0xff, 0xfa, 0x7b, 0x85, 0xdf, 0x66, 0xbf, 0xff, 0xe6, 0xde, 0xef, - 0xed, 0x97, 0xff, 0xf9, 0x38, 0x4a, 0x7f, 0x61, 0xb5, 0xeb, 0x56, 0x73, 0xa7, 0xff, 0xff, 0x31, - 0x8b, 0xa1, 0xcf, 0xff, 0x5b, 0x90, 0x07, 0x17, 0xff, 0xd6, 0xe1, 0x74, 0x39, 0xff, 0xeb, 0x72, - 0x5d, 0x14, 0xff, 0xf5, 0xb8, 0x95, 0x54, 0x7f, 0xf7, 0x5d, 0x1e, 0x26, 0x68, 0x00, 0x7f, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, - 0x45, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x7d, 0xb8, 0xff, 0xff, 0xb4, 0x7f, 0xff, 0xff, 0xfe, - 0x4d, 0x81, 0xbf, 0xff, 0x98, 0x9f, 0xff, 0xff, 0xe4, 0xd8, 0x1b, 0xff, 0xf9, 0x89, 0xff, 0xff, - 0xfe, 0x4d, 0x81, 0xbf, 0xd6, 0xbf, 0xff, 0xf9, 0x36, 0x0c, 0x1f, 0xff, 0xfc, 0x10, 0x3e, 0xfe, - 0x1f, 0xa1, 0x76, 0x5c, 0x22, 0x5f, 0x3b, 0x56, 0x57, 0xa0, 0x3c, 0x43, 0xf0, 0xc8, 0x00, 0x00, - 0x00, 0x19, 0xbb, 0xff, 0xff, 0xd8, 0x43, 0x10, 0x26, 0x67, 0xff, 0xff, 0xfe, 0x4a, 0x07, 0x8f, - 0x80, 0x7f, 0xff, 0xff, 0x94, 0xd2, 0x59, 0x02, 0xff, 0xff, 0x0d, 0x89, 0x18, 0x02, 0xff, 0xff, - 0x0d, 0x84, 0xb2, 0x05, 0xff, 0xfe, 0x1b, 0x14, 0xdd, 0x42, 0xff, 0xff, 0x0d, 0x82, 0xc8, 0xb1, - 0xff, 0xf9, 0x5c, 0x32, 0xfe, 0x00, 0x78, 0x60, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x20, 0x46, 0x00, 0x00, 0x03, 0x60, - 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xc3, 0x7f, 0xff, 0xff, 0xff, 0xd8, 0x19, 0x3f, 0xff, 0xe6, - 0x1b, 0xfb, 0x80, 0x95, 0x82, 0x3b, 0xd9, 0x4c, 0x81, 0xce, 0x52, 0x2b, 0xff, 0xff, 0xa0, 0xad, - 0xc9, 0x0e, 0x62, 0xff, 0xff, 0xff, 0x60, 0xf8, 0x75, 0x5f, 0xff, 0xff, 0x31, 0x92, 0xaa, 0x8f, - 0xfe, 0xeb, 0xa4, 0x4e, 0x5f, 0xff, 0x5b, 0x89, 0x55, 0x47, 0xff, 0x75, 0xd4, 0xd9, 0xef, 0xff, - 0xe4, 0x51, 0x64, 0x58, 0xff, 0xee, 0xba, 0xfc, 0x8d, 0x8f, 0x00, 0x95, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x44, 0x00, 0x00, - 0x03, 0x60, 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xfb, 0x50, 0x3f, 0xff, 0xff, 0xff, 0xea, 0xc5, - 0x0d, 0xff, 0xff, 0xe0, 0x09, 0xfc, 0xad, 0x23, 0x5f, 0xf8, 0xa3, 0x64, 0x9b, 0x40, 0x76, 0x8d, - 0xd6, 0xd1, 0x83, 0x6f, 0x24, 0xd5, 0xff, 0xff, 0xd4, 0xa9, 0x45, 0xac, 0xda, 0xdc, 0xd2, 0xbf, - 0xff, 0xff, 0xfd, 0x58, 0xbf, 0x7f, 0x8a, 0x7f, 0xff, 0xff, 0xad, 0xb9, 0x2d, 0x3c, 0xbf, 0xff, - 0xd4, 0xca, 0x46, 0x7d, 0x7f, 0xff, 0xe1, 0x64, 0x96, 0x9e, 0x5f, 0xff, 0xea, 0x65, 0x4d, 0xe9, - 0x97, 0xff, 0xfa, 0x99, 0x16, 0x71, 0xaf, 0xff, 0xfc, 0x2c, 0x99, 0x7f, 0x5b, 0x8b, 0x50, 0x9c, - 0xdc, 0x43, 0xa8, 0xb7, 0xd5, 0x68, 0x6a, 0xbc, 0x78, 0xef, 0xc2, 0x3d, 0xfb, 0x3f, 0xcc, 0x51, - 0x40, 0x86, 0x54, 0x02, 0xa4, 0xb2, 0xbd, 0x79, 0x93, 0xa2, 0x4a, 0x35, 0x36, 0xc8, 0x7c, 0xdb, - 0xb0, 0x1d, 0xc1, 0xd0, 0x1d, 0x51, 0x9a, 0xe4, 0x19, 0xe6, 0x08, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x46, 0x00, - 0x00, 0x03, 0x60, 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xa9, 0xef, 0xff, 0xff, 0xff, 0xd8, 0x59, - 0x9f, 0xff, 0x53, 0xff, 0xff, 0xff, 0xb4, 0x78, 0x24, 0x37, 0xff, 0xfb, 0x89, 0x98, 0x26, 0xc0, - 0xe3, 0x5f, 0x50, 0x5c, 0x2f, 0xff, 0xff, 0xf1, 0xd2, 0xd2, 0x4d, 0xfd, 0xf2, 0x7f, 0xff, 0x9f, - 0xff, 0x17, 0x85, 0xd2, 0xab, 0x4b, 0xbe, 0x3d, 0xff, 0xff, 0xff, 0xdf, 0x55, 0xbf, 0xff, 0xff, - 0xff, 0xfe, 0x6e, 0xe3, 0xe4, 0x07, 0x53, 0xd2, 0x5f, 0xff, 0xff, 0xff, 0xbe, 0xab, 0x7f, 0xff, - 0xff, 0xff, 0xcd, 0xdc, 0x1a, 0xbf, 0xff, 0xfe, 0xfa, 0xad, 0xff, 0xff, 0xff, 0xcd, 0xdc, 0x4e, - 0x1d, 0x57, 0xff, 0xff, 0xe0, 0x44, 0xaa, 0xa3, 0xff, 0xd3, 0x89, 0x13, 0x97, 0xff, 0xe5, 0xa2, - 0x55, 0x51, 0xff, 0xe9, 0xc4, 0xba, 0x29, 0xff, 0xeb, 0x71, 0x2a, 0xa8, 0xff, 0xee, 0xb9, 0xc4, - 0x7f, 0xff, 0xfb, 0xea, 0xb7, 0xff, 0xff, 0xff, 0x37, 0x71, 0x29, 0xfa, 0x00, -}; - -constexpr std::array Vp9Hashes{ - 0x552fd3c320b37408ull, 0xb2fb78bac8d2d7dfull, 0x312469345c491c3dull, - 0x9aeb0fb400c95a75ull, 0xe02e07646faa48beull, 0x1399072031abb375ull, -}; - constexpr std::array HevcMain10Stream{ 0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x1e, @@ -357,16 +274,8 @@ std::vector> hevcUnits(const std::uint8_t* stream, std return units; } -std::vector> ivfFrames() { - std::vector> frames; - for (std::size_t index = 32; index + 12 <= Vp9Stream.size();) { - const std::size_t length = Vp9Stream[index] | (Vp9Stream[index + 1] << 8u) | (Vp9Stream[index + 2] << 16u) | (static_cast(Vp9Stream[index + 3]) << 24u); - check(index + 12 + length <= Vp9Stream.size(), "truncated IVF frame"); - frames.emplace_back(Vp9Stream.begin() + static_cast(index + 12), Vp9Stream.begin() + static_cast(index + 12 + length)); - index += 12 + length; - } - return frames; -} +using Vp9Fixture::Vp9Hashes; +using Vp9Fixture::ivfFrames; void testClip(std::uint32_t codec, std::uint32_t profile, const std::vector>& units, const std::array& hashes) { const DecoderConfigInfo config{sizeof(DecoderConfigInfo), 1, codec, profile, 0, static_cast(Width), static_cast(Height), 4, 1}; diff --git a/core/libs/tests/Vp9Fixture.hpp b/core/libs/tests/Vp9Fixture.hpp new file mode 100644 index 0000000000..9878c4a620 --- /dev/null +++ b/core/libs/tests/Vp9Fixture.hpp @@ -0,0 +1,108 @@ +#ifndef CORE_LIBS_TESTS_VP9FIXTURE_HPP +#define CORE_LIBS_TESTS_VP9FIXTURE_HPP + +#include +#include +#include +#include + +namespace Vp9Fixture { + +inline constexpr std::array Vp9Stream{ + 0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x40, 0x00, 0x28, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x49, 0x83, 0x42, + 0x00, 0x03, 0xf0, 0x02, 0x76, 0x08, 0x38, 0x24, 0x1c, 0x18, 0x0e, 0x00, 0x01, 0xc0, 0x6f, 0xb1, + 0xfb, 0x17, 0xa6, 0xd2, 0x0f, 0x5f, 0xee, 0xf7, 0xec, 0xbf, 0xb4, 0x0c, 0x93, 0xbe, 0x87, 0x11, + 0x35, 0x1c, 0x51, 0x84, 0x8b, 0x70, 0xad, 0xe4, 0x00, 0x00, 0x79, 0x3b, 0xff, 0xff, 0xf5, 0x94, + 0xad, 0x57, 0x6e, 0xae, 0x8e, 0x1b, 0xfe, 0x64, 0xff, 0xc9, 0x64, 0xba, 0xca, 0x1d, 0x7f, 0x45, + 0x16, 0x95, 0x13, 0x8d, 0xd0, 0x61, 0x19, 0x80, 0xb7, 0xdf, 0xff, 0xff, 0xd6, 0x52, 0x1e, 0x7b, + 0x3c, 0x16, 0x3f, 0xff, 0xfb, 0x52, 0xe7, 0x26, 0xab, 0xd6, 0x3a, 0x6a, 0xff, 0xf8, 0x1b, 0x47, + 0xcd, 0xe2, 0xf5, 0x48, 0x65, 0x1e, 0x9f, 0x5c, 0x1a, 0x74, 0x0a, 0x99, 0xc0, 0x04, 0x41, 0x02, + 0xa9, 0x86, 0x36, 0x1f, 0xff, 0xf3, 0x4f, 0x71, 0xdf, 0x8a, 0xcd, 0x8a, 0x88, 0xdd, 0x37, 0xa2, + 0x27, 0x3a, 0x20, 0xb8, 0xf0, 0x4f, 0x67, 0x59, 0x8a, 0x82, 0xa2, 0xde, 0x08, 0xe3, 0xae, 0x87, + 0xff, 0xf0, 0x36, 0x8f, 0x9b, 0xc5, 0xea, 0x90, 0xca, 0x3d, 0x3e, 0xb8, 0x34, 0xe8, 0x15, 0x33, + 0x80, 0x08, 0x82, 0x05, 0x53, 0x0c, 0x6c, 0x3f, 0xff, 0xe6, 0x9e, 0xe3, 0xbf, 0x15, 0x9d, 0xec, + 0xdf, 0xe9, 0xce, 0x60, 0x88, 0x09, 0x48, 0x81, 0xa2, 0xb6, 0x27, 0x3e, 0xf9, 0xa9, 0x7e, 0xa7, + 0xe1, 0x9d, 0xdd, 0x3a, 0xa0, 0x39, 0x3f, 0x8a, 0xba, 0x73, 0x50, 0xde, 0xcf, 0x29, 0x5c, 0x2a, + 0x92, 0x01, 0xec, 0x06, 0xbe, 0x8b, 0x1e, 0x14, 0x34, 0x6b, 0xb1, 0x3a, 0x01, 0x47, 0x08, 0xe7, + 0xbe, 0xb0, 0xa7, 0x03, 0x77, 0xeb, 0xf0, 0xed, 0xd8, 0xa1, 0x4e, 0x7c, 0xd9, 0xe4, 0x7c, 0x9d, + 0x67, 0xfe, 0x36, 0xab, 0x5e, 0xa9, 0x5e, 0x81, 0x74, 0x68, 0xa1, 0x4b, 0x61, 0x81, 0xe2, 0xe4, + 0x4c, 0x6a, 0x4f, 0xe5, 0x65, 0x3f, 0x4c, 0x78, 0xea, 0x68, 0x5c, 0xf9, 0xc6, 0x58, 0x1f, 0x58, + 0xe0, 0x40, 0x4b, 0x88, 0x6d, 0x15, 0x94, 0x8c, 0x83, 0x4a, 0xa3, 0xf3, 0xd5, 0x80, 0x7f, 0x83, + 0x52, 0x7d, 0xbe, 0xa6, 0x9f, 0xb4, 0x89, 0xda, 0xb6, 0x5a, 0xdf, 0x1f, 0x78, 0xd8, 0xbf, 0xfe, + 0x7f, 0x52, 0xfa, 0xfd, 0xa4, 0xeb, 0xda, 0x91, 0x9c, 0xb6, 0xd0, 0xc3, 0xfd, 0x8e, 0x04, 0x04, + 0xb8, 0x86, 0xd1, 0x56, 0xa2, 0x24, 0xe6, 0xbc, 0x5b, 0xbf, 0x82, 0x36, 0xb5, 0xa7, 0x87, 0x73, + 0xd8, 0xbd, 0x30, 0x33, 0x3f, 0x7c, 0xbb, 0x4f, 0x70, 0xed, 0x3f, 0x8d, 0x2b, 0xe2, 0xce, 0x62, + 0x09, 0xfa, 0x04, 0x31, 0x67, 0xff, 0x09, 0xc1, 0x5c, 0x29, 0xf8, 0x77, 0xfa, 0xb6, 0xa7, 0xde, + 0xaa, 0xc1, 0x87, 0xd9, 0xb4, 0x10, 0x0d, 0xda, 0x5d, 0xf7, 0xdd, 0x7d, 0x08, 0x7a, 0xbe, 0x60, + 0xeb, 0x85, 0x00, 0x08, 0x21, 0x8e, 0xa0, 0x4d, 0xff, 0x73, 0x66, 0x55, 0xb8, 0x61, 0x69, 0xc5, + 0xf2, 0xef, 0x96, 0x27, 0x16, 0x2f, 0x0d, 0x93, 0xcd, 0xcd, 0xba, 0x2a, 0x80, 0x59, 0x90, 0x2b, + 0xc5, 0x43, 0x49, 0x25, 0x49, 0x01, 0x15, 0x9f, 0xf4, 0xdd, 0x02, 0x31, 0x8b, 0xf0, 0xbe, 0x28, + 0x87, 0xbd, 0x3f, 0xce, 0xb1, 0x6e, 0xd2, 0x7f, 0x77, 0x41, 0x09, 0x8a, 0x04, 0x17, 0x87, 0x57, + 0x1b, 0x72, 0x79, 0x69, 0x41, 0x7d, 0xc5, 0x78, 0xb7, 0x53, 0xe7, 0x80, 0xd2, 0x7f, 0x1f, 0xb9, + 0xd5, 0x0f, 0xff, 0xf5, 0xb8, 0xd0, 0xc5, 0x47, 0x30, 0x09, 0x36, 0x6e, 0xb1, 0xfd, 0xe1, 0xe9, + 0xf9, 0x81, 0x1d, 0x98, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x46, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x76, 0x21, + 0x2d, 0x37, 0xff, 0xff, 0xc3, 0x7f, 0xff, 0xff, 0xff, 0xd8, 0x59, 0x6b, 0xff, 0xa9, 0xff, 0xff, + 0xff, 0xda, 0x3d, 0xca, 0x5b, 0xc7, 0xff, 0xfd, 0x52, 0xf6, 0x2e, 0x02, 0xe0, 0x37, 0xe3, 0x5f, + 0xff, 0xea, 0x82, 0x5b, 0xff, 0xff, 0xfa, 0x7b, 0x85, 0xdf, 0x66, 0xbf, 0xff, 0xe6, 0xde, 0xef, + 0xed, 0x97, 0xff, 0xf9, 0x38, 0x4a, 0x7f, 0x61, 0xb5, 0xeb, 0x56, 0x73, 0xa7, 0xff, 0xff, 0x31, + 0x8b, 0xa1, 0xcf, 0xff, 0x5b, 0x90, 0x07, 0x17, 0xff, 0xd6, 0xe1, 0x74, 0x39, 0xff, 0xeb, 0x72, + 0x5d, 0x14, 0xff, 0xf5, 0xb8, 0x95, 0x54, 0x7f, 0xf7, 0x5d, 0x1e, 0x26, 0x68, 0x00, 0x7f, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, + 0x45, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x7d, 0xb8, 0xff, 0xff, 0xb4, 0x7f, 0xff, 0xff, 0xfe, + 0x4d, 0x81, 0xbf, 0xff, 0x98, 0x9f, 0xff, 0xff, 0xe4, 0xd8, 0x1b, 0xff, 0xf9, 0x89, 0xff, 0xff, + 0xfe, 0x4d, 0x81, 0xbf, 0xd6, 0xbf, 0xff, 0xf9, 0x36, 0x0c, 0x1f, 0xff, 0xfc, 0x10, 0x3e, 0xfe, + 0x1f, 0xa1, 0x76, 0x5c, 0x22, 0x5f, 0x3b, 0x56, 0x57, 0xa0, 0x3c, 0x43, 0xf0, 0xc8, 0x00, 0x00, + 0x00, 0x19, 0xbb, 0xff, 0xff, 0xd8, 0x43, 0x10, 0x26, 0x67, 0xff, 0xff, 0xfe, 0x4a, 0x07, 0x8f, + 0x80, 0x7f, 0xff, 0xff, 0x94, 0xd2, 0x59, 0x02, 0xff, 0xff, 0x0d, 0x89, 0x18, 0x02, 0xff, 0xff, + 0x0d, 0x84, 0xb2, 0x05, 0xff, 0xfe, 0x1b, 0x14, 0xdd, 0x42, 0xff, 0xff, 0x0d, 0x82, 0xc8, 0xb1, + 0xff, 0xf9, 0x5c, 0x32, 0xfe, 0x00, 0x78, 0x60, 0x00, 0x56, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x20, 0x46, 0x00, 0x00, 0x03, 0x60, + 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xc3, 0x7f, 0xff, 0xff, 0xff, 0xd8, 0x19, 0x3f, 0xff, 0xe6, + 0x1b, 0xfb, 0x80, 0x95, 0x82, 0x3b, 0xd9, 0x4c, 0x81, 0xce, 0x52, 0x2b, 0xff, 0xff, 0xa0, 0xad, + 0xc9, 0x0e, 0x62, 0xff, 0xff, 0xff, 0x60, 0xf8, 0x75, 0x5f, 0xff, 0xff, 0x31, 0x92, 0xaa, 0x8f, + 0xfe, 0xeb, 0xa4, 0x4e, 0x5f, 0xff, 0x5b, 0x89, 0x55, 0x47, 0xff, 0x75, 0xd4, 0xd9, 0xef, 0xff, + 0xe4, 0x51, 0x64, 0x58, 0xff, 0xee, 0xba, 0xfc, 0x8d, 0x8f, 0x00, 0x95, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x44, 0x00, 0x00, + 0x03, 0x60, 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xfb, 0x50, 0x3f, 0xff, 0xff, 0xff, 0xea, 0xc5, + 0x0d, 0xff, 0xff, 0xe0, 0x09, 0xfc, 0xad, 0x23, 0x5f, 0xf8, 0xa3, 0x64, 0x9b, 0x40, 0x76, 0x8d, + 0xd6, 0xd1, 0x83, 0x6f, 0x24, 0xd5, 0xff, 0xff, 0xd4, 0xa9, 0x45, 0xac, 0xda, 0xdc, 0xd2, 0xbf, + 0xff, 0xff, 0xfd, 0x58, 0xbf, 0x7f, 0x8a, 0x7f, 0xff, 0xff, 0xad, 0xb9, 0x2d, 0x3c, 0xbf, 0xff, + 0xd4, 0xca, 0x46, 0x7d, 0x7f, 0xff, 0xe1, 0x64, 0x96, 0x9e, 0x5f, 0xff, 0xea, 0x65, 0x4d, 0xe9, + 0x97, 0xff, 0xfa, 0x99, 0x16, 0x71, 0xaf, 0xff, 0xfc, 0x2c, 0x99, 0x7f, 0x5b, 0x8b, 0x50, 0x9c, + 0xdc, 0x43, 0xa8, 0xb7, 0xd5, 0x68, 0x6a, 0xbc, 0x78, 0xef, 0xc2, 0x3d, 0xfb, 0x3f, 0xcc, 0x51, + 0x40, 0x86, 0x54, 0x02, 0xa4, 0xb2, 0xbd, 0x79, 0x93, 0xa2, 0x4a, 0x35, 0x36, 0xc8, 0x7c, 0xdb, + 0xb0, 0x1d, 0xc1, 0xd0, 0x1d, 0x51, 0x9a, 0xe4, 0x19, 0xe6, 0x08, 0x00, 0x95, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x40, 0x92, 0x9c, 0x10, 0x46, 0x00, + 0x00, 0x03, 0x60, 0x00, 0x00, 0x73, 0xaf, 0xff, 0xff, 0xa9, 0xef, 0xff, 0xff, 0xff, 0xd8, 0x59, + 0x9f, 0xff, 0x53, 0xff, 0xff, 0xff, 0xb4, 0x78, 0x24, 0x37, 0xff, 0xfb, 0x89, 0x98, 0x26, 0xc0, + 0xe3, 0x5f, 0x50, 0x5c, 0x2f, 0xff, 0xff, 0xf1, 0xd2, 0xd2, 0x4d, 0xfd, 0xf2, 0x7f, 0xff, 0x9f, + 0xff, 0x17, 0x85, 0xd2, 0xab, 0x4b, 0xbe, 0x3d, 0xff, 0xff, 0xff, 0xdf, 0x55, 0xbf, 0xff, 0xff, + 0xff, 0xfe, 0x6e, 0xe3, 0xe4, 0x07, 0x53, 0xd2, 0x5f, 0xff, 0xff, 0xff, 0xbe, 0xab, 0x7f, 0xff, + 0xff, 0xff, 0xcd, 0xdc, 0x1a, 0xbf, 0xff, 0xfe, 0xfa, 0xad, 0xff, 0xff, 0xff, 0xcd, 0xdc, 0x4e, + 0x1d, 0x57, 0xff, 0xff, 0xe0, 0x44, 0xaa, 0xa3, 0xff, 0xd3, 0x89, 0x13, 0x97, 0xff, 0xe5, 0xa2, + 0x55, 0x51, 0xff, 0xe9, 0xc4, 0xba, 0x29, 0xff, 0xeb, 0x71, 0x2a, 0xa8, 0xff, 0xee, 0xb9, 0xc4, + 0x7f, 0xff, 0xfb, 0xea, 0xb7, 0xff, 0xff, 0xff, 0x37, 0x71, 0x29, 0xfa, 0x00, +}; + +inline constexpr std::array Vp9Hashes{ + 0x552fd3c320b37408ull, 0xb2fb78bac8d2d7dfull, 0x312469345c491c3dull, + 0x9aeb0fb400c95a75ull, 0xe02e07646faa48beull, 0x1399072031abb375ull, +}; + +inline std::vector> ivfFrames() { + std::vector> frames; + for (std::size_t index = 32; index + 12 <= Vp9Stream.size();) { + const std::size_t length = Vp9Stream[index] | (Vp9Stream[index + 1] << 8u) | (Vp9Stream[index + 2] << 16u) | (static_cast(Vp9Stream[index + 3]) << 24u); + if (index + 12 + length > Vp9Stream.size()) return {}; + frames.emplace_back(Vp9Stream.begin() + static_cast(index + 12), Vp9Stream.begin() + static_cast(index + 12 + length)); + index += 12 + length; + } + return frames; +} + +} + +#endif diff --git a/docs/dev/debt/libSceVdecsw.md b/docs/dev/debt/libSceVdecsw.md index bffe3f23ab..0bf65cb433 100644 --- a/docs/dev/debt/libSceVdecsw.md +++ b/docs/dev/debt/libSceVdecsw.md @@ -4,6 +4,8 @@ - [libSceVdecsw](../../../core/libs/prx/libSceVdecsw/Export.cpp) (libSceVdecsw) - structure layouts follow shadPS4's `vdecsw.h` and were checked against PPSA05684's movie decoder (`movie-decoder-vdecsw.cpp`), which asserts `pictureCount == 1` at offset 0xb of `OutputInfo` and reads `InputResult` offset 0x10 as the number of frames an access unit produced. The compute, decoder memory and decode input structures and the first 0x28 bytes of the 0x50-byte decoder config are those of libSceVideodec2, and decoding goes through it. The 0x78-byte AVC picture info is filled with Videodec2's leading fields and the cropping fields only. OUTPUT_PENDING (0x81510115), INPUT_QUEUE_EMPTY (0x81510116) and DECODE_PENDING (0x81510117) follow from the codes each call's assert accepts. At most four decoded pictures are queued and the last one is held until `sceVdecswFinalizeDecodeSequence`; the console's queue depth is unknown. `sceVdecswSyncDecodeInput` / `sceVdecswSyncDecodeOutput` retry the TrySync variants until they stop returning DECODE_PENDING / OUTPUT_PENDING; they do not sleep beyond a yield, so another thread must drain outputs when the picture queue is full. `sceVdecswGetHevcPictureInfo` still throws: the 0xb8-byte HEVC picture layout is in shadPS4's `vdecsw.h`, but this wrapper only keeps AVC picture info from Videodec2 +- [sceVdecswGetVp9PictureInfo](../../../core/libs/prx/libSceVdecsw/Export.cpp) (libSceVdecsw) - no console behaviour is recorded; the signature and 0x58-byte layout come from [SharpProspero's Vdecsw.cs](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/Vdecsw.cs#L892-L952), and shadPS4 has no VP9 getter. It copies the leading `thisSize - 8` bytes of the picture's info, as [shadPS4's `sceVdecswGetPictureInfo`](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/videodec/vdecsw.cpp#L303-L334) does for AVC and HEVC, from Videodec2's VP9 info (`level` is 0 there). An output with no picture returns 0 and writes nothing. Null pointers, `thisSize` below 8, two pictures and a frame buffer no VP9 decoder returned (an H.264 picture too) throw; console error codes are unknown. VP9 decoding goes through Videodec2 and rejects pictures deeper than 10 bits + ### Functional - [sceVdecswGetHevcPictureInfo](../../../core/libs/prx/libSceVdecsw/Export.cpp) (libSceVdecsw) - throws until HEVC picture info is filled from the decoder From 367648cb79466ef73b3b950defb4033e40e92bcf Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Tue, 20 Oct 2026 13:05:00 +0200 Subject: [PATCH 043/523] feat(libSceVideoRecordingP): report a recorder that is never open in sceVideoRecordingGetStatus --- .../prx/libSceVideoRecordingP/CMakeLists.txt | 15 +++++ .../libs/prx/libSceVideoRecordingP/Export.cpp | 4 +- core/libs/tests/GuestVideoRecording.cpp | 67 +++++++++++++++++++ docs/dev/debt/libSceVideoRecordingP.md | 8 ++- 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 core/libs/prx/libSceVideoRecordingP/CMakeLists.txt create mode 100644 core/libs/tests/GuestVideoRecording.cpp diff --git a/core/libs/prx/libSceVideoRecordingP/CMakeLists.txt b/core/libs/prx/libSceVideoRecordingP/CMakeLists.txt new file mode 100644 index 0000000000..ba8c08e28a --- /dev/null +++ b/core/libs/prx/libSceVideoRecordingP/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(libSceVideoRecordingP SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceVideoRecordingP PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceVideoRecordingP) + +if(BUILD_TESTING) + add_test_executable(guest_video_recording_tests ${LIBS_INCLUDE_DIR}/tests/GuestVideoRecording.cpp) + target_include_directories(guest_video_recording_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_video_recording_tests PRIVATE libSceVideoRecordingP libc) + configure_windows_unwind(guest_video_recording_tests) + add_test(NAME guest_video_recording COMMAND guest_video_recording_tests) + if(WIN32) + set_tests_properties(guest_video_recording PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceVideoRecordingP/Export.cpp b/core/libs/prx/libSceVideoRecordingP/Export.cpp index 915a17728e..6abbdbb129 100644 --- a/core/libs/prx/libSceVideoRecordingP/Export.cpp +++ b/core/libs/prx/libSceVideoRecordingP/Export.cpp @@ -12,6 +12,7 @@ namespace { std::mutex g_infoMutex; std::map> g_info; +constexpr int SceVideoRecordingStatusNone = 0; } extern "C" { @@ -63,8 +64,7 @@ int APS5_VABI sceVideoRecordingGetInfoByHandle(int handle, int32_t info, void* d } int APS5_VABI sceVideoRecordingGetStatus(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return SceVideoRecordingStatusNone; } int APS5_VABI sceVideoRecordingGetSupportInfo(void* info) { diff --git a/core/libs/tests/GuestVideoRecording.cpp b/core/libs/tests/GuestVideoRecording.cpp new file mode 100644 index 0000000000..3338094cac --- /dev/null +++ b/core/libs/tests/GuestVideoRecording.cpp @@ -0,0 +1,67 @@ +#include "prx/libc/include/general/VabiMacros.hpp" + +#include +#include +#include +#include +#include + +extern "C" { +void APS5_VABI _sceVideoRecordingQueryParam_nid_postfix(void* param); +int APS5_VABI sceVideoRecordingGetStatus(void); +int APS5_VABI sceVideoRecordingQueryMemSize(const void* param); +int APS5_VABI sceVideoRecordingOpen(const char* path, const void* param, void* heap, int heapSize); +int APS5_VABI sceVideoRecordingStart(void); +int APS5_VABI sceVideoRecordingStop(void); +int APS5_VABI sceVideoRecordingClose(int discard); +} + +namespace { + +constexpr int StatusNone = 0; +constexpr std::uint8_t Sentinel = 0xA5; + +void Require(bool condition, const char* message) { + if (!condition) { + std::fprintf(stderr, "VideoRecording: %s\n", message); + std::abort(); + } +} + +template +bool Throws(TCall call) { + try { + call(); + } catch (const std::exception&) { + return true; + } + return false; +} + +} + +int main() { + Require(sceVideoRecordingGetStatus() == StatusNone, "a recorder that was never opened reported a status"); + Require(sceVideoRecordingGetStatus() == StatusNone, "the status changed between two reads"); + + std::array param{}; + param.fill(Sentinel); + std::array heap{}; + heap.fill(Sentinel); + + Require(Throws([&] { _sceVideoRecordingQueryParam_nid_postfix(param.data()); }), "the default parameters were produced"); + Require(Throws([&] { sceVideoRecordingQueryMemSize(param.data()); }), "a heap size was reported"); + Require(Throws([&] { sceVideoRecordingQueryMemSize(nullptr); }), "a heap size was reported for no parameters"); + Require(Throws([&] { sceVideoRecordingOpen("clip.mp4", param.data(), heap.data(), static_cast(heap.size())); }), "the recorder opened"); + Require(Throws([&] { sceVideoRecordingStart(); }), "the encoder started"); + Require(Throws([&] { sceVideoRecordingStop(); }), "the encoder stopped"); + Require(Throws([&] { sceVideoRecordingClose(0); }), "the recorder closed"); + Require(Throws([&] { sceVideoRecordingClose(1); }), "the recorder closed with discard"); + + for (const std::uint8_t value : param) Require(value == Sentinel, "the parameters were written"); + for (const std::uint8_t value : heap) Require(value == Sentinel, "the heap was written"); + Require(sceVideoRecordingGetStatus() == StatusNone, "a failed open changed the status"); + + std::puts("VideoRecording tests passed"); + return 0; +} diff --git a/docs/dev/debt/libSceVideoRecordingP.md b/docs/dev/debt/libSceVideoRecordingP.md index 79bb5888e1..50c98e9fc0 100644 --- a/docs/dev/debt/libSceVideoRecordingP.md +++ b/docs/dev/debt/libSceVideoRecordingP.md @@ -2,7 +2,7 @@ ### Unknown function info -- [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - declared with `void(void* param)` signature following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/VideoRecording.cs#L22); unimplemented and throws `NotImplemented_nid_no_patch` +- [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - declared with `void(void* param)` signature following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L67-L69); unimplemented and throws `NotImplemented_nid_no_patch` - [sceVideoRecordingCloseByHandle, sceVideoRecordingGetInfoByHandle](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - inferred handle-based recording control signatures taking an integer recording handle; unimplemented and throw `NotImplemented_nid_no_patch` - [sceVideoRecordingCopyBGRAtoNV12](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - inferred pixel buffer conversion signature `(void* dst, const void* src, uint32_t width, uint32_t height)`; unimplemented and throws `NotImplemented_nid_no_patch` - [sceVideoRecordingGetSupportInfo, sceVideoRecordingGetVideoOutMode](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - query signatures taking an output pointer; exact structure layouts and return values are unverified and throw `NotImplemented_nid_no_patch` @@ -10,5 +10,11 @@ - [sceVideoRecordingInitialize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - initialization stub taking an optional parameter structure pointer; unverified and throws `NotImplemented_nid_no_patch` - [sceVideoRecordingOpen2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - extended recording open signature; parameters inferred from `sceVideoRecordingOpen` and [fpPS4](https://github.com/red-prig/fpPS4); unimplemented and throws `NotImplemented_nid_no_patch` - [sceVideoRecordingQueryHddSize2, sceVideoRecordingQueryMemSize2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - memory and disk size query signatures taking parameter pointer; unverified and throw `NotImplemented_nid_no_patch` +- [sceVideoRecordingQueryMemSize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - signature `int(const SceVideoRecordingParam*)` from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L75-L77), which says only that it reports the heap bytes needed for the settings in the parameter; how the size follows from `ringSec` and the format is not documented. [`MaxMemSize` (4096)](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L64-L65) is the largest heap the recorder needs, an upper bound and not the size for given settings, so returning it would be invented. Throws `NotImplemented_nid_no_patch` +- [sceVideoRecordingOpen, sceVideoRecordingStart, sceVideoRecordingStop, sceVideoRecordingClose](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - signatures from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L79-L101). The recorder is not modelled, so these throw `NotImplemented_nid_no_patch` instead of pretending to record. SharpProspero documents no error code for an unavailable recorder; [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/video_recording/video_recording_error.h#L9-L18) lists codes such as `0x80A80008` (UNSUPPORTED), but nothing shows the console returns one here - [sceVideoRecordingRestModeProcessGetStatus, sceVideoRecordingRestModeProcessStart](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - rest mode recording interfaces; unverified and throw `NotImplemented_nid_no_patch` - [sceVideoRecordingSendCommand](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - command dispatch signature taking integer command ID and parameter pointer; unverified and throws `NotImplemented_nid_no_patch` + +### Functional + +- [sceVideoRecordingGetStatus](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) always returns 0, `None` ("The recorder is not open") in [SharpProspero's `VideoRecordingStatus`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L18-L29), also listed in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/video_recording/video_recording.h#L32-L36). It is true here because `sceVideoRecordingOpen` throws, so no recorder is ever open. Unverified: whether the console returns 0 or an error before `sceVideoRecordingInit`/`sceVideoRecordingInitialize`, which throw, and when it reports `Running` (1) or `Paused` (2) From 25a8636b50faa6ba809a2c350e7e837fbf6bca05 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:02:33 +0200 Subject: [PATCH 044/523] fix(libkernel): never block SIGKILL and SIGSTOP in sigprocmask and sigsuspend FreeBSD 11.4 kern_sigprocmask applies SIG_CANTMASK to the set for SIG_BLOCK and SIG_SETMASK (sys/kern/kern_sig.c:1056, 1067), and sigsuspend sets its mask through the same call (kern_sig.c:1534). The guest mask kept both bits, so sigprocmask(SIG_BLOCK, {SIGKILL}) read back as blocked. Co-Authored-By: Claude Sonnet 5.5 --- .../libs/prx/libkernel/System/src/Signals.cpp | 7 +++--- core/libs/tests/GuestSignals.cpp | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/Signals.cpp b/core/libs/prx/libkernel/System/src/Signals.cpp index c9211181d4..7319aae317 100644 --- a/core/libs/prx/libkernel/System/src/Signals.cpp +++ b/core/libs/prx/libkernel/System/src/Signals.cpp @@ -37,6 +37,7 @@ using GuestHandler = void (APS5_VABI *)(int); constexpr int MaxSignal = 128; constexpr int GuestSigkill = 9; constexpr int GuestSigstop = 17; +constexpr std::uint32_t UnblockableSignals = (1u << (GuestSigkill - 1)) | (1u << (GuestSigstop - 1)); constexpr int GuestSigalrm = 14; constexpr int SaRestart = 0x2; constexpr int SaSiginfo = 0x40; @@ -555,7 +556,7 @@ int APS5_VABI _sigprocmask_nid_postfix(int how, const GuestSignalSet* set, Guest if (set != nullptr) { switch (how) { case 1: - blockedMask.fetch_or(set->bits[0]); + blockedMask.fetch_or(set->bits[0] & ~UnblockableSignals); for (int word = 1; word < 4; ++word) blockedMaskExtra[word - 1].fetch_or(set->bits[word]); break; case 2: @@ -563,7 +564,7 @@ int APS5_VABI _sigprocmask_nid_postfix(int how, const GuestSignalSet* set, Guest for (int word = 1; word < 4; ++word) blockedMaskExtra[word - 1].fetch_and(~set->bits[word]); break; case 3: - blockedMask.store(set->bits[0]); + blockedMask.store(set->bits[0] & ~UnblockableSignals); for (int word = 1; word < 4; ++word) blockedMaskExtra[word - 1].store(set->bits[word]); break; } @@ -628,7 +629,7 @@ int APS5_VABI sigsuspend_nid_postfix(const GuestSignalSet* set) { GuestSignalSet previous{{0, 0, 0, 0}}; { std::lock_guard lock(registration); - previous.bits[0] = blockedMask.exchange(set->bits[0]); + previous.bits[0] = blockedMask.exchange(set->bits[0] & ~UnblockableSignals); for (int word = 1; word < 4; ++word) previous.bits[word] = blockedMaskExtra[word - 1].exchange(set->bits[word]); } WaitForDeliverableSignal(); diff --git a/core/libs/tests/GuestSignals.cpp b/core/libs/tests/GuestSignals.cpp index 016c4c5a0f..8e254fd717 100644 --- a/core/libs/tests/GuestSignals.cpp +++ b/core/libs/tests/GuestSignals.cpp @@ -64,6 +64,11 @@ void APS5_VABI Callback(int value) { received = value; handlerReturn = reinterpret_cast(__builtin_return_address(0)); } +GuestSignalSet maskInHandler{}; +void APS5_VABI MaskCallback(int value) { + received = value; + sigprocmask_nid_postfix(1, nullptr, &maskInHandler); +} static void Require(bool value) { if (!value) std::abort(); } int main() { const auto invalid = reinterpret_cast(static_cast(-1)); @@ -112,6 +117,17 @@ int main() { Require(sigprocmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == 0x20); Require(pthread_sigmask_nid_postfix(2, &blocked, &previous) == 0 && previous.bits[0] == 0x20); Require(pthread_sigmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == 0); + const GuestSignalSet withKillAndStop{{(1u << 8) | (1u << 16) | 0x20, 0, 0, 0}}; + const GuestSignalSet noSignals{{0, 0, 0, 0}}; + Require(sigprocmask_nid_postfix(1, &withKillAndStop, nullptr) == 0); + Require(sigprocmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == 0x20); + Require(sigprocmask_nid_postfix(3, &noSignals, nullptr) == 0); + Require(sigprocmask_nid_postfix(3, &withKillAndStop, nullptr) == 0); + Require(sigprocmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == 0x20); + Require(sigprocmask_nid_postfix(3, &noSignals, nullptr) == 0); + Require(pthread_sigmask_nid_postfix(1, &withKillAndStop, nullptr) == 0); + Require(pthread_sigmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == 0x20); + Require(pthread_sigmask_nid_postfix(3, &noSignals, nullptr) == 0); const auto callback = reinterpret_cast(Callback); const auto infoCallback = reinterpret_cast(InfoCallback); @@ -184,6 +200,14 @@ int main() { *__error_nid_postfix() = 0; Require(sigsuspend_nid_postfix(&empty) == -1 && *__error_nid_postfix() == 4 && received == 30); Require(sigprocmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == user.bits[0]); + Require(signal_nid_postfix(30, MaskCallback) != invalid); + const GuestSignalSet suspendWithKillAndStop{{(1u << 8) | (1u << 16) | (1u << 28), 0, 0, 0}}; + received = 0; + Require(raise_nid_postfix(30) == 0 && received == 0); + Require(sigsuspend_nid_postfix(&suspendWithKillAndStop) == -1 && received == 30); + Require(maskInHandler.bits[0] == (1u << 28)); + Require(sigprocmask_nid_postfix(1, nullptr, &previous) == 0 && previous.bits[0] == user.bits[0]); + Require(signal_nid_postfix(30, Callback) != invalid); received = 0; SuspendWait wait{&empty, 0, 0}; void* suspender = nullptr; From 529c1a0f5146f4b67267b98bca37795785466c29 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:16:18 +0200 Subject: [PATCH 045/523] fix(libkernel): reject undefined sa_flags in sigaction with EINVAL FreeBSD 11.4 kern_sigaction fails with EINVAL when a handler other than SIG_DFL and SIG_IGN comes with flags outside SA_ONSTACK, SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP, SA_NODEFER, SA_NOCLDWAIT and SA_SIGINFO (sys/kern/kern_sig.c:691-695). The guest accepted any flag word. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/System/src/Signals.cpp | 2 ++ core/libs/tests/GuestSignals.cpp | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/core/libs/prx/libkernel/System/src/Signals.cpp b/core/libs/prx/libkernel/System/src/Signals.cpp index c9211181d4..fe28f0e76f 100644 --- a/core/libs/prx/libkernel/System/src/Signals.cpp +++ b/core/libs/prx/libkernel/System/src/Signals.cpp @@ -40,6 +40,7 @@ constexpr int GuestSigstop = 17; constexpr int GuestSigalrm = 14; constexpr int SaRestart = 0x2; constexpr int SaSiginfo = 0x40; +constexpr int SaDefinedFlags = 0x7f; std::atomic handlers[MaxSignal + 1]{}; static_assert(std::atomic::is_always_lock_free); std::atomic blockedMask{0}; @@ -485,6 +486,7 @@ GuestHandler APS5_VABI signal_nid_postfix(int guest, GuestHandler handler) { } int APS5_VABI sigaction_nid_postfix(int guest, const GuestSigaction* action, GuestSigaction* previous) { if (guest < 1 || guest > MaxSignal) { *__error_nid_postfix() = 22; return -1; } + if (action && action->handler > 1 && (action->flags & ~SaDefinedFlags) != 0) { *__error_nid_postfix() = 22; return -1; } if (action && (guest == GuestSigkill || guest == GuestSigstop) && action->handler != 0) { *__error_nid_postfix() = 22; return -1; } std::lock_guard lock(registration); const GuestSigaction old = dispositions[guest]; diff --git a/core/libs/tests/GuestSignals.cpp b/core/libs/tests/GuestSignals.cpp index 016c4c5a0f..90293808f7 100644 --- a/core/libs/tests/GuestSignals.cpp +++ b/core/libs/tests/GuestSignals.cpp @@ -130,6 +130,15 @@ int main() { Require(signal_nid_postfix(15, Callback) == nullptr); Require(sigaction_nid_postfix(15, nullptr, ¤t) == 0 && current.handler == callback && current.flags == 0x2); Require(signal_nid_postfix(15, nullptr) == Callback); + for (const int flags : {0x80, 0x100, 0x8000, 0x40 | 0x80}) { + const GuestSigaction undefinedFlags{callback, flags, {}}; + *__error_nid_postfix() = 0; + Require(sigaction_nid_postfix(15, &undefinedFlags, nullptr) == -1 && *__error_nid_postfix() == 22); + Require(sigaction_nid_postfix(15, nullptr, ¤t) == 0 && current.handler == 0 && current.flags == 0x2); + } + const GuestSigaction ignoredWithFlags{1, 0x8000, {}}; + Require(sigaction_nid_postfix(15, &ignoredWithFlags, nullptr) == 0); + Require(sigaction_nid_postfix(15, &defaults, nullptr) == 0); const GuestSigaction crash{infoCallback, 0x40 | 0x10, {}}; for (int recorded : {10, 1, 64, 128}) { From dc642205a5068b75927bec53576af4cbfddcb4a1 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Tue, 20 Oct 2026 15:19:42 +0300 Subject: [PATCH 046/523] docs(libSceNpManager): list sceNpUnregisterPlusEventCallback under Silent stubs Co-Authored-By: Claude Opus 5.5 --- docs/dev/debt/libSceNpManager.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceNpManager.md b/docs/dev/debt/libSceNpManager.md index 09d9c6857e..fcf80d6cb1 100644 --- a/docs/dev/debt/libSceNpManager.md +++ b/docs/dev/debt/libSceNpManager.md @@ -1,5 +1,9 @@ # libSceNpManager technical debt +### Silent stubs + +- [sceNpUnregisterPlusEventCallback](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - returns 0 whether or not a callback was registered, as `sceNpUnregisterPremiumEventCallback` does: `sceNpRegisterPlusEventCallback` keeps no callback, and a signed-out user gets no plus events + ### Unknown function info - [sceNpGetSandboxInfo](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager, NID `ZVZ++68nNI8`) - the name hashes to the imported NID (found with nidhunt, confirmed with `tools/nid_names.py`). STAR WARS: Galactic Racer (PPSA28416) imports it; its OnlineSubsystemPS5 calls it with one pointer to a 0x50-byte stack buffer and reads the buffer as a string (discussions/3086). The returned string, the exact parameter struct and console errors are unknown, so every call throws without reading or writing caller memory. A working implementation belongs with that title's bring-up. @@ -15,7 +19,6 @@ - [sceNpGetUserIdByOnlineId](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(online id, user id output)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L703): a null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output, as `sceNpGetUserIdByAccountId` does - [sceNpCheckPlus](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(request id, parameter, result)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L246): request id 0 or a null parameter or result returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and every other call returns `SCE_NP_ERROR_SIGNED_OUT` without writing the result, as `sceNpCheckPremium` does. The parameter's size and feature fields, which shadPS4 checks, are not read: the errors the console returns for them are not known - [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(user id, timeout)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp). A null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`; otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured -- [sceNpUnregisterPlusEventCallback](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - returns 0 whether or not a callback was registered, as `sceNpUnregisterPremiumEventCallback` does: `sceNpRegisterPlusEventCallback` keeps no callback, and a signed-out user gets no plus events - [NP reachability callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable. - [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests - [NP state callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the registration rules are [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L857-L901)'s: `sceNpRegisterStateCallbackA` keeps up to 8 callbacks and returns the slot plus 1, a null callback returns `INVALID_ARGUMENT`, a callback already registered `CALLBACK_ALREADY_REGISTERED` and a ninth one `CALLBACK_MAX` (0x8055001D, [np_error.h](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_error.h#L38)); `sceNpUnregisterStateCallbackA` returns `INVALID_ARGUMENT` for an id outside 1..8 and `CALLBACK_NOT_REGISTERED` for an empty slot; the legacy [sceNpRegisterStateCallback](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L981-L1005) keeps one callback with the same errors. Each registration gets `SIGNED_OUT` for the initial user (0x10000000) once, from the next `sceNpCheckCallback` on the calling thread. For the A form this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3959-L3974), whose [sceNpRegisterStateCallbackA](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4021-L4034) marks the registration pending. KytyPS5 never calls the legacy callback ([sceNpRegisterStateCallback](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4009-L4019) stores nothing); it is called as `(user, SIGNED_OUT, nullptr, userdata)`, as in shadPS4, which passes an NpId only for `SIGNED_IN` ([L823](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L823), [L938-L940](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L938-L940)). shadPS4 instead queues `SIGNED_OUT` from the user service login event and delivers it from `sceNpCheckCallback` ([DispatchPendingNpStateCallbacks](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L903-L962)); the reachability callback is not given an initial `UNAVAILABLE`, unlike that function ([L922-L961](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L922-L961)). A callback being delivered when another thread unregisters it is still called once. When and how often the console calls the state callbacks, and the errors, are not measured From 9fc0c88550c263f0bb1afe55d94317fcc6c60af4 Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Tue, 20 Oct 2026 17:54:26 +0530 Subject: [PATCH 047/523] fix(libScePsml): copy MFSR object fields under the lock and drop the dump title id Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- core/libs/prx/libScePsml_debug/Export.cpp | 26 ++++++++++++++--------- docs/dev/debt/libScePsml_debug.md | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libScePsml_debug/Export.cpp b/core/libs/prx/libScePsml_debug/Export.cpp index aba81b67da..448d0c8a60 100644 --- a/core/libs/prx/libScePsml_debug/Export.cpp +++ b/core/libs/prx/libScePsml_debug/Export.cpp @@ -97,10 +97,12 @@ void* CreateMfsrObject(MfsrObjectKind kind, void* sharedResources, const char* f return object; } -const MfsrObject* FindMfsrObject(void* handle) { +bool FindMfsrObject(void* handle, MfsrObject* out) { std::lock_guard lock(g_mfsrMutex); const auto found = g_mfsrObjects.find(handle); - return found != g_mfsrObjects.end() ? found->second : nullptr; + if (found == g_mfsrObjects.end()) return false; + *out = *found->second; + return true; } std::pair MfsrInputDimensions(const MfsrContextParam* param) { @@ -218,7 +220,8 @@ std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacket1300(void* context, void* std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(void* context, std::uint32_t* packetSizeInDwords) { if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; - if (FindMfsrObject(context) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; + MfsrObject object; + if (!FindMfsrObject(context, &object)) return SCE_PSML_ERROR_INVALID_ARGUMENT; if (packetSizeInDwords == nullptr) return SCE_PSML_ERROR_NULL_OUTPUT; *packetSizeInDwords = MfsrDispatchPacketDwords; return 0; @@ -226,29 +229,32 @@ std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacketSizeInDwords(void* contex std::int32_t APS5_VABI scePsmlMfsrSelectConfig(void* config) { if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; - if (FindMfsrObject(config) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; + MfsrObject object; + if (!FindMfsrObject(config, &object)) return SCE_PSML_ERROR_INVALID_ARGUMENT; return 0; } std::int32_t APS5_VABI scePsmlMfsrGetMipmapBias(void* context, float* bias, std::uint32_t dimension) { if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; - const MfsrObject* object = FindMfsrObject(context); - if (object == nullptr || object->kind != MfsrObjectKind::Context) return SCE_PSML_ERROR_INVALID_ARGUMENT; + MfsrObject object; + if (!FindMfsrObject(context, &object) || object.kind != MfsrObjectKind::Context) return SCE_PSML_ERROR_INVALID_ARGUMENT; if (bias == nullptr) return SCE_PSML_ERROR_NULL_OUTPUT; - if (object->inputWidth == 0) return SCE_PSML_ERROR_INVALID_ARGUMENT; - *bias = std::log2f(static_cast(dimension) / static_cast(object->inputWidth)) - 1.0f; + if (object.inputWidth == 0) return SCE_PSML_ERROR_INVALID_ARGUMENT; + *bias = std::log2f(static_cast(dimension) / static_cast(object.inputWidth)) - 1.0f; return 0; } std::int32_t APS5_VABI scePsmlMfsrRequestCapture(void* context) { if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; - if (FindMfsrObject(context) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; + MfsrObject object; + if (!FindMfsrObject(context, &object)) return SCE_PSML_ERROR_INVALID_ARGUMENT; return 0; } std::int32_t APS5_VABI scePsmlMfsrIsCaptureInProgress(void* context) { if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; - if (FindMfsrObject(context) == nullptr) return SCE_PSML_ERROR_INVALID_ARGUMENT; + MfsrObject object; + if (!FindMfsrObject(context, &object)) return SCE_PSML_ERROR_INVALID_ARGUMENT; return 0; } diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index 7a25620b4c..e63e91a198 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -11,5 +11,5 @@ ### Functional -- [scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture, scePsmlMfsrIsCaptureInProgress](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - semantics from the APR-EMU `fakelib/libScePsml.sprx` in PPSA20397. Guards: `0x8A810001` uninitialized, `0x8A810005` null/foreign handle (fakelib checks an engine id `0xA9C4`/`0x9231`; our map check is the equivalent), `0x8A810009` null output. `GetDispatchMfsrPacketSizeInDwords` writes `0x600`; `GetMipmapBias(context, bias, dimension)` writes `log2f(dimension / context->inputWidth) - 1.0f`, width recorded at `CreateContext800M3_2` from the parameter pair at 0x18 or `descriptor0 + 0x20` (indirection inferred). `SelectConfig`, `RequestCapture`, `IsCaptureInProgress` validate then return 0. The `GetDispatchMfsrPacket*` calls still throw: the 0x158-byte template's packet routine is not understood +- [scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture, scePsmlMfsrIsCaptureInProgress](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - semantics from the APR-EMU `fakelib/libScePsml.sprx`. Guards: `0x8A810001` uninitialized, `0x8A810005` null/foreign handle (fakelib checks an engine id `0xA9C4`/`0x9231`; our map check is the equivalent), `0x8A810009` null output. `GetDispatchMfsrPacketSizeInDwords` writes `0x600`; `GetMipmapBias(context, bias, dimension)` writes `log2f(dimension / context->inputWidth) - 1.0f`, width recorded at `CreateContext800M3_2` from the parameter pair at 0x18 or `descriptor0 + 0x20` for callers that pass a descriptor pointer there (indirection inferred; zero width returns `0x8A810005`). `SelectConfig`, `RequestCapture`, `IsCaptureInProgress` validate then return 0. The `GetDispatchMfsrPacket*` calls still throw: the 0x158-byte template's packet routine is not understood - [scePsmlMfsrInit](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - succeeds and only sets the initialized state; the shared resources and contexts are bookkeeping objects that use none of the guest blocks. Whether Sony's library fails on hardware without PSSR is unknown. PPSA08330 calls the setup unconditionally and only uses a context when its cached `sceKernelIsTrinityMode` result is set (0x3c643d, 0x567f97; otherwise it stores a -0.25 mip bias itself at 0x567fe2), which AnyPS5 reports as 0, so the dispatch, mip bias and capture calls still throw From 2e1864cdf428e34b2c228ae4dcede0612f7ecf5c Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Tue, 20 Oct 2026 15:21:52 +0300 Subject: [PATCH 048/523] fix(relinker): keep starting standard module folders on Windows The browser converter treats sce_module as a runtime directory, so the Windows deferral of runtime modules the executable needs also deferred libc.prx and libSceFios2.prx, which titles never load themselves. Modules in sce_module, sce_modules and prx that the executable needs are started at startup again, as the Linux deferral of #6586 keeps them. Co-Authored-By: Claude Opus 5.5 --- .../relinker/src/guest/GuestModuleBuilder.cpp | 6 ++- .../relinker/tests/ConversionTests.cpp | 51 ++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp index 89b3cee2f4..88a1450c60 100644 --- a/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp +++ b/core/relinker/relinker/src/guest/GuestModuleBuilder.cpp @@ -259,9 +259,13 @@ std::vector GuestModuleBuilder::Build(const std::filesystem::path const auto isRuntime = [&](const std::size_t index) { return std::find(runtimeDirectories.begin(), runtimeDirectories.end(), images[index].SourcePath.parent_path().lexically_normal()) != runtimeDirectories.end(); }; + const auto standardDirectory = [&](const std::size_t index) { + const auto directory = images[index].SourcePath.parent_path().lexically_normal(); + return directory == (root / "sce_module").lexically_normal() || directory == (root / "sce_modules").lexically_normal() || directory == (root / "prx").lexically_normal(); + }; for (const auto& name : neededNames) { const auto found = findGuest(name); - if (found != guestNames.end() && !windows) require(found->second); + if (found != guestNames.end() && (!windows || standardDirectory(found->second))) require(found->second); } for (const auto provider : executableProviders) require(provider); for (std::size_t index = 0; index < images.size(); ++index) { diff --git a/core/relinker/relinker/tests/ConversionTests.cpp b/core/relinker/relinker/tests/ConversionTests.cpp index 0b6d9d62c9..468921cf4f 100644 --- a/core/relinker/relinker/tests/ConversionTests.cpp +++ b/core/relinker/relinker/tests/ConversionTests.cpp @@ -1,5 +1,6 @@ #include "TestProgress.hpp" #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include namespace { @@ -23,7 +25,7 @@ void reject(TAction action) { throw std::runtime_error("Invalid conversion was accepted"); } -std::vector fixture() { +std::vector fixture(const std::string& needed = {}) { std::vector bytes(0x3000); const std::array magic{0x7f, 'E', 'L', 'F', 2, 1, 1}; std::copy(magic.begin(), magic.end(), bytes.begin()); @@ -51,7 +53,12 @@ std::vector fixture() { header(1, 1, 6, 0x2000, 0x1000); for (std::size_t index = 3; index < 8; ++index) header(index, 0x6fffff01, 0, 0, 0); bytes[0x1000] = 0xc3; - const std::array, 9> tags{{{5, 0x2600}, {10, 1}, {6, 0x2620}, {11, 24}, {7, 0x2700}, {8, 0}, {9, 24}, {4, 0x2680}, {0, 0}}}; + std::vector> tags{{5, 0x2600}, {10, needed.empty() ? 1 : needed.size() + 2}, {6, 0x2620}, {11, 24}, {7, 0x2700}, {8, 0}, {9, 24}, {4, 0x2680}}; + if (!needed.empty()) { + std::copy(needed.begin(), needed.end(), bytes.begin() + 0x2601); + tags.push_back({1, 1}); + } + tags.push_back({0, 0}); header(2, 2, 6, 0x2000, tags.size() * 16); for (std::size_t index = 0; index < tags.size(); ++index) { Io::WriteU64(bytes, 0x2000 + index * 16, tags[index].first); @@ -246,6 +253,45 @@ void baseRelocations() { reject([&] { (void)builder.BuildBaseRelocations({0x10fff}); }); } +std::size_t fileOffset(const std::vector& pe, std::uint32_t address) { + const auto header = Io::ReadU32(pe, 0x3c); + const auto sections = header + 24 + Io::ReadU16(pe, header + 20); + for (std::size_t index = 0; index < Io::ReadU16(pe, header + 6); ++index) { + const auto at = sections + index * 40; + const auto start = Io::ReadU32(pe, at + 12); + if (address >= start && address - start < Io::ReadU32(pe, at + 16)) return Io::ReadU32(pe, at + 20) + address - start; + } + throw std::runtime_error("Unmapped PE address"); +} + +bool exports(const std::vector& pe, std::string_view name) { + const auto address = Io::ReadU32(pe, Io::ReadU32(pe, 0x3c) + 24 + 112); + if (address == 0) return false; + const auto directory = fileOffset(pe, address); + const auto names = fileOffset(pe, Io::ReadU32(pe, directory + 32)); + for (std::size_t index = 0; index < Io::ReadU32(pe, directory + 24); ++index) { + if (reinterpret_cast(pe.data() + fileOffset(pe, Io::ReadU32(pe, names + index * 4))) == name) return true; + } + return false; +} + +void runtimeDirectories() { + TestProgress progress; + const Relinker::Conversion converter(progress); + Relinker::ConversionOptions options; + options.Target = Relinker::TargetSystem::Windows; + for (const auto* directory : {"sce_module", "sce_modules", "prx", "Media/Modules"}) { + auto source = input(); + const auto parent = source.ModuleRoot / directory; + source.Executable = fixture("needed.prx"); + source.Modules.push_back({parent / "needed.prx", fixture()}); + source.RuntimeDirectories.push_back(parent); + const auto output = converter.Convert(std::move(source), options); + require(output.Modules.size() == 1, "Module was not converted"); + require(exports(output.Modules.front().Bytes, Relinker::GuestInitializeExport) == (std::string_view(directory) == "Media/Modules"), "Wrong start of a runtime module the executable needs"); + } +} + } int main() { @@ -256,6 +302,7 @@ int main() { diagnosticContext(); cancellation(); baseRelocations(); + runtimeDirectories(); } catch (const std::exception& error) { std::cerr << error.what() << '\n'; return 1; From e9bde2a2d4c69b908544718f654bba742bb50ed5 Mon Sep 17 00:00:00 2001 From: hypermist Date: Wed, 21 Oct 2026 01:28:22 +1300 Subject: [PATCH 049/523] feat(libSceVideoOut): implement sceVideoOutRemoveBufferAttributeOption Clear the given bits of the attribute's option field, the inverse of sceVideoOutAddBufferAttributeOption, as libSceVideoOut does. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- core/libs/prx/libSceVideoOut/include/Buffer.hpp | 1 + core/libs/prx/libSceVideoOut/src/Buffer.cpp | 7 +++++++ core/libs/prx/libSceVideoOut/tests/Flip.cpp | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/core/libs/prx/libSceVideoOut/include/Buffer.hpp b/core/libs/prx/libSceVideoOut/include/Buffer.hpp index 8fb708400b..5ae8013ae6 100644 --- a/core/libs/prx/libSceVideoOut/include/Buffer.hpp +++ b/core/libs/prx/libSceVideoOut/include/Buffer.hpp @@ -13,6 +13,7 @@ void APS5_VABI sceVideoOutSetBufferAttribute(VideoOutBufferAttribute* attribute, int APS5_VABI sceVideoOutRegisterBuffers2(int handle, int set_index, int buffer_index_start, const VideoOutBuffers* buffers, int buffer_num, const VideoOutBufferAttribute2* attribute, int category, void* option); void APS5_VABI sceVideoOutSetBufferAttribute2(VideoOutBufferAttribute2* attribute, uint64_t pixel_format, uint32_t tiling_mode, uint32_t width, uint32_t height, uint64_t option, uint32_t dcc_control, uint64_t dcc_cb_register_clear_color); void APS5_VABI sceVideoOutAddBufferAttributeOption(VideoOutBufferAttribute2* attribute, uint64_t option); +void APS5_VABI sceVideoOutRemoveBufferAttributeOption(VideoOutBufferAttribute2* attribute, uint64_t option); int APS5_VABI sceVideoOutSubmitChangeBufferAttribute2(int handle, int set_index, const VideoOutBufferAttribute2* attribute, void* option); int APS5_VABI sceVideoOutSubmitEopFlip(int handle, int index, int flip_mode, int64_t flip_arg, std::uint32_t* token); int APS5_VABI sceVideoOutSubmitFlip(int handle, int index, int flip_mode, int64_t flip_arg); diff --git a/core/libs/prx/libSceVideoOut/src/Buffer.cpp b/core/libs/prx/libSceVideoOut/src/Buffer.cpp index 81722d188e..7197986ca7 100644 --- a/core/libs/prx/libSceVideoOut/src/Buffer.cpp +++ b/core/libs/prx/libSceVideoOut/src/Buffer.cpp @@ -120,6 +120,13 @@ void APS5_VABI sceVideoOutAddBufferAttributeOption(VideoOutBufferAttribute2* att attribute->option |= option; } +void APS5_VABI sceVideoOutRemoveBufferAttributeOption(VideoOutBufferAttribute2* attribute, uint64_t option) { + if (attribute == nullptr) { + throw std::runtime_error("sceVideoOutRemoveBufferAttributeOption: null attribute"); + } + attribute->option &= ~option; +} + int APS5_VABI sceVideoOutRegisterBuffers2(int handle, int setIndex, int bufferIndexStart, const VideoOutBuffers* buffers, int bufferNum, const VideoOutBufferAttribute2* attribute, int category, void* option) try { auto cfg = VideoOutDriver::Get().GetConfig(handle); if (cfg == nullptr) { diff --git a/core/libs/prx/libSceVideoOut/tests/Flip.cpp b/core/libs/prx/libSceVideoOut/tests/Flip.cpp index b86554b599..51e6e4670c 100644 --- a/core/libs/prx/libSceVideoOut/tests/Flip.cpp +++ b/core/libs/prx/libSceVideoOut/tests/Flip.cpp @@ -372,6 +372,12 @@ void testDecode() { check(group.attribute.option == 0x28, "an added buffer option did not keep the existing bits"); check(expectFailure([&] { DescribeVideoOutBuffer(registered, group); }).find("buffer option 40") != std::string::npos, "an unsupported added buffer option was accepted"); check(expectFailure([&] { sceVideoOutAddBufferAttributeOption(nullptr, 0x20); }).find("null attribute") != std::string::npos, "a null attribute was accepted"); + sceVideoOutRemoveBufferAttributeOption(&group.attribute, 0x21); + check(group.attribute.option == VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY && group.attribute.width == 259 && group.attribute.height == 137, "a removed buffer option was not cleared alone"); + check(DescribeVideoOutBuffer(registered, group).address == plain.address, "a buffer whose unsupported option was removed was rejected"); + sceVideoOutRemoveBufferAttributeOption(&group.attribute, VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_STRICT_COLORIMETRY); + check(group.attribute.option == VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE, "removing the last buffer option did not clear it"); + check(expectFailure([&] { sceVideoOutRemoveBufferAttributeOption(nullptr, 0x20); }).find("null attribute") != std::string::npos, "a null attribute was accepted for removal"); VideoOutBuffer dccBuffer{0, reinterpret_cast(tiled.data()), 0x7f0000}; sceVideoOutSetBufferAttribute2(&group.attribute, 0x8000000000000000ull, 0, 259, 137, VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE, 0x208, 0x11223344); group.category = VIDEO_OUT_BUFFER_ATTRIBUTE_CATEGORY_COMPRESSED; From fa0e7b8c1320eb4dc183aea0cef2f470595c5513 Mon Sep 17 00:00:00 2001 From: hypermist Date: Wed, 21 Oct 2026 01:33:25 +1300 Subject: [PATCH 050/523] feat(libkernel): implement pthread_cleanup_push and pthread_cleanup_pop The exported forms allocate the handler record themselves and free it when it is popped, as libkernel does; __pthread_cleanup_pop_imp now frees such records too. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- .../prx/libkernel/Pthread/include/Cancel.hpp | 2 ++ .../libs/prx/libkernel/Pthread/src/Thread.cpp | 10 +++++++++ core/libs/tests/GuestPthreadCancel.cpp | 22 ++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libkernel/Pthread/include/Cancel.hpp b/core/libs/prx/libkernel/Pthread/include/Cancel.hpp index 8f2df852ca..e45cbaee3f 100644 --- a/core/libs/prx/libkernel/Pthread/include/Cancel.hpp +++ b/core/libs/prx/libkernel/Pthread/include/Cancel.hpp @@ -22,6 +22,8 @@ struct CleanupHandler { extern "C" { void APS5_VABI __pthread_cleanup_push_imp_nid_postfix(void (APS5_VABI* routine)(void*), void* argument, void* info); void APS5_VABI __pthread_cleanup_pop_imp_nid_postfix(int execute); +void APS5_VABI pthread_cleanup_push_nid_postfix(void (APS5_VABI* routine)(void*), void* argument); +void APS5_VABI pthread_cleanup_pop_nid_postfix(int execute); } namespace ThreadCancel { diff --git a/core/libs/prx/libkernel/Pthread/src/Thread.cpp b/core/libs/prx/libkernel/Pthread/src/Thread.cpp index bd61a61fc9..99e24d61e6 100644 --- a/core/libs/prx/libkernel/Pthread/src/Thread.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Thread.cpp @@ -1062,6 +1062,16 @@ void APS5_VABI __pthread_cleanup_pop_imp_nid_postfix(int execute) { if (!handler) return; CurrentGuestState().cleanupHandlers = handler->previous; if (execute) handler->routine(handler->argument); + if (handler->onHeap) delete handler; +} + +void APS5_VABI pthread_cleanup_push_nid_postfix(void (APS5_VABI* routine)(void*), void* argument) { + auto* handler = new CleanupHandler{CurrentGuestState().cleanupHandlers, routine, argument, 1}; + CurrentGuestState().cleanupHandlers = handler; +} + +void APS5_VABI pthread_cleanup_pop_nid_postfix(int execute) { + __pthread_cleanup_pop_imp_nid_postfix(execute); } int APS5_VABI scePthreadEqual(Pthread thread1, Pthread thread2) { diff --git a/core/libs/tests/GuestPthreadCancel.cpp b/core/libs/tests/GuestPthreadCancel.cpp index 576414c6c3..81cc47ebde 100644 --- a/core/libs/tests/GuestPthreadCancel.cpp +++ b/core/libs/tests/GuestPthreadCancel.cpp @@ -34,6 +34,8 @@ int APS5_VABI sem_timedwait_nid_postfix(void* sem, const KernelTimespec* abstime int APS5_VABI sem_getvalue_nid_postfix(void* sem, int* value); void APS5_VABI __pthread_cleanup_push_imp_nid_postfix(void (APS5_VABI* routine)(void*), void* argument, void* info); void APS5_VABI __pthread_cleanup_pop_imp_nid_postfix(int execute); +void APS5_VABI pthread_cleanup_push_nid_postfix(void (APS5_VABI* routine)(void*), void* argument); +void APS5_VABI pthread_cleanup_pop_nid_postfix(int execute); } static constexpr int SCE_OK = 0; @@ -60,7 +62,7 @@ struct Shared { std::atomic returned{false}; std::atomic survived{false}; std::atomic cleanups{0}; - int order[2] = {0, 0}; + int order[3] = {0, 0, 0}; }; static Shared shared; @@ -189,6 +191,18 @@ static void* APS5_VABI Handlers(void*) { return nullptr; } +static void* APS5_VABI HeapHandlers(void*) { + std::uintptr_t first[8]; + __pthread_cleanup_push_imp_nid_postfix(RecordOrder, reinterpret_cast(std::intptr_t{1}), first); + pthread_cleanup_push_nid_postfix(RecordOrder, reinterpret_cast(std::intptr_t{2})); + pthread_cleanup_pop_nid_postfix(0); + pthread_cleanup_push_nid_postfix(RecordOrder, reinterpret_cast(std::intptr_t{3})); + __pthread_cleanup_pop_imp_nid_postfix(1); + pthread_cleanup_push_nid_postfix(RecordOrder, reinterpret_cast(std::intptr_t{4})); + scePthreadExit(reinterpret_cast(std::intptr_t{8})); + return nullptr; +} + static Pthread Start(PthreadEntry entry) { shared.ready.store(false); shared.release.store(false); @@ -275,6 +289,12 @@ int main() { Require(reinterpret_cast(result) == 7); Require(shared.cleanups.load() == 2 && shared.order[0] == 3 && shared.order[1] == 1); + shared.cleanups.store(0); + Require(scePthreadCreate(&thread, nullptr, HeapHandlers, nullptr, nullptr) == SCE_OK); + Require(scePthreadJoin(thread, &result) == SCE_OK); + Require(reinterpret_cast(result) == 8); + Require(shared.cleanups.load() == 3 && shared.order[0] == 3 && shared.order[1] == 4 && shared.order[2] == 1); + Require(sem_destroy_nid_postfix(&shared.posixSem) == 0); Require(scePthreadSemDestroy(&shared.sem) == SCE_OK); Require(scePthreadCondDestroy(&shared.cond) == SCE_OK); From 12785e21924857264f608455f45df8007d0708f3 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:35:04 +0200 Subject: [PATCH 051/523] fix(libkernel): accept MAP_ANON without MAP_PRIVATE and refuse MAP_SHARED with MAP_PRIVATE FreeBSD 11.4 kern_mmap maps anonymous memory whenever MAP_ANON is set, with or without MAP_PRIVATE (sys/vm/vm_mmap.c:339), and fails with EINVAL when both MAP_SHARED and MAP_PRIVATE are set (vm_mmap.c:252). The guest failed the first with ENOTSUP (45) and the second with ENOTSUP as well. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp | 4 +++- core/libs/tests/GuestMemory.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp b/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp index 7c69af40fa..35e9784538 100644 --- a/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp +++ b/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp @@ -155,7 +155,9 @@ void* APS5_VABI mmap_nid_postfix(void* address, std::size_t length, int protecti if (!RoundLength(length, rounded) || (protection & ~7) != 0) return failed(GuestInvalid); if ((flags & (GuestMapExclusive | GuestMapFixed)) == GuestMapExclusive) return failed(GuestInvalid); - const int mappingFlags = flags & ~GuestAlignedSuper; + if ((flags & (GuestShared | GuestPrivate)) == (GuestShared | GuestPrivate)) return failed(GuestInvalid); + int mappingFlags = flags & ~GuestAlignedSuper; + if ((mappingFlags & (GuestShared | GuestPrivate | GuestAnonymous)) == GuestAnonymous) mappingFlags |= GuestPrivate; const bool fixedAnonymous = (mappingFlags & ~(GuestMapFixed | GuestMapExclusive)) == (GuestPrivate | GuestAnonymous) && (mappingFlags & GuestMapFixed) != 0; if (mappingFlags == (GuestPrivate | GuestAnonymous) || fixedAnonymous) { diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 04fe239d47..d638a658b1 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -2985,6 +2985,14 @@ int main(int argc, char** argv) { reject(page, 3, 0x1012, -1, 0, 22); reject(page, 3, 0x2, 0, 0, 45); // file-backed reject(page, 3, 0x22, -1, 0, 45); // Linux MAP_ANON is not guest MAP_ANON + reject(page, 3, 0x1003, -1, 0, 22); + reject(page, 3, 0x3, 0, 0, 22); + auto* anonymousOnly = static_cast(mmap_nid_postfix(nullptr, page, 3, 0x1000, -1, 0)); + Require(anonymousOnly != failed && (reinterpret_cast(anonymousOnly) & (page - 1)) == 0); + anonymousOnly[page - 1] = 7; + Require(anonymousOnly[page - 1] == 7 && anonymousOnly[0] == 0); + Require(munmap_nid_postfix(const_cast(anonymousOnly), page) == 0); + reject(page, 3, 0x1000, 0, 0, 22); auto* memory = static_cast(mmap_nid_postfix(nullptr, page * 3 - 1, 3, 0x1002, -1, 0)); Require(memory != failed && (reinterpret_cast(memory) & (page - 1)) == 0); From ce8f7803ea5d830733c046e1991ed9485fa1fef2 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:54:24 +0200 Subject: [PATCH 052/523] fix(libkernel): check socket address arguments before the descriptor bind, connect and sendto looked the descriptor up before they looked at the address length, so a descriptor that is not open hid an argument error. FreeBSD 11.4 sys_bind, sys_connect and sendit run getsockaddr first (ENAMETOOLONG, EINVAL, EFAULT) and only then look the descriptor up, so these arguments are now answered first and EBADF only follows for a valid address. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 14 +++++++++++--- core/libs/tests/GuestSocketAddressLength.cpp | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 7b64c96aae..deb8421e4a 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -226,10 +226,13 @@ std::shared_ptr Lookup(int descriptor) { Fail(descriptor >= 0 && DescriptorIsOpen_nid_no_patch(descriptor) ? 38 : 9); return {}; } +int AddressLengthError(const void* input, std::uint32_t length) { + if (length > 255) return 63; + if (length < 2) return 22; + return input ? 0 : 14; +} bool Address(const void* input, std::uint32_t length, sockaddr_storage& native, socklen_t& size) { - if (length > 255) { Fail(63); return false; } - if (length < 2) { Fail(22); return false; } - if (!input) { Fail(14); return false; } + if (const int error = AddressLengthError(input, length)) { Fail(error); return false; } const auto* bytes = static_cast(input); if ((bytes[1] == 2 && length < 16) || (bytes[1] == 28 && length < 28)) { Fail(22); return false; } if (bytes[1] == 2 && length >= 16) { @@ -1094,6 +1097,7 @@ int APS5_VABI socketpair_nid_postfix(int family, int type, int protocol, int* de } } int APS5_VABI connect_nid_postfix(int descriptor, const void* address, std::uint32_t length) { + if (const int error = AddressLengthError(address, length)) return Fail(error); const auto socket = Lookup(descriptor); if (!socket) return -1; if (socket->family != GuestUnix && socket->type == 2 && address && length >= 2 && static_cast(address)[1] == 0 @@ -1179,6 +1183,7 @@ std::int64_t APS5_VABI recv_nid_postfix(int descriptor, void* buffer, std::uint6 return result < 0 ? Fail(NativeError()) : result; } int APS5_VABI bind_nid_postfix(int descriptor, const void* address, std::uint32_t length) { + if (const int error = AddressLengthError(address, length)) return Fail(error); const auto socket = Lookup(descriptor); if (!socket) return -1; sockaddr_storage native{}; @@ -1253,6 +1258,9 @@ int APS5_VABI _ioctl_nid_postfix(int descriptor, std::uint64_t request, void* ar } std::int64_t APS5_VABI sendto_nid_postfix(int descriptor, const void* buffer, std::uint64_t length, int flags, const void* address, std::uint32_t addressLength) { + if (address) { + if (const int error = AddressLengthError(address, addressLength)) return Fail(error); + } const auto socket = Lookup(descriptor); if (!socket) return -1; if ((flags & ~GuestSendFlags) != 0) return Fail(45); diff --git a/core/libs/tests/GuestSocketAddressLength.cpp b/core/libs/tests/GuestSocketAddressLength.cpp index 930a8626d6..d18dd5910c 100644 --- a/core/libs/tests/GuestSocketAddressLength.cpp +++ b/core/libs/tests/GuestSocketAddressLength.cpp @@ -56,6 +56,19 @@ void ShortAddressesAreInvalid() { Require(close_nid_postfix(stream) == 0 && close_nid_postfix(datagram) == 0 && close_nid_postfix(stream6) == 0, "close failed"); } +void ArgumentsAreCheckedBeforeTheDescriptor() { + const auto address = Loopback(); + constexpr int ErrorBadDescriptor = 9; + Require(Fails(bind_nid_postfix(-1, address.data(), 1), ErrorInvalid), "bind(-1) with a 1-byte address must fail with EINVAL (22) before the descriptor is looked up"); + Require(Fails(bind_nid_postfix(-1, address.data(), 256), ErrorNameTooLong), "bind(-1) with a 256-byte address must fail with ENAMETOOLONG (63)"); + Require(Fails(bind_nid_postfix(-1, nullptr, address.size()), ErrorFault), "bind(-1) with a null address must fail with EFAULT (14)"); + Require(Fails(connect_nid_postfix(-1, address.data(), 1), ErrorInvalid), "connect(-1) with a 1-byte address must fail with EINVAL (22)"); + Require(Fails(sendto_nid_postfix(-1, "x", 1, 0, address.data(), 1), ErrorInvalid), "sendto(-1) with a 1-byte address must fail with EINVAL (22)"); + Require(Fails(bind_nid_postfix(-1, address.data(), address.size()), ErrorBadDescriptor), "bind(-1) with a valid address must fail with EBADF (9)"); + Require(Fails(connect_nid_postfix(-1, address.data(), address.size()), ErrorBadDescriptor), "connect(-1) with a valid address must fail with EBADF (9)"); + Require(Fails(sendto_nid_postfix(-1, "x", 1, 0, address.data(), address.size()), ErrorBadDescriptor), "sendto(-1) with a valid address must fail with EBADF (9)"); +} + void FullAddressesStillWork() { const int stream = socket_nid_postfix(2, Stream, 0); Require(stream >= 0, "socket failed"); @@ -68,6 +81,7 @@ void FullAddressesStillWork() { int main() { ShortAddressesAreInvalid(); + ArgumentsAreCheckedBeforeTheDescriptor(); FullAddressesStillWork(); return 0; } From 26f861dd272da78edbd4d3d6626f5f39fbc86ed3 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:12:33 +0200 Subject: [PATCH 053/523] feat(libkernel): accept MSG_WAITALL in recv, recvfrom and recvmsg on a stream socket soreceive_generic keeps a MSG_WAITALL receive on a stream socket waiting until the buffer is full, the peer closes or an error occurs. The three calls failed every flag but MSG_PEEK and MSG_DONTWAIT with EOPNOTSUPP. A stream socket now passes the flag to the host receive; a datagram socket ignores it, as a record is delivered whole. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 16 +++++---- core/libs/tests/GuestTcp.cpp | 42 ++++++++++++++++++++++ docs/dev/debt/libkernel.md | 1 + 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 7b64c96aae..edd764e713 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -67,10 +67,11 @@ constexpr auto Invalid = -1; #endif int Fail(int error) { *__error_nid_postfix() = error; return -1; } constexpr int GuestPeek = 0x2; +constexpr int GuestWaitAll = 0x40; constexpr int GuestDontWait = 0x80; constexpr int GuestNoSignal = 0x20000; constexpr int GuestSendFlags = GuestNoSignal | GuestDontWait; -constexpr int GuestReceiveFlags = GuestPeek | GuestDontWait; +constexpr int GuestReceiveFlags = GuestPeek | GuestWaitAll | GuestDontWait; int NativeSendFlags(int flags) { #ifdef _WIN32 static_cast(flags); @@ -79,11 +80,12 @@ int NativeSendFlags(int flags) { return (flags & GuestNoSignal ? MSG_NOSIGNAL : 0) | (flags & GuestDontWait ? MSG_DONTWAIT : 0); #endif } -int NativeReceiveFlags(int flags) { +int NativeReceiveFlags(int flags, int type) { + const int waitAll = type == 1 && (flags & GuestWaitAll) != 0 ? MSG_WAITALL : 0; #ifdef _WIN32 - return flags & GuestPeek ? MSG_PEEK : 0; + return (flags & GuestPeek ? MSG_PEEK : 0) | waitAll; #else - return (flags & GuestPeek ? MSG_PEEK : 0) | (flags & GuestDontWait ? MSG_DONTWAIT : 0); + return (flags & GuestPeek ? MSG_PEEK : 0) | (flags & GuestDontWait ? MSG_DONTWAIT : 0) | waitAll; #endif } int NativeError() { @@ -1172,7 +1174,7 @@ std::int64_t APS5_VABI recv_nid_postfix(int descriptor, void* buffer, std::uint6 if (!destination.Open()) return Fail(14); if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); const auto result = ::recv(socket->value, static_cast(buffer), static_cast(length), - NativeReceiveFlags(flags)); + NativeReceiveFlags(flags, socket->type)); #ifdef _WIN32 if (result < 0 && socket->type == 2 && WSAGetLastError() == WSAEMSGSIZE) return static_cast(length); #endif @@ -1286,7 +1288,7 @@ std::int64_t APS5_VABI recvfrom_nid_postfix(int descriptor, void* buffer, std::u socklen_t size = sizeof(native); if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); auto result = ::recvfrom(socket->value, static_cast(buffer), static_cast(length), - NativeReceiveFlags(flags), reinterpret_cast(&native), &size); + NativeReceiveFlags(flags, socket->type), reinterpret_cast(&native), &size); #ifdef _WIN32 if (result < 0 && socket->type == 2 && WSAGetLastError() == WSAEMSGSIZE) result = static_cast(length); #endif @@ -1434,7 +1436,7 @@ std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message, socklen_t size = sizeof(native); if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); std::int64_t received = ::recvfrom(socket->value, buffer.data(), static_cast(buffer.size()), - NativeReceiveFlags(flags), reinterpret_cast(&native), &size); + NativeReceiveFlags(flags, socket->type), reinterpret_cast(&native), &size); if (received < 0) { #ifdef _WIN32 if (!records || WSAGetLastError() != WSAEMSGSIZE) return Fail(NativeError()); diff --git a/core/libs/tests/GuestTcp.cpp b/core/libs/tests/GuestTcp.cpp index 2b211538ed..15f8d514a6 100644 --- a/core/libs/tests/GuestTcp.cpp +++ b/core/libs/tests/GuestTcp.cpp @@ -17,6 +17,7 @@ int APS5_VABI connect_nid_postfix(int, const void*, std::uint32_t); int APS5_VABI accept_nid_postfix(int, void*, std::uint32_t*); std::int64_t APS5_VABI send_nid_postfix(int, const void*, std::uint64_t, int); std::int64_t APS5_VABI recv_nid_postfix(int, void*, std::uint64_t, int); +std::int64_t APS5_VABI sendto_nid_postfix(int, const void*, std::uint64_t, int, const void*, std::uint32_t); int APS5_VABI getpeername_nid_postfix(int, void*, std::uint32_t*); int APS5_VABI shutdown_nid_postfix(int, int); std::int64_t APS5_VABI recvfrom_nid_postfix(int, void*, std::uint64_t, int, void*, std::uint32_t*); @@ -137,10 +138,51 @@ static void UnboundSocketNameIsZero() { } } +static void WaitAllReceivesTheWholeRequest() { + constexpr int WaitAll = 0x40; + 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(connect_nid_postfix(client, address.data(), address.size()) == 0); + const int accepted = accept_nid_postfix(listener, nullptr, nullptr); + Require(accepted >= 0); + + Require(send_nid_postfix(client, "abcd", 4, 0) == 4); + std::thread sender([client] { + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + Require(send_nid_postfix(client, "efgh", 4, 0) == 4); + }); + char received[8]{}; + Require(recv_nid_postfix(accepted, received, sizeof(received), WaitAll) == 8); + sender.join(); + Require(std::memcmp(received, "abcdefgh", 8) == 0); + + Require(send_nid_postfix(client, "xy", 2, 0) == 2 && shutdown_nid_postfix(client, 1) == 0); + char tail[8]{}; + Require(recvfrom_nid_postfix(accepted, tail, sizeof(tail), WaitAll, nullptr, nullptr) == 2); + Require(std::memcmp(tail, "xy", 2) == 0); + + const int datagram = socket_nid_postfix(2, 2, 0); + Require(datagram >= 0); + std::array local{16, 2, 0, 0, 127, 0, 0, 1}; + Require(bind_nid_postfix(datagram, local.data(), local.size()) == 0); + size = local.size(); + Require(getsockname_nid_postfix(datagram, local.data(), &size) == 0); + Require(sendto_nid_postfix(datagram, "dgram", 5, 0, local.data(), local.size()) == 5); + char record[16]{}; + Require(recv_nid_postfix(datagram, record, sizeof(record), WaitAll) == 5 && std::memcmp(record, "dgram", 5) == 0); + for (const int descriptor : {datagram, client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0); +} + int main() { StreamReceiveReportsNoSender(); ListenersShareAPortAcrossFamilies(); AcceptedSocketsFollowListenerBlocking(); + WaitAllReceivesTheWholeRequest(); UnboundSocketNameIsZero(); const int listener = socket_nid_postfix(2, 1, 6); Require(listener >= 0); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 04342995ea..b7248ac4ef 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -179,6 +179,7 @@ - [UDP recv/recvfrom](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) and [sceNetRecv/sceNetRecvfrom](../../../core/libs/prx/libSceNet/Export.cpp) translate Windows WSAEMSGSIZE into the copied byte count for datagram sockets, including MSG_PEEK, and retain the sender address. This follows FreeBSD 11.4 recv(2) and Winsock's documented truncated-datagram behavior; console behavior and existing title impact are unverified. - [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`. - [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) check a socket address length as FreeBSD 11.4's [`getsockaddr`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_syscalls.c) and the `sa_len` test of `tcp_usr_connect` and `in_pcbconnect` do: below 2 or an `AF_INET` address below 16 or an `AF_INET6` address below 28 bytes fails with `EINVAL`, above 255 with `ENAMETOOLONG`. An address longer than `sockaddr_in` or `sockaddr_in6` is still accepted where FreeBSD answers `EINVAL`, and an address of another family still fails with `EAFNOSUPPORT`, as the console's behaviour for these is not known. None of this was measured on a console. - [read, write and vector I/O](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) on a guest socket use `recv` and `send` without flags; `sceKernelReadv` and `sceKernelWritev` gather or scatter through one socket operation, preserving stream short reads and datagram boundaries. The positioned `pread`, `pwrite`, `preadv` and `pwritev` forms, and their `sceKernel` forms, return `ESPIPE` for an open guest socket and `EBADF` for a closed one. `EPIPE` from a write raises the host's `SIGPIPE` on Linux unless the socket has `SO_NOSIGPIPE` set, and the guest cannot ignore it: `signal(SIGPIPE, SIG_IGN)` and `sigaction` record the disposition, as they do for every number from 1 to 128, but only the six signals with host equivalents (2, 4, 6, 8, 11 and 15) take effect on the host or are delivered, so the host `SIGPIPE` is not stopped; nothing is raised on Windows; a `write` longer than `INT_MAX` bytes is `EMSGSIZE` where the console sends it. AIO on a guest socket still goes to the host, which has no such descriptor From 1dbf3b4ac2416407078036b954416158f8ac5b5c Mon Sep 17 00:00:00 2001 From: orezraey Date: Tue, 20 Oct 2026 10:35:28 -0300 Subject: [PATCH 054/523] feat(libScePlayGo): store the language mask scePlayGoSetLanguageMask sets Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libScePlayGo/Export.cpp | 8 +++++--- core/libs/tests/GuestPlayGo.cpp | 7 +++++++ docs/dev/debt/libScePlayGo.md | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libScePlayGo/Export.cpp b/core/libs/prx/libScePlayGo/Export.cpp index 555a68f373..32782ddc1a 100644 --- a/core/libs/prx/libScePlayGo/Export.cpp +++ b/core/libs/prx/libScePlayGo/Export.cpp @@ -25,6 +25,7 @@ static constexpr int32_t PLAYGO_INSTALL_SPEED_FULL = 2; static constexpr uint32_t PLAYGO_SCENARIO_CHUNK_COUNT = 1000; static int32_t g_installSpeed = PLAYGO_INSTALL_SPEED_FULL; +static uint64_t g_languageMask = PLAYGO_LANGUAGE_MASK_ALL; static constexpr int SCE_PLAYGO_ERROR_BAD_CHUNK_ID = static_cast(0x80B2000C); static constexpr int SCE_PLAYGO_ERROR_BAD_SIZE = static_cast(0x80B2000B); @@ -169,7 +170,7 @@ int APS5_VABI scePlayGoGetInstallSpeed(int handle, int32_t* out_speed) { int APS5_VABI scePlayGoGetLanguageMask(int handle, uint64_t* out_language_mask) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; if (!out_language_mask) return SCE_PLAYGO_ERROR_BAD_POINTER; - *out_language_mask = PLAYGO_LANGUAGE_MASK_ALL; + *out_language_mask = g_languageMask; return 0; } @@ -267,8 +268,9 @@ int APS5_VABI scePlayGoRequestNextChunk() { return 0; } -int APS5_VABI scePlayGoSetLanguageMask(std::uint32_t handle, std::uint64_t languageMask) { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI scePlayGoSetLanguageMask(int handle, uint64_t language_mask) { + if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; + g_languageMask = language_mask; return 0; } diff --git a/core/libs/tests/GuestPlayGo.cpp b/core/libs/tests/GuestPlayGo.cpp index 4dd0d6a5f7..24ff50b6d0 100644 --- a/core/libs/tests/GuestPlayGo.cpp +++ b/core/libs/tests/GuestPlayGo.cpp @@ -17,6 +17,7 @@ int APS5_VABI scePlayGoInitialize(const PlayGoInitParams*); int APS5_VABI scePlayGoOpen(int*, const void*); int APS5_VABI scePlayGoClose(int); int APS5_VABI scePlayGoGetLanguageMask(int, std::uint64_t*); +int APS5_VABI scePlayGoSetLanguageMask(int, std::uint64_t); int APS5_VABI scePlayGoGetSupportedOptionalChunk(int, std::int32_t, PlayGoOptionalChunk*); int APS5_VABI scePlayGoGetOptionalChunk(int, std::int32_t, PlayGoOptionalChunk*); int APS5_VABI scePlayGoSetToDoList(int, const PlayGoToDo*, std::uint32_t); @@ -103,6 +104,12 @@ static int CheckNoPlayGoFile(const char* root) { Require(scePlayGoGetLanguageMask(handle + 1, &mask) == badHandle); Require(scePlayGoGetLanguageMask(handle, nullptr) == badPointer); Require(scePlayGoGetLanguageMask(handle, &mask) == 0 && mask == ~0ull); + Require(scePlayGoSetLanguageMask(handle + 1, 0x5) == badHandle); + Require(scePlayGoGetLanguageMask(handle, &mask) == 0 && mask == ~0ull); + Require(scePlayGoSetLanguageMask(handle, 0x5) == 0); + Require(scePlayGoGetLanguageMask(handle, &mask) == 0 && mask == 0x5); + Require(scePlayGoSetLanguageMask(handle, ~0ull) == 0); + Require(scePlayGoGetLanguageMask(handle, &mask) == 0 && mask == ~0ull); for (const auto getOptionalChunk : {scePlayGoGetSupportedOptionalChunk, scePlayGoGetOptionalChunk}) { PlayGoOptionalChunk option{}; diff --git a/docs/dev/debt/libScePlayGo.md b/docs/dev/debt/libScePlayGo.md index e1834da340..cfd6b6de0d 100644 --- a/docs/dev/debt/libScePlayGo.md +++ b/docs/dev/debt/libScePlayGo.md @@ -3,6 +3,6 @@ ### Unknown function info - [scePlayGoRequestNextChunk, scePlayGoSnapshot](../../../core/libs/prx/libScePlayGo/Export.cpp) (libScePlayGo) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePlayGo.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim -- [scePlayGoSetLanguageMask](../../../core/libs/prx/libScePlayGo/Export.cpp) (libScePlayGo) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePlayGo.c); the `(uint32_t, uint64_t)` prototype follows [ChonkyStation4 `ScePlayGo.cpp`](https://github.com/liuk7071/ChonkyStation4/blob/c101a23ecbc15e56c6e30d5dd24d3ed8880a957a/ChonkyStation4/OS/Libraries/ScePlayGo/ScePlayGo.cpp#L240), is not measured on a console, and every call throws +- [scePlayGoSetLanguageMask](../../../core/libs/prx/libScePlayGo/Export.cpp) (libScePlayGo) - stores the mask that `scePlayGoGetLanguageMask` then reports, and returns `SCE_PLAYGO_ERROR_BAD_HANDLE` for another handle, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/8d50471b7dc141dd700b51276b8edd1f293bce26/src/core/libraries/playgo/playgo.cpp#L346-L359) does on the PS4. shadPS4 also returns `SCE_PLAYGO_ERROR_NOT_INITIALIZED` before `scePlayGoInitialize` and starts from the system language's mask; here the mask starts with every language and initialization is not tracked. Not measured on a PS5 - [libScePlayGo](../../../core/libs/prx/libScePlayGo/Export.cpp) - the chunk set comes from `playgo-chunkdefs.xml` (every listed chunk plus chunks 0 through the default chunk), otherwise from `sce_sys/playgo-chunk.dat`, whose `plgx` header is read as holding the chunk count as a 16-bit value at offset 0x0A and ids 0 to count - 1 are taken as valid. The field was identified on four dumps, where exactly that many 0x20-byte entries follow from offset 0x100 (RIDE 4 PPSA01599: 35, Persona 5 Royal PPSA05110: 25, LEGO Horizon Adventures PPSA14632 and Kena PPSA01802: 1); the rest of the format is not decoded and ids are assumed to be contiguous. Every listed chunk is reported as installed (`LOCAL_FAST`). Without either file, a package with `sce_sys/playgo-scenario.json` (PPSA01748, whose dump lists no chunks) accepts every chunk id as installed, and `scePlayGoGetChunkId` lists ids 0 to 999. A title that builds its set of installed chunks from that list skips any chunk it does not see. The console's chunk limit is not known here, and a package with more chunks would see ids from 1000 up as not listed. A package with no `playgo-*` file in `sce_sys` (PPSA03001, PPSA03490 and PPSA01285 here) accepts chunk 0; querying any other id throws with a message naming the missing `playgo-chunk.dat` and `playgo-scenario.json`, since the incomplete dump cannot establish whether that id exists; `APS5_PLAYGO_UNLISTED_CHUNKS=installed` answers every such id as installed (`LOCAL_FAST`, which PPSA15388 needs to load its saves) and `=absent` answers `SCE_PLAYGO_ERROR_BAD_CHUNK_ID` (which ends PPSA03001's id walk), as the user's choice for that dump. A `sce_sys` that holds other `playgo-*` files (`playgo-ficm.dat`, `playgo-hash-table.dat`) without `playgo-chunk.dat` or `playgo-scenario.json`, or a `playgo-chunk.dat` without a `plgx` header and a nonzero count, throws naming the file, because no such dump is known. These answers are unverified on a console, as is the console's answer for an id outside a package's chunk table. - [libScePlayGo](../../../core/libs/prx/libScePlayGo/Export.cpp) - `scePlayGoGetSupportedOptionalChunk` and `scePlayGoGetOptionalChunk` report every bit set for any `type`, i.e. every optional chunk (language pack) supported and installed, like `scePlayGoGetLanguageMask`. The bit layout and the values of `type` are not known; PPSA01748 calls both with type 0 and hides every text language listed in `playgo-scenario.json` `chunkSupportedLanguages` when the bitmask is 0. The console's answer for a package that ships no optional chunks is unverified. From 669455d1f27cf71227c7c64aad1d99b5689e4078 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:56:03 +0200 Subject: [PATCH 055/523] fix(libkernel): reject a pthread barrier count above INT_MAX like libthr _pthread_barrier_init fails a count above INT_MAX with EINVAL. pthread_barrier_init accepted any count above zero. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp | 3 ++- core/libs/tests/GuestPthreadBarrier.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp b/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp index 8a4c613626..4f4c7884db 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp @@ -1,4 +1,5 @@ #include "prx/libc/include/General.hpp" +#include #include #include #include @@ -60,7 +61,7 @@ int APS5_VABI pthread_barrierattr_setpshared_nid_postfix(PthreadBarrierattrPriva int APS5_VABI pthread_barrier_init_nid_postfix(PthreadBarrierPrivate** barrier, PthreadBarrierattrPrivate* const* attr, unsigned count) { (void)attr; - if (!barrier || count == 0) return 22; + if (!barrier || count == 0 || count > INT_MAX) return 22; try { auto* value = new PthreadBarrierPrivate; value->count = count; diff --git a/core/libs/tests/GuestPthreadBarrier.cpp b/core/libs/tests/GuestPthreadBarrier.cpp index 6fbdcb4bad..163c97b14c 100644 --- a/core/libs/tests/GuestPthreadBarrier.cpp +++ b/core/libs/tests/GuestPthreadBarrier.cpp @@ -91,6 +91,8 @@ int main() { GuestBarrier* invalid = nullptr; Require(pthread_barrier_init_nid_postfix(nullptr, &attr, 1) == GUEST_EINVAL); Require(pthread_barrier_init_nid_postfix(&invalid, &attr, 0) == GUEST_EINVAL && invalid == nullptr); + Require(pthread_barrier_init_nid_postfix(&invalid, &attr, 0x80000000u) == GUEST_EINVAL && invalid == nullptr); + Require(pthread_barrier_init_nid_postfix(&invalid, &attr, 0xffffffffu) == GUEST_EINVAL && invalid == nullptr); Context withAttr; Require(pthread_barrier_init_nid_postfix(&withAttr.barrier, &attr, WAITERS) == 0 && withAttr.barrier != nullptr); From 894e9993a2265200e9bae5c093d594ba8b6aadaa Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:56:07 +0200 Subject: [PATCH 056/523] fix(libkernel): reject a negative cond timedwait deadline like libthr _pthread_cond_timedwait fails an abstime with a negative tv_sec with EINVAL before it looks at the condition variable or the mutex. pthread_cond_timedwait took it as a deadline in the past and answered ETIMEDOUT. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/Pthread/Posix/Cond.cpp | 2 +- core/libs/tests/GuestCondTimedwait.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp b/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp index 6dd1e09509..f357cac53a 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp @@ -39,7 +39,7 @@ int APS5_VABI pthread_cond_signal_nid_postfix(PthreadCond* cond) { } int APS5_VABI pthread_cond_timedwait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime) { - if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return PosixThread::GUEST_EINVAL; + if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return PosixThread::GUEST_EINVAL; return toPosix(CondOperations::AbsoluteTimedwait(cond, mutex, abstime)); } diff --git a/core/libs/tests/GuestCondTimedwait.cpp b/core/libs/tests/GuestCondTimedwait.cpp index 91c18ac93e..434d257d2d 100644 --- a/core/libs/tests/GuestCondTimedwait.cpp +++ b/core/libs/tests/GuestCondTimedwait.cpp @@ -18,6 +18,7 @@ int APS5_VABI scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, un int APS5_VABI scePthreadCondSignal(PthreadCond* cond); int APS5_VABI pthread_cond_init_nid_postfix(PthreadCond* cond, const PthreadCondattr* attr); int APS5_VABI pthread_cond_destroy_nid_postfix(PthreadCond* cond); +int APS5_VABI pthread_cond_timedwait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime); int APS5_VABI pthread_cond_reltimedwait_np_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, unsigned int usec); int APS5_VABI __cxa_atexit_nid_postfix(void (APS5_VABI *)(void*), void*, void*); void APS5_VABI __pthread_cxa_finalize_nid_postfix(void*); @@ -31,6 +32,7 @@ static constexpr int SCE_OK = 0; static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = static_cast(0x8002003C); static constexpr int SCE_KERNEL_ERROR_EBUSY = static_cast(0x80020010); static constexpr int POSIX_EPERM = 1; +static constexpr int POSIX_EINVAL = 22; static constexpr int POSIX_ETIMEDOUT = 60; static void Require(bool value) { if (!value) std::abort(); } @@ -131,6 +133,14 @@ int main() { Require(scePthreadMutexUnlock(&context.mutex) == SCE_OK); Require(scePthreadCondDestroy(&cond) == SCE_OK); Require(pthread_cond_init_nid_postfix(&cond, &unset) == 0); + { + const KernelTimespec negativeSeconds{-1, 0}; + const KernelTimespec negativeNanoseconds{1, -1}; + Require(scePthreadMutexLock(&context.mutex) == SCE_OK); + Require(pthread_cond_timedwait_nid_postfix(&cond, &context.mutex, &negativeSeconds) == POSIX_EINVAL); + Require(pthread_cond_timedwait_nid_postfix(&cond, &context.mutex, &negativeNanoseconds) == POSIX_EINVAL); + Require(scePthreadMutexUnlock(&context.mutex) == SCE_OK); + } Require(pthread_cond_destroy_nid_postfix(&cond) == 0); Require(scePthreadMutexDestroy(&context.mutex) == SCE_OK); From 52a64aef65bac29b85c3725ebcaae0eb66bee35f Mon Sep 17 00:00:00 2001 From: MiguelAdolfsen Date: Thu, 15 Oct 2026 03:41:22 +0200 Subject: [PATCH 057/523] perf(agc): refresh only the layers a use of an array color target covers A color view of one slice of a 2D array target refreshed the whole array image before each draw, sampled lookup and present. When the array's unused slices lie over other live targets (ps5-opengl declares every target as a two-slice array and allocates the next one over its second slice), each refresh stored the other targets' pending results, wrote back its own and uploaded the whole image from guest memory again, on every pass. StorageTexture::RefreshLayers refreshes the tracked layers that hold the given array layers alone: it flushes and compares their memory only, and results of other images pending over the other layers stay pending. A layer outside the range whose memory changed or that holds another image's pending results is left behind; GenerationFor reports it as never current, so a use over it takes the whole Refresh. It falls back to Refresh for volumes, single-layer and DCC images, an alias, or results of the image pending outside the range. A draw into an array slice, a 2D view of one layer of an array image and a present of one layer use it. The compare memo of untracked memory is kept per range, so a layer refresh's compare is memoized like the whole image's. Co-Authored-By: Claude Opus 5.5 --- .../Execution/src/VulkanDevice.cpp | 5 +- .../Graphics/include/ShaderResources.hpp | 1 + .../Graphics/include/Texture.hpp | 13 +- .../prx/libSceAgcDriver/Graphics/src/Draw.cpp | 5 +- .../Graphics/src/ShaderResources.cpp | 36 ++++-- .../libSceAgcDriver/Graphics/src/Texture.cpp | 116 +++++++++++++++--- .../tests/execution/ArrayLayerTarget.cpp | 47 +++++-- 7 files changed, 182 insertions(+), 41 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index c19c2efa29..b372d843c2 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -3182,8 +3182,9 @@ bool VulkanDevice::PresentDisplayBuffer(const DisplayBuffer& buffer) { static const bool noAdopt = std::getenv("APS5_NO_PRESENT_EPOCH") != nullptr; if (!noAdopt) GuestMemory::AdoptLatestCollectEpoch(); GuestMemory::CollectWritesUncached(buffer.address, bytes); - if (!GuestMemory::UnchangedSince(buffer.address, bytes, resident->Generation()) || (buffer.dccAddress != 0 && ResidentKeysMoved(*resident))) { - resident->Refresh(); + if (!GuestMemory::UnchangedSince(buffer.address, bytes, resident->GenerationFor(buffer.address, bytes)) || (buffer.dccAddress != 0 && ResidentKeysMoved(*resident))) { + if (resident->ImageLayers() > 1) resident->RefreshLayers(layer, 1); + else resident->Refresh(); ++refreshedPresents; } ++residentPresents; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp index 9999703dc6..bf3f14504e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp @@ -52,6 +52,7 @@ bool SpirvStoresThroughAddresses(std::span words); // The cached storage image of a surface (render targets use it as their resident image); brought up // to date with guest memory before it is returned. std::shared_ptr CachedStorageSurface(const Context& context, const GuestTextureResource& resource); +std::shared_ptr CachedStorageLayers(const Context& context, const GuestTextureResource& resource, std::uint32_t firstLayer, std::uint32_t layerCount); // Whether `image` is still the storage cache's image of its surface (what CachedStorageSurface // would return); a true answer counts as a use for the cache's eviction order, as a lookup would. bool StorageImageCached(const Context& context, const StorageTexture* image); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp index f54483fcbd..70a910562f 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp @@ -349,6 +349,7 @@ public: // validates textures copied from this image. std::uint64_t Version() const { return version; } std::uint64_t Generation() const { return generation; } + std::uint64_t GenerationFor(std::uint64_t address, std::uint64_t bytes) const; // The DCC keys the content was uploaded under and the last scan of the surface's keys // (ProvedClearKeys): Refresh's own key rule for the fast revalidation. Under // GuestMemory::GpuMutex only, as Refresh is; never from a build's stage A. @@ -363,6 +364,7 @@ public: // was still current (nothing uploaded). // Keeps the image current with guest memory (see GuestMemory::CollectWrites). bool Refresh(); + bool RefreshLayers(std::uint32_t first, std::uint32_t count); static std::uint64_t SinglePassMoves(); static std::uint64_t SlabRetiles(); static std::uint64_t RefreshesProved(); @@ -473,6 +475,8 @@ private: // layer generation (the whole-surface fast check), `dirty` whether any layer is pending. std::uint64_t layerBegin(std::uint32_t layer) const { return descriptor.baseAddress + static_cast(layer) * trackedLayerBytes; } bool anyLayerPending() const; + bool refresh(std::uint32_t firstLayer, std::uint32_t lastLayer); + void noteLayersBehind(std::uint32_t firstLayer, std::uint32_t lastLayer, const std::vector& outside); void refreshGeneration(); bool refreshProved(); void takeRefreshProof(bool aliased); @@ -549,7 +553,13 @@ private: std::uint64_t sliceLinearBytes = 0; SurfaceGeometry geometry; std::vector original; - mutable std::array comparedGuestBytes{}; + struct ComparedRange { + std::uint64_t address = 0; + std::size_t bytes = 0; + std::array stamp{}; + }; + mutable std::array comparedRanges{}; + mutable std::uint32_t nextComparedRange = 0; std::vector generationBaseline; // DCC keys the image content was uploaded under: a fast-cleared surface starts as its clear value. DccKeys uploadedKeys = DccKeys::Uncompressed; @@ -575,6 +585,7 @@ private: std::uint64_t trackedLayerBytes = 0; std::vector layerGeneration; std::vector layerPending; + std::vector layerBehind; bool blockUnits = false; // Write-back hysteresis (block units, see writeBack): partial stores in the current window of // presents, its first present, and the present until which every pending unit is stored. diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index e0a69a53ac..61f6e4690e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -2234,7 +2234,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters& const auto geometry = DescribeSurface(array); for (std::uint32_t slice = 0; slice <= color.arrayLastSlice; ++slice) Require(geometry.GuestLayerOffset(slice) == slice * sliceBytes, "the array image's layer pitch differs from the color view's slice offset"); Require(color.surfaceAddress == color.arrayAddress + color.arraySlice * sliceBytes, "the array image's layer pitch differs from the color view's slice offset"); - auto resident = CachedStorageSurface(context, array); + auto resident = CachedStorageLayers(context, array, color.arraySlice, 1); Require(resident->Attachable(), "storage format cannot be a color attachment"); return resident; } @@ -2833,7 +2833,8 @@ DrawRecipeOutcome DrawWithRecipe(const Context& context, const State& state, con auto stored = recipe.targets[index].lock(); if (stored == nullptr || !StorageImageCached(context, stored.get()) || !StorageImageServesKeys(*stored, state.colors[index].dccAddress)) return miss(DrawRecipeMiss::TargetGone); auto resident = refreshResidentTarget(context, state, state.colors[index], outcome, profile, [&] { - stored->Refresh(); + if (state.colors[index].arrayLastSlice != 0) stored->RefreshLayers(state.colors[index].arraySlice, 1); + else stored->Refresh(); return stored; }); timer.phase(PhaseReadTarget); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index 56936290cd..fcb90818cc 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -460,7 +460,12 @@ private: bool outermost; }; -std::shared_ptr cachedStorageTexture(const Context& context, std::span words, const GuestTextureResource& resource, std::uint32_t mip, std::uint64_t guestBytes = 0, bool written = false); +void refreshForView(StorageTexture& source, const GuestTextureResource& resource) { + if (resource.dimension == TextureDimension::k2D && source.ImageLayers() > 1) source.RefreshLayers(resource.baseArray, 1); + else source.Refresh(); +} + +std::shared_ptr cachedStorageTexture(const Context& context, std::span words, const GuestTextureResource& resource, std::uint32_t mip, std::uint64_t guestBytes = 0, bool written = false, std::optional> layers = std::nullopt); std::uint64_t heldBytes(const StorageTexture& image) { return image.BudgetBytes(); @@ -785,9 +790,9 @@ std::shared_ptr cachedTexture(const Context& context, std::spanGeneration())) { + if (source != nullptr && !GuestMemory::UnchangedSince(address, bytes, source->GenerationFor(address, bytes))) { // The CPU wrote the memory while results were pending: Refresh merges them the usual way. - source->Refresh(); + refreshForView(*source, resource); } // Results stored to guest memory just now are stamped newer than `generation`; a snapshot read // after them is current at the generation of a second (memoized) collect. The store marks the @@ -855,7 +860,7 @@ std::shared_ptr cachedTexture(const Context& context, std::spansource) && (source != nullptr || *keys == DccKeys::Uncompressed) && StorageImageServesKeys(*it->source, resource.dccAddress)) { - if (!GuestMemory::UnchangedSince(address, bytes, it->source->Generation())) it->source->Refresh(); + if (!GuestMemory::UnchangedSince(address, bytes, it->source->GenerationFor(address, bytes))) refreshForView(*it->source, resource); if (it->texture->IsDepthCopy()) it->texture->RefreshDepthCopy(); it->keys = *keys; touchTexture(cache, it); @@ -1102,7 +1107,7 @@ static WrittenStorage WrittenStorageBytes(const GuestTextureResource& viewed, st } // `guestBytes` is the surface size when the caller described the surface already (0: described here). -std::shared_ptr cachedStorageTexture(const Context& context, std::span words, const GuestTextureResource& requested, std::uint32_t mip, std::uint64_t guestBytes, bool written) { +std::shared_ptr cachedStorageTexture(const Context& context, std::span words, const GuestTextureResource& requested, std::uint32_t mip, std::uint64_t guestBytes, bool written, std::optional> layers) { const auto viewed = ArrayLayerWindow(requested, context.limits.maxImageArrayLayers); if (viewed.baseAddress != requested.baseAddress) guestBytes = 0; Require(viewed.dimension != TextureDimension::k2DMsaa, "storage image access to a multisampled texture is not implemented"); @@ -1147,7 +1152,8 @@ std::shared_ptr cachedStorageTexture(const Context& context, std it = cache.entries.end(); } if (it != cache.entries.end()) { - it->texture->Refresh(); + if (layers) it->texture->RefreshLayers(layers->first, layers->second); + else it->texture->Refresh(); cache.entries.splice(cache.entries.begin(), cache.entries, it); texture = it->texture; counters.storageHits.fetch_add(1, std::memory_order_relaxed); @@ -1429,6 +1435,10 @@ std::shared_ptr CachedStorageSurface(const Context& context, con return cachedStorageTexture(context, {}, resource, 0); } +std::shared_ptr CachedStorageLayers(const Context& context, const GuestTextureResource& resource, std::uint32_t firstLayer, std::uint32_t layerCount) { + return cachedStorageTexture(context, {}, resource, 0, 0, false, std::pair{firstLayer, layerCount}); +} + bool StorageImageServesKeys(const StorageTexture& image, std::uint64_t dccAddress) { GuestTextureResource resource{}; resource.dccAddress = dccAddress; @@ -2977,7 +2987,7 @@ bool ShaderResources::fastRevalidate(std::uint64_t serialBefore, std::spanCached()) return fail(FastFail::Evicted); - queries.push_back({address, bytes, source->Generation()}); + queries.push_back({address, bytes, source->GenerationFor(address, bytes)}); images.push_back(source); } else { queries.push_back({address, bytes, surface.generation}); @@ -3005,7 +3015,7 @@ bool ShaderResources::fastRevalidate(std::uint64_t serialBefore, std::spanCached()) return fail(FastFail::Evicted); - queries.push_back({address, bytes, image->Generation()}); + queries.push_back({address, bytes, image->GenerationFor(address, bytes)}); images.push_back(image); } if (!pending.empty()) { @@ -3071,7 +3081,7 @@ bool ShaderResources::fastRevalidateEach() { if (surface.source != nullptr) { // The view follows the image: it needs the image current with guest memory, as the // lookup's Refresh would make it. - if (!StorageImageCached(context, surface.source) || !GuestMemory::UnchangedSince(address, bytes, surface.source->Generation())) return false; + if (!StorageImageCached(context, surface.source) || !GuestMemory::UnchangedSince(address, bytes, surface.source->GenerationFor(address, bytes))) return false; } else if (!GuestMemory::UnchangedSince(address, bytes, surface.generation)) { return false; } @@ -3105,7 +3115,7 @@ bool ShaderResources::fastRevalidateEach() { if (GuestMemory::CollectWrites(address, bytes) == 0) return false; if (PendingStorageOverlaps(address, bytes, image)) return false; if (!StorageImageCached(context, image)) return false; - if (!GuestMemory::UnchangedSince(address, bytes, image->Generation())) return false; + if (!GuestMemory::UnchangedSince(address, bytes, image->GenerationFor(address, bytes))) return false; } // Every check passed: snapshot textures are current at the collects issued before them. Storage // images keep their own generation: nothing was stamped above it, so the blocks a later write @@ -3139,9 +3149,9 @@ ShaderResources::OwnRefreshFallback ShaderResources::refreshOwnObjects(std::span if (found != nullptr && !Texture::CanCopyFrom(*found, surface.resource)) found.reset(); if (found != nullptr) { if (found != source) return OwnRefreshFallback::ForeignView; - if (!GuestMemory::UnchangedSince(address, bytes, source->Generation())) { + if (!GuestMemory::UnchangedSince(address, bytes, source->GenerationFor(address, bytes))) { countOwnRefresh(false); - source->Refresh(); + refreshForView(*source, surface.resource); } return OwnRefreshFallback::Count; } @@ -4514,7 +4524,7 @@ std::shared_ptr ShaderResources::fastTexture(const ImageRecord& record) // keys and stays valid while they are unchanged); a change is the full lookup's to judge. if (keys != record.entryKeys) return nullptr; if (record.source != nullptr) { - if (!StorageImageCached(context, record.source.get()) || !GuestMemory::UnchangedSince(address, bytes, record.source->Generation())) return nullptr; + if (!StorageImageCached(context, record.source.get()) || !GuestMemory::UnchangedSince(address, bytes, record.source->GenerationFor(address, bytes))) return nullptr; if (record.resource.dccAddress != 0 && IsDccClear(record.source->FilledKeys())) return nullptr; // Under fast-clear keys the view holds only while its image's results are still pending over // the surface; flushed, the clear the image cannot see makes the lookup take a snapshot. diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp index a2d90b6926..2168c4a4b2 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp @@ -2016,11 +2016,31 @@ LookupOutcomes& ThreadLookupOutcomes() { } bool StorageTexture::Refresh() { + return refresh(0, trackedLayers); +} + +bool StorageTexture::RefreshLayers(std::uint32_t first, std::uint32_t count) { + if (count == 0 || geometry.thick || geometry.imageLayers <= 1 || first >= geometry.imageLayers || count > geometry.imageLayers - first || geometry.layerBytes == 0 || trackedLayerBytes == 0 || descriptor.dccAddress != 0) return Refresh(); + const auto begin = geometry.GuestLayerOffset(first); + const auto end = geometry.GuestLayerOffset(first + count - 1) + geometry.layerBytes; + const auto firstLayer = static_cast(begin / trackedLayerBytes); + const auto lastLayer = static_cast(std::min((end + trackedLayerBytes - 1) / trackedLayerBytes, trackedLayers)); + if (firstLayer >= lastLayer || (firstLayer == 0 && lastLayer == trackedLayers)) return Refresh(); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { + if (layerPending[layer] && (layer < firstLayer || layer >= lastLayer)) return Refresh(); + } + if (pendingAlias() != nullptr) return Refresh(); + return refresh(firstLayer, lastLayer); +} + +bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) { const bool profile = LookupOutcomes::Profiled(); auto start = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + const bool scoped = firstLayer != 0 || lastLayer != trackedLayers; CompletePendingCpuWrites(context, descriptor.baseAddress, static_cast(guestBytes)); if (descriptor.dccAddress != 0) CompletePendingCpuWrites(context, descriptor.dccAddress, DccKeyBytes(guestBytes)); if (refreshProved()) { + layerBehind.clear(); NoteProved(); refreshesProved.fetch_add(1, std::memory_order_relaxed); if (profile) LookupOutcomes::Add(LookupOutcomes::RefreshProved, start); @@ -2042,7 +2062,7 @@ bool StorageTexture::Refresh() { // generation, no results of this image's own there) or dead to its own write-back (tracked and // stamped); an untracked unit is stored whole by that write-back, and a unit pending in both // images takes the old order (store, then this image re-uploads it). - auto alias = pendingAlias(); + auto alias = scoped ? std::shared_ptr{} : pendingAlias(); if (alias != nullptr && !(ProvedClearKeys(descriptor, guestBytes, keyProof) == DccKeys::Uncompressed && HostImportFor(context, descriptor.baseAddress, static_cast(guestBytes)) != nullptr)) alias = nullptr; if (alias != nullptr) { std::vector aliasStamped(trackedLayers); @@ -2055,8 +2075,10 @@ bool StorageTexture::Refresh() { } } } + const auto scopeBegin = scoped ? layerBegin(firstLayer) : descriptor.baseAddress; + const auto scopeBytes = scoped ? static_cast(layerBegin(lastLayer - 1) + layerBytes(lastLayer - 1) - scopeBegin) : static_cast(guestBytes); // No publish: the upload below reads the stored units from the unit shadow itself. - const bool flushed = FlushPending(descriptor.baseAddress, static_cast(guestBytes), alias.get(), "storage refresh", PublishScope::None); + const bool flushed = FlushPending(scopeBegin, scopeBytes, alias.get(), "storage refresh", PublishScope::None); if (profile && flushed) start = LookupOutcomes::Add(LookupOutcomes::PendingFlush, start); // `original` holds the guest bytes the image was last uploaded from or written back as; while the // guest memory and the DCC keys still match, the image content is current. Pages nobody wrote @@ -2068,6 +2090,7 @@ bool StorageTexture::Refresh() { // by a CPU store; scanned once when anything changed. std::vector generations; std::vector stampedBlocks, cpuBlocks; + std::vector outside; // Units taken from the alias's image instead of guest memory. std::vector borrowed; bool anyBorrowed = false; @@ -2083,6 +2106,7 @@ bool StorageTexture::Refresh() { const auto equalsOriginal = [&] { // Named for the [hooksync] attribution: the compare goes through the flush hook. const GuestMemory::ReadSiteScope site(GuestMemory::ReadSite::TextureCompare); + if (scoped) return GuestMemory::EqualsCommitted(scopeBegin, std::span(original).subspan(static_cast(scopeBegin - descriptor.baseAddress), scopeBytes)); return GuestMemory::EqualsCommitted(descriptor.baseAddress, original); }; const auto scanBlocks = [&] { @@ -2091,13 +2115,19 @@ bool StorageTexture::Refresh() { cpuBlocks.assign(generations.size(), 0); tracked = GuestMemory::ChangedBlocks(descriptor.baseAddress, static_cast(guestBytes), generations, stampedBlocks, cpuBlocks); if (!tracked) awaitStoredSave(nullptr); - compared = !tracked && compareUntracked(descriptor.baseAddress, static_cast(guestBytes), stampedBlocks, true); + const auto firstBlock = static_cast(scopeBegin / 65536 - descriptor.baseAddress / 65536); + const auto blocks = static_cast((scopeBegin + scopeBytes - 1) / 65536 - scopeBegin / 65536 + 1); + compared = !tracked && (scoped ? compareUntracked(scopeBegin, scopeBytes, std::span(stampedBlocks).subspan(firstBlock, blocks), true) : compareUntracked(descriptor.baseAddress, static_cast(guestBytes), stampedBlocks, true)); if (compared) { for (const auto& [from, unitGeneration] : keptAtUnwatch) { const auto unit = static_cast((from - descriptor.baseAddress) / trackedLayerBytes); if (keptAtUnwatchHolds(from, unit)) stampedBlocks[static_cast(from / 65536 - descriptor.baseAddress / 65536)] = GuestMemory::BlockWritten; } cpuBlocks = stampedBlocks; + if (scoped) { + std::fill(stampedBlocks.begin(), stampedBlocks.begin() + static_cast(firstBlock), GuestMemory::BlockWritten); + std::fill(stampedBlocks.begin() + static_cast(firstBlock + blocks), stampedBlocks.end(), GuestMemory::BlockWritten); + } } }; const auto keysStart = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; @@ -2142,6 +2172,15 @@ bool StorageTexture::Refresh() { } unchanged = std::none_of(changed.begin(), changed.end(), [](bool value) { return value; }); } + if (scoped && !keysChanged) { + outside.assign(trackedLayers, false); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { + if ((layer >= firstLayer && layer < lastLayer) || (!changed[layer] && !compared)) continue; + outside[layer] = true; + changed[layer] = false; + } + unchanged = std::none_of(changed.begin(), changed.end(), [](bool value) { return value; }); + } for (std::size_t k = 0; k < cpuBlocks.size(); ++k) { if (cpuBlocks[k] != 0 && (compared || (tracked && generations[k] != 0))) cpuWrote = true; } @@ -2152,9 +2191,10 @@ bool StorageTexture::Refresh() { for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { if (changed[layer] && layerPending[layer]) pendingChanged = true; } - if (!unchanged && !keysChanged && !pendingChanged && originalHolds(descriptor.baseAddress, static_cast(guestBytes)) && equalsOriginal()) { + if (!unchanged && !keysChanged && !pendingChanged && (scoped ? originalHolds(scopeBegin, scopeBytes) : originalHolds(descriptor.baseAddress, static_cast(guestBytes))) && equalsOriginal()) { unchanged = true; stamped = false; + if (!scoped) outside.clear(); } // Only the direct path uploads the selected layers alone (see upload); the others replace // the whole image, so every pending layer's results are stored first. @@ -2182,11 +2222,16 @@ bool StorageTexture::Refresh() { } } } + const bool outsideChanged = std::any_of(outside.begin(), outside.end(), [](bool value) { return value; }); + if (scoped) noteLayersBehind(firstLayer, lastLayer, outside); + else layerBehind.clear(); if (unchanged) { ++Profile().storageReused; - layerGeneration.assign(trackedLayers, current); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { + if (!outsideChanged || !outside[layer]) layerGeneration[layer] = current; + } refreshGeneration(); - takeRefreshProof(alias != nullptr); + if (layerBehind.empty()) takeRefreshProof(alias != nullptr); if (profile) LookupOutcomes::Add(stamped ? LookupOutcomes::RefreshUnchanged : LookupOutcomes::RefreshCompared, start); return true; } @@ -2389,7 +2434,7 @@ void traceKeyStore(const char* path, const GuestTextureResource& descriptor, std void StorageTexture::captureGuestBytes(const std::vector* layers) { original.resize(static_cast(guestBytes)); - comparedGuestBytes = {}; + comparedRanges = {}; std::vector selected(trackedLayers, true); if (layers != nullptr) selected = *layers; for (const auto& [begin, end] : unitRuns(selected)) GuestMemory::FlushGpuWrites(descriptor.baseAddress + begin, static_cast(end - begin)); @@ -2426,11 +2471,10 @@ bool StorageTexture::compareUntracked(std::uint64_t address, std::size_t bytes, Require(changed.size() == (end - 1) / blockBytes - first + 1, "texture comparison block count differs"); const auto* recorder = Recorder::Active(); const auto epoch = GuestMemory::CollectEpoch(); - const bool whole = address == descriptor.baseAddress && bytes == guestBytes; - const auto hint = memoize && epoch != 0 && whole ? GuestMemory::UntrackedHint(address, bytes) : 0; + const auto hint = memoize && epoch != 0 ? GuestMemory::UntrackedHint(address, bytes) : 0; const std::array stamp{hint != 0, hint != 0 ? hint : epoch, GuestMemory::NewestStamp(address, bytes), GuestMemory::ForgetSerial(), recorder != nullptr ? recorder->NewestWriteNote(address, bytes) : 0}; - const bool cacheable = memoize && epoch != 0 && (stamp[3] & 1u) == 0 && whole; - if (cacheable && comparedGuestBytes == stamp) { + const bool cacheable = memoize && epoch != 0 && (stamp[3] & 1u) == 0; + if (cacheable && std::any_of(comparedRanges.begin(), comparedRanges.end(), [&](const ComparedRange& entry) { return entry.address == address && entry.bytes == bytes && entry.stamp == stamp; })) { std::fill(changed.begin(), changed.end(), GuestMemory::BlockUnchanged); return true; } @@ -2438,10 +2482,14 @@ bool StorageTexture::compareUntracked(std::uint64_t address, std::size_t bytes, const auto saved = std::span(original).subspan(static_cast(address - descriptor.baseAddress), bytes); if (GuestMemory::EqualsCommittedUnsynced(address, saved)) { std::fill(changed.begin(), changed.end(), GuestMemory::BlockUnchanged); - if (cacheable) comparedGuestBytes = stamp; + if (cacheable) { + auto found = std::find_if(comparedRanges.begin(), comparedRanges.end(), [&](const ComparedRange& entry) { return entry.address == address && entry.bytes == bytes; }); + if (found == comparedRanges.end()) found = comparedRanges.begin() + nextComparedRange++ % comparedRanges.size(); + *found = {address, bytes, stamp}; + } return true; } - comparedGuestBytes = {}; + comparedRanges = {}; for (std::size_t index = 0; index < changed.size(); ++index) { const auto begin = std::max(address, (first + index) * blockBytes); const auto stop = std::min(end, (first + index + 1) * blockBytes); @@ -3541,6 +3589,44 @@ void StorageTexture::refreshGeneration() { generation = *std::min_element(layerGeneration.begin(), layerGeneration.end()); } +std::uint64_t StorageTexture::GenerationFor(std::uint64_t address, std::uint64_t bytes) const { + if (layerBehind.empty()) return generation; + std::optional oldest; + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { + const auto begin = layerBegin(layer); + if (address >= begin + layerBytes(layer) || begin >= address + bytes) continue; + if (layerBehind[layer]) return 0; + oldest = std::min(oldest.value_or(layerGeneration[layer]), layerGeneration[layer]); + } + return oldest.value_or(generation); +} + +void StorageTexture::noteLayersBehind(std::uint32_t firstLayer, std::uint32_t lastLayer, const std::vector& outside) { + if (layerBehind.size() != trackedLayers) layerBehind.assign(trackedLayers, false); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { + if (layer >= firstLayer && layer < lastLayer) layerBehind[layer] = false; + else if (layer < outside.size() && outside[layer]) layerBehind[layer] = true; + } + const auto layersEnd = layerBegin(lastLayer - 1) + layerBytes(lastLayer - 1); + const std::array, 2> ranges{{{descriptor.baseAddress, layerBegin(firstLayer)}, {layersEnd, descriptor.baseAddress + guestBytes}}}; + for (const auto& [from, to] : ranges) { + if (to <= from) continue; + for (const auto& other : overlappingPending(from, static_cast(to - from))) { + const bool whole = other->descriptor.dccAddress != 0 && other->uploadedKeys != DccKeys::Uncompressed; + for (std::uint32_t unit = 0; unit < other->trackedLayers; ++unit) { + if (!whole && !other->layerPending[unit]) continue; + const auto begin = std::max(from, other->layerBegin(unit)); + const auto end = std::min(to, other->layerBegin(unit) + other->layerBytes(unit)); + if (end <= begin) continue; + const auto first = static_cast((begin - descriptor.baseAddress) / trackedLayerBytes); + const auto last = static_cast(std::min((end - descriptor.baseAddress + trackedLayerBytes - 1) / trackedLayerBytes, trackedLayers)); + for (auto layer = first; layer < last; ++layer) layerBehind[layer] = true; + } + } + } + if (std::none_of(layerBehind.begin(), layerBehind.end(), [](bool behind) { return behind; })) layerBehind.clear(); +} + void StorageTexture::markLayersPending(std::uint32_t first, std::uint32_t count) { CaptureTrace::Log("image-write image=%llx first=%u count=%u generation=%llu", static_cast(descriptor.baseAddress), first, count, static_cast(generation)); static const bool eager = std::getenv("APS5_EAGER_WRITEBACK") != nullptr || std::getenv("APS5_NO_TEXTURE_CACHE") != nullptr; @@ -3801,7 +3887,7 @@ void StorageTexture::saveBeforeUnwatch() { const auto runs = unitRuns(units); if (runs.empty()) return; original.resize(static_cast(guestBytes)); - comparedGuestBytes = {}; + comparedRanges = {}; for (const auto& [begin, end] : runs) GuestMemory::ReadCommittedUnsynced(descriptor.baseAddress + begin, std::span(original).subspan(static_cast(begin), static_cast(end - begin))); for (const auto& [begin, end] : runs) GuestMemory::CollectWritesUncached(descriptor.baseAddress + begin, static_cast(end - begin)); constexpr std::uint64_t block = 65536; @@ -3862,7 +3948,7 @@ void StorageTexture::applyStoredSave(std::uint64_t ticket, const std::vector(guestBytes)); - comparedGuestBytes = {}; + comparedRanges = {}; const auto offset = static_cast(layerBegin(unit) - descriptor.baseAddress); std::memcpy(original.data() + offset, stored.data() + offset, static_cast(layerBytes(unit))); layerOriginal[unit] = true; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp index f9e388295e..5d635c3f2c 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp @@ -68,6 +68,7 @@ alignas(256) std::array PixelCode{}; alignas(256) std::array Output{}; void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode); +void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode); std::uint64_t AddressOf(const void* data) { return static_cast(reinterpret_cast(data)); @@ -170,10 +171,10 @@ void Load(AgcDriver::VulkanDevice& device, std::span code, Dispatch(device, code, userData); } -void LoadLayer(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t layer, std::uint32_t swizzleMode) { +void LoadLayer(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t layer, std::uint32_t swizzleMode, std::uint32_t lastLayer = Layers - 1u) { auto& code = LoadCodes[layer]; code = {0x7e020280, 0x7e040280u | layer, 0xf0001128, 0x00010300, 0x34080082, 0xbf8c3f70, 0xe0701000, 0x80000304, 0xbf810000}; - Load(device, code, SurfaceDescriptor(surface, swizzleMode, Type2DArray, Layers - 1u)); + Load(device, code, SurfaceDescriptor(surface, swizzleMode, Type2DArray, lastLayer)); } void Sample2D(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t swizzleMode) { @@ -187,23 +188,23 @@ void RequireOutput(std::uint32_t inside, std::uint32_t outside, const std::strin } } -AgcDriver::Graphics::ColorTarget DecodeTarget(std::uint64_t address, std::uint32_t slice, std::uint32_t swizzleMode, bool collapsed) { +AgcDriver::Graphics::ColorTarget DecodeTarget(std::uint64_t address, std::uint32_t slice, std::uint32_t swizzleMode, bool collapsed, std::uint32_t lastSlice) { AgcDriver::Registers cx; cx[0x318] = static_cast(address >> 8u); cx[0x31b] = slice | (slice << 13u); cx[0x31c] = Color32FloatInfo; cx[0x31d] = 0; cx[0x3b0] = ((Side - 1u) << 14u) | (Side - 1u); - cx[0x3b8] = (TiledAttrib3 & ~(0x1fu << 14u)) | (swizzleMode << 14u) | (collapsed ? 0u : Layers - 1u); + cx[0x3b8] = (TiledAttrib3 & ~(0x1fu << 14u)) | (swizzleMode << 14u) | (collapsed ? 0u : lastSlice); cx[0x390] = static_cast(address >> 40u); const auto color = AgcDriver::Graphics::DecodeColorBuffer(cx, 0); Require(color.format == VK_FORMAT_R32_SFLOAT && color.bytes == LayerBytes && color.address == address + slice * LayerBytes, "the COLOR_32 float view did not decode as a 128x128 R32 slice of the array"); if (collapsed) Require(color.arrayAddress == 0 && color.arrayLastSlice == 0, "a collapsed slice view unexpectedly describes a whole array"); - else Require(color.arrayAddress == address && color.arrayLastSlice == Layers - 1u && color.arraySlice == slice, "the color view did not keep its array and slice"); + else Require(color.arrayAddress == address && color.arrayLastSlice == lastSlice && color.arraySlice == slice, "the color view did not keep its array and slice"); return color; } -void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t slice, std::uint32_t swizzleMode, bool collapsed) { +void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint32_t slice, std::uint32_t swizzleMode, bool collapsed, std::uint32_t lastSlice = Layers - 1u, std::uint32_t width = ScissorWidth) { const auto target = device.Target(); constexpr std::uint32_t waveSize = 64; std::size_t at = 0; @@ -230,7 +231,7 @@ void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint const auto vertexResult = ShaderRecompiler::Recompile(vertex); const auto vertexPush = static_cast(vertexResult.pushConstants.size()); - const auto color = DecodeTarget(surface, slice, swizzleMode, collapsed); + const auto color = DecodeTarget(surface, slice, swizzleMode, collapsed, lastSlice); VkPipelineColorBlendAttachmentState blend{}; blend.colorWriteMask = 0xf; AgcDriver::Graphics::State state{}; @@ -242,7 +243,7 @@ void DrawSlice(AgcDriver::VulkanDevice& device, std::uint64_t surface, std::uint state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; state.viewport = {0, static_cast(Side), static_cast(Side), -static_cast(Side), 0, 1}; state.negativeOneToOne = false; - state.scissor = {{0, 0}, {ScissorWidth, Side}}; + state.scissor = {{0, 0}, {width, Side}}; state.cullMode = VK_CULL_MODE_NONE; state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; state.blends = {blend}; @@ -296,6 +297,7 @@ void Run(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t sw RequireOutput(drawn, Initial, "slice " + std::to_string(slice) + " uploaded again from guest memory"); AgcDriver::Graphics::ClearCachedTextures(device.Device()); SampleSliceZero(device, texels, swizzleMode); + OverlappingArrays(device, texels, swizzleMode); } void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) { @@ -354,6 +356,35 @@ void RetiredArrayLayer(AgcDriver::VulkanDevice& device) { Require(untouched, "CPU write-back stored results over a retired allocation"); } +void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) { + const auto surface = AddressOf(texels); + const auto next = surface + LayerBytes; + const auto drawn = std::bit_cast(Drawn); + std::fill_n(reinterpret_cast(texels), SurfaceBytes / 4u, Initial); + AgcDriver::GuestMemory::CollectWrites(surface, SurfaceBytes); + AgcDriver::GuestMemory::BumpCollectEpoch(); + DrawSlice(device, surface, 0, swizzleMode, false, 1u); + DrawSlice(device, next, 0, swizzleMode, false, 1u, Side); + const auto neighbour = StorageTexture::FindPending(next, 2u * LayerBytes); + Require(neighbour != nullptr && neighbour->Descriptor().baseAddress == next && neighbour->Descriptor().dimension == TextureDimension::k2DArray, "the next target did not render into the image of its own array"); + DrawSlice(device, surface, 0, swizzleMode, false, 1u); + Require(StorageTexture::FindPending(next, 2u * LayerBytes) == neighbour, "a draw into slice 0 of the first array stored the next target's results, which only its unused second slice overlaps"); + Sample2D(device, surface, swizzleMode); + RequireOutput(drawn, Initial, "slice 0 of the first array sampled as a 2D texture after both draws"); + Require(StorageTexture::FindPending(next, 2u * LayerBytes) == neighbour, "a 2D texture over slice 0 of the first array stored the next target's results, which only its unused second slice overlaps"); + LoadLayer(device, next, 0, swizzleMode, 1u); + RequireOutput(drawn, drawn, "slice 0 of the next array after the draw into the first"); + LoadLayer(device, surface, 1, swizzleMode, 1u); + RequireOutput(drawn, drawn, "slice 1 of the first array (the next target's slice 0) read while the next target's results were pending"); + LoadLayer(device, surface, 0, swizzleMode, 1u); + RequireOutput(drawn, Initial, "slice 0 of the first array after both draws"); + LoadLayer(device, next, 1, swizzleMode, 1u); + RequireOutput(Initial, Initial, "slice 1 of the next array, which nothing drew"); + StorageTexture::FlushPending(surface, SurfaceBytes, nullptr, "test"); + device.WaitIdle(); + AgcDriver::Graphics::ClearCachedTextures(device.Device()); +} + } int main() { From 2d677974d144f45504b249744879b441298e2cd6 Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Thu, 15 Oct 2026 18:12:28 +0400 Subject: [PATCH 058/523] test(agc): cover CPU refresh of overlapping array targets --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 7 +++++++ .../tests/execution/ArrayLayerTarget.cpp | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 83d79012a9..8a6d7127d1 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -2504,6 +2504,13 @@ if(BUILD_TESTING) endif() endforeach() endforeach() + add_test(NAME agc_driver_array_layer_target_cpu COMMAND agc_driver_array_layer_target_tests) + set_tests_properties(agc_driver_array_layer_target_cpu PROPERTIES + ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_HOST_IMPORT=1" TIMEOUT 30 SKIP_RETURN_CODE 77) + if(WIN32) + set_tests_properties(agc_driver_array_layer_target_cpu PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() foreach(preparedMode IN ITEMS indirect uncached debug debug_failure deferred deferred_undecodable registered_workgroup registered_workgroup_sync abi_deferred_compute abi_deferred_pixel abi_deferred_stages abi_deferred_rectangle abi_deferred_undecodable_state abi_deferred_registration_failure abi_deferred_vertex_inputs abi_deferred_rectangle_pairs lazy lazy_sync) set(preparedTest "agc_driver_prepared_shaders_${preparedMode}") if(preparedMode MATCHES "^abi_deferred_") diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp index 5d635c3f2c..d43fd89626 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp @@ -68,7 +68,7 @@ alignas(256) std::array PixelCode{}; alignas(256) std::array Output{}; void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode); -void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode); +void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode, bool cpuWrite = false); std::uint64_t AddressOf(const void* data) { return static_cast(reinterpret_cast(data)); @@ -298,6 +298,7 @@ void Run(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t sw AgcDriver::Graphics::ClearCachedTextures(device.Device()); SampleSliceZero(device, texels, swizzleMode); OverlappingArrays(device, texels, swizzleMode); + OverlappingArrays(device, texels, swizzleMode, true); } void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) { @@ -356,7 +357,7 @@ void RetiredArrayLayer(AgcDriver::VulkanDevice& device) { Require(untouched, "CPU write-back stored results over a retired allocation"); } -void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) { +void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode, bool cpuWrite) { const auto surface = AddressOf(texels); const auto next = surface + LayerBytes; const auto drawn = std::bit_cast(Drawn); @@ -367,6 +368,11 @@ void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, st DrawSlice(device, next, 0, swizzleMode, false, 1u, Side); const auto neighbour = StorageTexture::FindPending(next, 2u * LayerBytes); Require(neighbour != nullptr && neighbour->Descriptor().baseAddress == next && neighbour->Descriptor().dimension == TextureDimension::k2DArray, "the next target did not render into the image of its own array"); + if (cpuWrite) { + *reinterpret_cast(texels) = std::bit_cast(0.125f); + AgcDriver::GuestMemory::CollectWrites(surface, LayerBytes); + AgcDriver::GuestMemory::BumpCollectEpoch(); + } DrawSlice(device, surface, 0, swizzleMode, false, 1u); Require(StorageTexture::FindPending(next, 2u * LayerBytes) == neighbour, "a draw into slice 0 of the first array stored the next target's results, which only its unused second slice overlaps"); Sample2D(device, surface, swizzleMode); From c25566ca6da65b82e6f6f272df2d2de9c645a35c Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Thu, 15 Oct 2026 18:30:56 +0400 Subject: [PATCH 059/523] perf(agc): upload selected array layers through CPU staging --- .../Graphics/include/Texture.hpp | 3 +- .../libSceAgcDriver/Graphics/src/Texture.cpp | 79 +++++++++++++++---- .../tests/execution/ArrayLayerTarget.cpp | 34 +++++++- 3 files changed, 94 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp index 70a910562f..abd019bf76 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp @@ -378,9 +378,8 @@ private: StorageTexture(const Context& context, TextureDetiler& detiler, const GuestTextureResource& descriptor, std::uint32_t mipLevel); // The regions of every array layer, or of the tracked layers `layers` selects. std::vector CopyRegions(const std::vector* layers = nullptr) const; - // Uploads the surface, or only the tracked layers `layers` selects (the direct path; the others - // upload everything). void upload(const std::vector* layers = nullptr); + std::vector cpuUploadLayers(const std::vector& units) const; void captureGuestBytes(const std::vector* layers); bool originalHolds(std::uint64_t address, std::size_t bytes) const; bool compareUntracked(std::uint64_t address, std::size_t bytes, std::span changed, bool memoize = false) const; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp index 2168c4a4b2..b109717dbf 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp @@ -2196,8 +2196,6 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) stamped = false; if (!scoped) outside.clear(); } - // Only the direct path uploads the selected layers alone (see upload); the others replace - // the whole image, so every pending layer's results are stored first. direct = keys == DccKeys::Uncompressed && HostImportFor(context, descriptor.baseAddress, static_cast(guestBytes)) != nullptr; if (alias != nullptr && direct) { // The alias's pending units that nothing stamped since its generation (a CPU store or a @@ -2281,6 +2279,7 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) } std::fprintf(stderr, "[dcc-keys] refresh 0x%llx+0x%llx keys %s -> %s (dirty %d, %zu pending units, %zu cpu-stamped units%s)\n", static_cast(descriptor.baseAddress), static_cast(guestBytes), DccKeysName(uploadedKeys), DccKeysName(keys), dirty ? 1 : 0, pendingUnits, stampedUnits, keyFlip ? ", flip keeps unstamped results" : ""); } + const bool selectedCpu = !direct && keys == DccKeys::Uncompressed && !cpuUploadLayers(changed).empty(); for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { if (!layerPending[layer]) continue; if (keyFlip && layerGeneration[layer] != 0 && stampedBlocks[layer] != GuestMemory::BlockWritten) { @@ -2299,7 +2298,7 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) dropped = true; continue; } - } else if (direct) { + } else if (direct || selectedCpu) { continue; } stored[layer] = true; @@ -2501,6 +2500,25 @@ bool StorageTexture::compareUntracked(std::uint64_t address, std::size_t bytes, constexpr std::uint64_t GenerationBaselineBytes = 1ull << 20; +std::vector StorageTexture::cpuUploadLayers(const std::vector& units) const { + if (descriptor.dimension != TextureDimension::k2DArray || descriptor.mipCount != 1 || descriptor.dccAddress != 0 || IsBlockCompressed(descriptor.format) || geometry.thick || arrayLayers <= 1 || geometry.imageLayers != arrayLayers || trackedLayerBytes == 0 || units.size() != trackedLayers) return {}; + std::vector selected(arrayLayers, false); + for (std::uint32_t layer = 0; layer < arrayLayers; ++layer) { + const auto begin = geometry.GuestLayerOffset(layer); + const auto end = begin + geometry.layerBytes; + if (end <= begin || end > guestBytes || begin % trackedLayerBytes != 0 || (end % trackedLayerBytes != 0 && end != guestBytes)) return {}; + const auto first = static_cast(begin / trackedLayerBytes); + const auto last = static_cast((end + trackedLayerBytes - 1) / trackedLayerBytes); + if (first >= last || last > trackedLayers) return {}; + selected[layer] = units[first]; + for (auto unit = first + 1; unit < last; ++unit) { + if (units[unit] != selected[layer]) return {}; + } + } + if (std::none_of(selected.begin(), selected.end(), [](bool value) { return value; }) || std::all_of(selected.begin(), selected.end(), [](bool value) { return value; })) return {}; + return selected; +} + void StorageTexture::upload(const std::vector* layers) { const bool profile = LookupOutcomes::Profiled(); const auto start = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; @@ -2509,8 +2527,6 @@ void StorageTexture::upload(const std::vector* layers) { CaptureTrace::Log("upload image=%llx bytes=%llu generation=%llu reason=%s partial=%d", static_cast(descriptor.baseAddress), static_cast(guestBytes), static_cast(generation), uploadReason, layers != nullptr); const auto elementBytes = BytesPerElement(descriptor.format); const auto linearBytes = sliceLinearBytes * arrayLayers; - // A layer selection applies to the direct path alone (the others upload everything); it names - // array layers, which the tracked layers are when there are several. if (layers != nullptr && ((!blockUnits && trackedLayers != arrayLayers) || std::all_of(layers->begin(), layers->end(), [](bool selected) { return selected; }))) layers = nullptr; const auto now = GuestMemory::CollectWrites(descriptor.baseAddress, static_cast(guestBytes)); const auto stampLayers = [&](bool selectedOnly) { @@ -2717,15 +2733,36 @@ void StorageTexture::upload(const std::vector* layers) { if (profile) LookupOutcomes::Add(LookupOutcomes::UploadDirect, start); return; } - layerOriginal.assign(trackedLayers, true); - cancelStoredSaves(); - forgetBorrowed(0, trackedLayers); - stampLayers(false); - original.resize(static_cast(guestBytes)); - GuestMemory::ReadCommitted(descriptor.baseAddress, original); + const auto cpuLayers = uploadedKeys == DccKeys::Uncompressed && layers != nullptr ? cpuUploadLayers(*layers) : std::vector{}; + const bool selectedCpu = !cpuLayers.empty(); + std::uint64_t uploadedBytes = guestBytes; + if (selectedCpu) { + captureGuestBytes(layers); + for (std::uint32_t unit = 0; unit < trackedLayers; ++unit) { + if ((*layers)[unit]) forgetBorrowed(unit, 1); + } + stampLayers(true); + uploadedBytes = 0; + for (std::uint32_t layer = 0; layer < arrayLayers; ++layer) { + if (cpuLayers[layer]) uploadedBytes += geometry.layerBytes; + } + } else { + layerOriginal.assign(trackedLayers, true); + cancelStoredSaves(); + forgetBorrowed(0, trackedLayers); + stampLayers(false); + original.resize(static_cast(guestBytes)); + GuestMemory::ReadCommitted(descriptor.baseAddress, original); + } { Buffer staging(context, original.size(), VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - if (uploadedKeys == DccKeys::Uncompressed) std::memcpy(staging.Bytes().data(), original.data(), original.size()); + if (selectedCpu) { + for (std::uint32_t layer = 0; layer < arrayLayers; ++layer) { + if (!cpuLayers[layer]) continue; + const auto offset = geometry.GuestLayerOffset(layer); + std::memcpy(staging.Bytes().data() + offset, original.data() + offset, static_cast(geometry.layerBytes)); + } + } else if (uploadedKeys == DccKeys::Uncompressed) std::memcpy(staging.Bytes().data(), original.data(), original.size()); else ReadTextureSurface(descriptor, uploadedKeys, staging.Bytes().first(original.size())); DeviceBuffer tiled(context, original.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); DeviceBuffer linear(context, static_cast(linearBytes), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); @@ -2733,9 +2770,16 @@ void StorageTexture::upload(const std::vector* layers) { CommandBatch batch(context); const auto commands = batch.Handle(); RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - CopyBuffer(context, commands, staging.Handle(), 0, tiled.Handle(), 0, original.size()); + if (selectedCpu) { + for (std::uint32_t layer = 0; layer < arrayLayers; ++layer) { + if (!cpuLayers[layer]) continue; + const auto offset = geometry.GuestLayerOffset(layer); + CopyBuffer(context, commands, staging.Handle(), offset, tiled.Handle(), offset, geometry.layerBytes); + } + } else CopyBuffer(context, commands, staging.Handle(), 0, tiled.Handle(), 0, original.size()); RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); for (std::uint32_t layer = 0; layer < arrayLayers; ++layer) { + if (selectedCpu && !cpuLayers[layer]) continue; for (std::uint32_t level = 0; level < mips.size(); ++level) { if (!geometry.HasLayer(level, layer)) continue; const auto& mip = mips[level]; @@ -2744,15 +2788,16 @@ void StorageTexture::upload(const std::vector* layers) { } const auto linearRead = WholeBufferBarrier(linear.Handle(), VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); VkImageMemoryBarrier toTransfer{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + toTransfer.srcAccessMask = selectedCpu ? VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT : 0u; toTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - toTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + toTransfer.oldLayout = selectedCpu ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED; toTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; toTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; toTransfer.image = image; toTransfer.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, descriptor.mipCount, 0, geometry.imageLayers}; - context.Function("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &linearRead, 1, &toTransfer); - const auto regions = CopyRegions(); + context.Function("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | (selectedCpu ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT : 0u), VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &linearRead, 1, &toTransfer); + const auto regions = CopyRegions(selectedCpu ? &cpuLayers : nullptr); context.Function("vkCmdCopyBufferToImage")(commands, linear.Handle(), image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast(regions.size()), regions.data()); VkImageMemoryBarrier toGeneral = toTransfer; toGeneral.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; @@ -2764,7 +2809,7 @@ void StorageTexture::upload(const std::vector* layers) { batch.SubmitAndWait(); APS5_LOG_CHARS_OUT("StorageTexture upload done"); } - countStorageUpload(2, guestBytes); + countStorageUpload(2, uploadedBytes); ++version; if (profile) LookupOutcomes::Add(LookupOutcomes::UploadCpu, start); } diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp index d43fd89626..5a578e1a4c 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp @@ -69,6 +69,7 @@ alignas(256) std::array Output{}; void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode); void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode, bool cpuWrite = false); +void OverlappingArrayLastSlice(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode); std::uint64_t AddressOf(const void* data) { return static_cast(reinterpret_cast(data)); @@ -299,6 +300,7 @@ void Run(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t sw SampleSliceZero(device, texels, swizzleMode); OverlappingArrays(device, texels, swizzleMode); OverlappingArrays(device, texels, swizzleMode, true); + OverlappingArrayLastSlice(device, texels, swizzleMode); } void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) { @@ -318,6 +320,32 @@ void SampleSliceZero(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std: AgcDriver::Graphics::ClearCachedTextures(device.Device()); } +void OverlappingArrayLastSlice(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode) { + const auto neighbourAddress = AddressOf(texels); + const auto surface = neighbourAddress + LayerBytes; + const auto cpuValue = std::bit_cast(0.125f); + const auto drawn = std::bit_cast(Drawn); + std::fill_n(reinterpret_cast(texels), SurfaceBytes / sizeof(std::uint32_t), Initial); + AgcDriver::GuestMemory::CollectWrites(neighbourAddress, SurfaceBytes); + AgcDriver::GuestMemory::BumpCollectEpoch(); + DrawSlice(device, surface, 1u, swizzleMode, false, 1u); + DrawSlice(device, neighbourAddress, 1u, swizzleMode, false, 1u, Side); + const auto neighbour = StorageTexture::FindPending(neighbourAddress, 2u * LayerBytes); + Require(neighbour != nullptr && neighbour->Descriptor().baseAddress == neighbourAddress, "the preceding target has no pending results"); + std::fill_n(reinterpret_cast(texels + 2u * LayerBytes), LayerBytes / sizeof(std::uint32_t), cpuValue); + AgcDriver::GuestMemory::CollectWrites(surface + LayerBytes, LayerBytes); + AgcDriver::GuestMemory::BumpCollectEpoch(); + DrawSlice(device, surface, 1u, swizzleMode, false, 1u); + Require(StorageTexture::FindPending(neighbourAddress, 2u * LayerBytes) == neighbour, "CPU refresh of the last slice stored the preceding target's results"); + LoadLayer(device, surface, 1u, swizzleMode, 1u); + RequireOutput(drawn, cpuValue, "the refreshed last slice keeps the CPU's untouched pixels"); + LoadLayer(device, surface, 0u, swizzleMode, 1u); + RequireOutput(drawn, drawn, "the first slice sees the preceding target's pending results"); + StorageTexture::FlushPending(neighbourAddress, SurfaceBytes, nullptr, "test"); + device.WaitIdle(); + AgcDriver::Graphics::ClearCachedTextures(device.Device()); +} + void UnregisteredUnusedLayer(AgcDriver::VulkanDevice& device, std::uint32_t slice) { GuestBlock block(LayerBytes, slice * LayerBytes); auto* texels = block.Data(); @@ -369,21 +397,21 @@ void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, st const auto neighbour = StorageTexture::FindPending(next, 2u * LayerBytes); Require(neighbour != nullptr && neighbour->Descriptor().baseAddress == next && neighbour->Descriptor().dimension == TextureDimension::k2DArray, "the next target did not render into the image of its own array"); if (cpuWrite) { - *reinterpret_cast(texels) = std::bit_cast(0.125f); + std::fill_n(reinterpret_cast(texels), LayerBytes / sizeof(std::uint32_t), std::bit_cast(0.125f)); AgcDriver::GuestMemory::CollectWrites(surface, LayerBytes); AgcDriver::GuestMemory::BumpCollectEpoch(); } DrawSlice(device, surface, 0, swizzleMode, false, 1u); Require(StorageTexture::FindPending(next, 2u * LayerBytes) == neighbour, "a draw into slice 0 of the first array stored the next target's results, which only its unused second slice overlaps"); Sample2D(device, surface, swizzleMode); - RequireOutput(drawn, Initial, "slice 0 of the first array sampled as a 2D texture after both draws"); + RequireOutput(drawn, cpuWrite ? std::bit_cast(0.125f) : Initial, "slice 0 of the first array sampled as a 2D texture after both draws"); Require(StorageTexture::FindPending(next, 2u * LayerBytes) == neighbour, "a 2D texture over slice 0 of the first array stored the next target's results, which only its unused second slice overlaps"); LoadLayer(device, next, 0, swizzleMode, 1u); RequireOutput(drawn, drawn, "slice 0 of the next array after the draw into the first"); LoadLayer(device, surface, 1, swizzleMode, 1u); RequireOutput(drawn, drawn, "slice 1 of the first array (the next target's slice 0) read while the next target's results were pending"); LoadLayer(device, surface, 0, swizzleMode, 1u); - RequireOutput(drawn, Initial, "slice 0 of the first array after both draws"); + RequireOutput(drawn, cpuWrite ? std::bit_cast(0.125f) : Initial, "slice 0 of the first array after both draws"); LoadLayer(device, next, 1, swizzleMode, 1u); RequireOutput(Initial, Initial, "slice 1 of the next array, which nothing drew"); StorageTexture::FlushPending(surface, SurfaceBytes, nullptr, "test"); From de0cf03efe0333d62f3aa6dbfd2b58a8d930aedf Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Fri, 16 Oct 2026 01:32:36 +0400 Subject: [PATCH 060/523] test(agc): cover array slices beside uncommitted memory --- .../tests/execution/ArrayLayerTarget.cpp | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp index 5a578e1a4c..18a3373738 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp @@ -83,22 +83,24 @@ std::string Hex(std::uint32_t value) { class GuestBlock { public: - explicit GuestBlock(std::size_t registeredBytes = SurfaceBytes, std::size_t registeredOffset = 0) : registeredOffset(registeredOffset) { + explicit GuestBlock(std::size_t registeredBytes = SurfaceBytes, std::size_t registeredOffset = 0, bool commitAll = true) : registeredOffset(registeredOffset) { #ifdef _WIN32 block = static_cast(GuestArena::GuestArenaAllocate_nid_postfix(SurfaceBytes, LayerBytes)); - if (block != nullptr) GuestArena::GuestArenaCommit_nid_postfix(block, SurfaceBytes, PAGE_READWRITE, SurfaceBytes); + if (block != nullptr) GuestArena::GuestArenaCommit_nid_postfix(block + (commitAll ? 0 : registeredOffset), commitAll ? SurfaceBytes : registeredBytes, PAGE_READWRITE, commitAll ? SurfaceBytes : registeredBytes); #else - void* mapped = mmap(nullptr, SurfaceBytes + LayerBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + void* mapped = mmap(nullptr, SurfaceBytes + LayerBytes, commitAll ? PROT_READ | PROT_WRITE : PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mapped != MAP_FAILED) { const auto begin = reinterpret_cast(mapped); const auto aligned = (begin + LayerBytes - 1) & ~(static_cast(LayerBytes) - 1); if (aligned != begin) munmap(mapped, aligned - begin); if (aligned + SurfaceBytes != begin + SurfaceBytes + LayerBytes) munmap(reinterpret_cast(aligned + SurfaceBytes), begin + LayerBytes - aligned); block = reinterpret_cast(aligned); - if (AgcDriver::GuestMemory::WriteWatched()) GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block, SurfaceBytes); + if (!commitAll) Require(mprotect(block + registeredOffset, registeredBytes, PROT_READ | PROT_WRITE) == 0, "cannot commit the active array slice"); + if (AgcDriver::GuestMemory::WriteWatched()) GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block + (commitAll ? 0 : registeredOffset), commitAll ? SurfaceBytes : registeredBytes); } #endif Require(block != nullptr, "array layer target: cannot allocate the guest block"); + if (!commitAll) GuestAllocations::GuestAllocationsInvalidate_nid_postfix(reinterpret_cast(block), SurfaceBytes); GuestAllocations::Mutation().Add(block + registeredOffset, registeredBytes, true, true, true); } @@ -385,6 +387,39 @@ void RetiredArrayLayer(AgcDriver::VulkanDevice& device) { Require(untouched, "CPU write-back stored results over a retired allocation"); } +void UncommittedUnusedLayers(AgcDriver::VulkanDevice& device, std::uint32_t slice) { + GuestBlock block(LayerBytes, slice * LayerBytes, false); + const auto surface = AddressOf(block.Data()); + const auto active = surface + slice * LayerBytes; + auto* texels = reinterpret_cast(active); + std::fill_n(texels, LayerBytes / sizeof(std::uint32_t), Initial); + Require(AgcDriver::GuestMemory::CollectWrites(active, LayerBytes) != 0, "the committed array slice has no write history"); + Require(AgcDriver::GuestMemory::CollectWrites(surface, SurfaceBytes) == 0, "the unused array slices unexpectedly have complete write history"); + AgcDriver::GuestMemory::BumpCollectEpoch(); + DrawSlice(device, surface, slice, TileR64KBX, false); + const auto pending = StorageTexture::FindPending(surface, SurfaceBytes); + Require(pending != nullptr, "the committed array slice has no pending draw results"); + Require(pending->GenerationFor(active, LayerBytes) != 0, "an uncommitted unused slice erased the active slice's write history"); + const auto version = pending->Version(); + for (unsigned repeat = 0; repeat < 3; ++repeat) { + AgcDriver::GuestMemory::BumpCollectEpoch(); + Require(pending->RefreshLayers(slice, 1), "an unchanged committed slice was uploaded again"); + Require(pending->GenerationFor(active, LayerBytes) != 0 && pending->Version() == version, "repeated refresh erased the committed slice's write history"); + Require(StorageTexture::FindPending(surface, SurfaceBytes) == pending, "refresh settled unchanged GPU results for an uncommitted unused slice"); + } + const auto cpu = std::bit_cast(0.125f); + std::fill_n(texels, LayerBytes / sizeof(std::uint32_t), cpu); + AgcDriver::GuestMemory::BumpCollectEpoch(); + Require(!pending->RefreshLayers(slice, 1), "the active array slice ignored a genuine CPU write"); + LoadLayer(device, surface, slice, TileR64KBX); + RequireOutput(cpu, cpu, "a committed array slice after a genuine CPU write"); + DrawSlice(device, surface, slice, TileR64KBX, false); + StorageTexture::FlushPending(active, LayerBytes, nullptr, "test"); + device.WaitIdle(); + Require(std::count(texels, texels + LayerBytes / sizeof(std::uint32_t), std::bit_cast(Drawn)) == ScissorWidth * Side, "write-back lost results in the committed array slice"); + AgcDriver::Graphics::ClearCachedTextures(device.Device()); +} + void OverlappingArrays(AgcDriver::VulkanDevice& device, std::uint8_t* texels, std::uint32_t swizzleMode, bool cpuWrite) { const auto surface = AddressOf(texels); const auto next = surface + LayerBytes; @@ -432,6 +467,8 @@ int main() { UnregisteredUnusedLayer(*device, 0u); UnregisteredUnusedLayer(*device, Layers - 1u); RetiredArrayLayer(*device); + UncommittedUnusedLayers(*device, 0u); + UncommittedUnusedLayers(*device, Layers - 1u); std::puts("array layer target tests passed"); return 0; } catch (const std::exception& error) { From 2421f6cfa00ec25a14c673bd54aac7f2244ce1bc Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Fri, 16 Oct 2026 01:41:02 +0400 Subject: [PATCH 061/523] fix(agc): retain write history for committed texture units --- .../Graphics/include/Texture.hpp | 1 + .../libSceAgcDriver/Graphics/src/Texture.cpp | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp index abd019bf76..4227e5e16f 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp @@ -477,6 +477,7 @@ private: bool refresh(std::uint32_t firstLayer, std::uint32_t lastLayer); void noteLayersBehind(std::uint32_t firstLayer, std::uint32_t lastLayer, const std::vector& outside); void refreshGeneration(); + std::uint64_t collectedLayerGeneration(std::uint32_t layer, std::uint64_t whole, bool uncached = false) const; bool refreshProved(); void takeRefreshProof(bool aliased); bool otherPendingOverlaps() const; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp index b109717dbf..ea981b6529 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp @@ -2084,6 +2084,16 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) // guest memory and the DCC keys still match, the image content is current. Pages nobody wrote // since `generation` need no comparison. const auto current = GuestMemory::CollectWrites(descriptor.baseAddress, static_cast(guestBytes)); + const auto scopedCurrent = current != 0 || !scoped ? current : GuestMemory::CollectWrites(scopeBegin, scopeBytes); + std::vector collected; + if (scopedCurrent == 0) { + collected.resize(trackedLayers); + for (auto layer = firstLayer; layer < lastLayer; ++layer) collected[layer] = collectedLayerGeneration(layer, 0); + } + const auto currentFor = [&](std::uint32_t layer) { + if (scoped && (layer < firstLayer || layer >= lastLayer)) return current; + return scopedCurrent != 0 ? scopedCurrent : collected[layer]; + }; // Per tracked layer: changed since its generation (a keys change makes every layer stale). std::vector changed(trackedLayers, false); // Per 64 KiB block of the surface (blockGenerations): stamped since its layer's generation, and @@ -2146,7 +2156,7 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) scanBlocks(); for (std::uint32_t unit = 0; unit < trackedLayers; ++unit) { if (stampedBlocks[unit] == 0) { - layerGeneration[unit] = current; + layerGeneration[unit] = currentFor(unit); continue; } changed[unit] = true; @@ -2226,7 +2236,7 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) if (unchanged) { ++Profile().storageReused; for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { - if (!outsideChanged || !outside[layer]) layerGeneration[layer] = current; + if (!outsideChanged || !outside[layer]) layerGeneration[layer] = currentFor(layer); } refreshGeneration(); if (layerBehind.empty()) takeRefreshProof(alias != nullptr); @@ -2320,7 +2330,7 @@ bool StorageTexture::refresh(std::uint32_t firstLayer, std::uint32_t lastLayer) uploadReason = "alias"; borrowUnits(*alias, borrowed); for (std::uint32_t unit = 0; unit < trackedLayers; ++unit) { - if (borrowed[unit]) layerGeneration[unit] = current; + if (borrowed[unit]) layerGeneration[unit] = currentFor(unit); } refreshGeneration(); } @@ -2538,7 +2548,7 @@ void StorageTexture::upload(const std::vector* layers) { } for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { if (!whole && !(*layers)[layer]) continue; - layerGeneration[layer] = now; + layerGeneration[layer] = collectedLayerGeneration(layer, now); if (whole || generationBaseline.size() != guestBytes) continue; const auto offset = static_cast(layerBegin(layer) - descriptor.baseAddress); GuestMemory::ReadCommitted(layerBegin(layer), std::span(generationBaseline).subspan(offset, static_cast(layerBytes(layer)))); @@ -3635,17 +3645,24 @@ void StorageTexture::refreshGeneration() { } std::uint64_t StorageTexture::GenerationFor(std::uint64_t address, std::uint64_t bytes) const { - if (layerBehind.empty()) return generation; + if (layerBehind.empty() && generation != 0) return generation; std::optional oldest; for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { const auto begin = layerBegin(layer); if (address >= begin + layerBytes(layer) || begin >= address + bytes) continue; - if (layerBehind[layer]) return 0; + if (!layerBehind.empty() && layerBehind[layer]) return 0; oldest = std::min(oldest.value_or(layerGeneration[layer]), layerGeneration[layer]); } return oldest.value_or(generation); } +std::uint64_t StorageTexture::collectedLayerGeneration(std::uint32_t layer, std::uint64_t whole, bool uncached) const { + if (whole != 0 || trackedLayers <= 1) return whole; + const auto begin = layerBegin(layer); + const auto bytes = static_cast(layerBytes(layer)); + return uncached ? GuestMemory::CollectWritesUncached(begin, bytes) : GuestMemory::CollectWrites(begin, bytes); +} + void StorageTexture::noteLayersBehind(std::uint32_t firstLayer, std::uint32_t lastLayer, const std::vector& outside) { if (layerBehind.size() != trackedLayers) layerBehind.assign(trackedLayers, false); for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { @@ -4630,7 +4647,8 @@ bool StorageTexture::clearByKeysFill(DccKeys keys, std::uint8_t key) { filledKeys = DccKeys::Uncompressed; keyProof = {}; forgetBorrowed(0, trackedLayers); - layerGeneration.assign(trackedLayers, GuestMemory::CollectWrites(descriptor.baseAddress, static_cast(guestBytes))); + const auto now = GuestMemory::CollectWrites(descriptor.baseAddress, static_cast(guestBytes)); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) layerGeneration[layer] = collectedLayerGeneration(layer, now); refreshGeneration(); ++version; if (traceKeys) std::fprintf(stderr, "[dcc-keys] key fill %s clears 0x%llx+0x%llx at once (keys 0x%llx, %zu pending units dropped, dirty %d)\n", DccKeysName(keys), static_cast(descriptor.baseAddress), static_cast(guestBytes), static_cast(descriptor.dccAddress), droppedUnits, wasDirty ? 1 : 0); @@ -4791,7 +4809,8 @@ bool StorageTexture::FillClear(std::span pattern, std::u return true; } if (!watched) captureGuestBytes(nullptr); - layerGeneration.assign(trackedLayers, GuestMemory::CollectWritesUncached(descriptor.baseAddress, static_cast(guestBytes))); + const auto now = GuestMemory::CollectWritesUncached(descriptor.baseAddress, static_cast(guestBytes)); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) layerGeneration[layer] = collectedLayerGeneration(layer, now, true); refreshGeneration(); forgetBorrowed(0, trackedLayers); MarkDirty(); @@ -4904,7 +4923,8 @@ bool StorageTexture::CopyFrom(StorageTexture& source, const char*& refusal) { captureGuestBytes(nullptr); } uploadedKeys = DccKeys::Uncompressed; - layerGeneration.assign(trackedLayers, GuestMemory::CollectWritesUncached(descriptor.baseAddress, static_cast(guestBytes))); + const auto now = GuestMemory::CollectWritesUncached(descriptor.baseAddress, static_cast(guestBytes)); + for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) layerGeneration[layer] = collectedLayerGeneration(layer, now, true); refreshGeneration(); forgetBorrowed(0, trackedLayers); MarkDirty(); @@ -5269,7 +5289,7 @@ void StorageTexture::writeBackLayers(const std::vector& layers) { if (!advances && adjacent.empty() && shadowed.empty()) return; const auto now = memoizedCollect ? GuestMemory::CollectWrites(firstStored, static_cast(lastStored - firstStored)) : GuestMemory::CollectWritesUncached(firstStored, static_cast(lastStored - firstStored)); for (std::uint32_t layer = 0; layer < trackedLayers; ++layer) { - if (layers[layer] && !skippedLayer[layer]) layerGeneration[layer] = now; + if (layers[layer] && !skippedLayer[layer]) layerGeneration[layer] = collectedLayerGeneration(layer, now, !memoizedCollect); } refreshGeneration(); advanceAdjacent(adjacent, now, firstBlock, lastBlock); From 5012f736d02792fcbdbaa8cc0fbf6fb644d82371 Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Fri, 16 Oct 2026 02:12:40 +0400 Subject: [PATCH 062/523] test(agc): scope unit history checks to write-watched hosts --- .../prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp index 18a3373738..0d0c8a865e 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp @@ -388,6 +388,7 @@ void RetiredArrayLayer(AgcDriver::VulkanDevice& device) { } void UncommittedUnusedLayers(AgcDriver::VulkanDevice& device, std::uint32_t slice) { + if (!AgcDriver::GuestMemory::WriteWatched()) return; GuestBlock block(LayerBytes, slice * LayerBytes, false); const auto surface = AddressOf(block.Data()); const auto active = surface + slice * LayerBytes; From b8f6d316a96799c06bd4970adfd812c91cd0a0bb Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Sat, 17 Oct 2026 23:40:31 +0400 Subject: [PATCH 063/523] test(agc): expose cached write history across allocation replacement --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 11 +++++++ .../prx/libSceAgcDriver/tests/Recorder.cpp | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 8a6d7127d1..2782537b10 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -1372,6 +1372,17 @@ if(BUILD_TESTING) set_tests_properties(agc_unchanged_cpu_stamps PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + foreach(mode IN ITEMS map ring shared uncached) + add_test(NAME agc_collect_mapping_history_${mode} COMMAND agc_driver_recorder_tests --collect-mapping-history-only) + set_tests_properties(agc_collect_mapping_history_${mode} PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30) + if(WIN32) + set_tests_properties(agc_collect_mapping_history_${mode} PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() + endforeach() + set_tests_properties(agc_collect_mapping_history_ring PROPERTIES ENVIRONMENT "APS5_NO_EPOCH_COLLECT_MAP=1") + set_tests_properties(agc_collect_mapping_history_shared PROPERTIES ENVIRONMENT "APS5_SHARED_COLLECT_MEMO=1") + set_tests_properties(agc_collect_mapping_history_uncached PROPERTIES ENVIRONMENT "APS5_NO_COLLECT_MEMO=1") add_test(NAME agc_storage_kept_unit_settle COMMAND agc_driver_recorder_tests --kept-unit-settle-only) set_tests_properties(agc_storage_kept_unit_settle PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30) if(WIN32) diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 3c749730b7..5edb320d3a 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -117,6 +117,32 @@ void ReleaseWatched(void* block, std::size_t bytes) { #endif } +bool collectMappingHistoryTests() { + constexpr std::size_t bytes = 131072; + auto* memory = static_cast(AllocateWatched(bytes, 65536)); + if (memory == nullptr) return false; + GuestAllocations::Mutation().Add(memory, bytes, true, true, false); + struct Cleanup { + std::uint8_t* memory; + ~Cleanup() { + GuestAllocations::Mutation().Remove(memory); + ReleaseWatched(memory, bytes); + } + } cleanup{memory}; + const auto address = reinterpret_cast(memory); + std::memset(memory, 0x11, bytes); + AgcDriver::GuestMemory::BumpCollectEpoch(); + const auto before = AgcDriver::GuestMemory::CollectWrites(address, bytes); + Require(before != 0, "the mapping fixture has no complete write history"); + memory[4096] = 0x22; + GuestAllocations::Mutation().Remove(memory); + GuestAllocations::Mutation().Add(memory, bytes, true, true, false); + Require(AgcDriver::GuestMemory::CollectWrites(address, bytes) != 0, "the replacement mapping lost write tracking"); + Require(!AgcDriver::GuestMemory::UnchangedSince(address, bytes, before), "a cached collect hid a CPU store across allocation replacement"); + Require(memory[4096] == 0x22, "collecting the replacement mapping changed its CPU bytes"); + return true; +} + std::optional selectDevice(std::span devices, std::string_view request) { const auto rankDeviceType = [](VkPhysicalDeviceType type) { switch (type) { @@ -9641,6 +9667,11 @@ void Run(Context context) { int main(int argc, char** argv) { try { + if (argc == 2 && std::string_view(argv[1]) == "--collect-mapping-history-only") { + if (!collectMappingHistoryTests()) return 77; + std::cout << "collect mapping history tests passed\n"; + return 0; + } if (argc == 2 && std::string_view(argv[1]) == "--device-selection-only") { deviceSelectionTests(); std::cout << "recorder device selection tests passed\n"; From 919e5f64f127fd83ab8134af56190169d4a57458 Mon Sep 17 00:00:00 2001 From: fogydes Date: Mon, 19 Oct 2026 13:22:41 +0530 Subject: [PATCH 064/523] feat(libSceVorbisDec): export missing firmware Vorbis decoder interfaces --- core/libs/CMakeLists.txt | 10 ++ core/libs/prx/libSceVorbisDec/Export.cpp | 178 +++++++++++++++++++++++ core/libs/tests/GuestVorbisDec.cpp | 100 +++++++++++++ docs/dev/debt/libSceVorbisDec.md | 38 +++++ 4 files changed, 326 insertions(+) create mode 100644 core/libs/prx/libSceVorbisDec/Export.cpp create mode 100644 core/libs/tests/GuestVorbisDec.cpp create mode 100644 docs/dev/debt/libSceVorbisDec.md diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 50373d1bf8..24068a4d44 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -656,4 +656,14 @@ if(BUILD_TESTING) set_tests_properties(guest_np_game_intent PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + + add_test_executable(guest_vorbis_dec_tests tests/GuestVorbisDec.cpp) + target_include_directories(guest_vorbis_dec_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_vorbis_dec_tests PRIVATE libSceVorbisDec libc) + configure_windows_unwind(guest_vorbis_dec_tests) + add_test(NAME guest_vorbis_dec COMMAND guest_vorbis_dec_tests) + if(WIN32) + set_tests_properties(guest_vorbis_dec PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() endif() diff --git a/core/libs/prx/libSceVorbisDec/Export.cpp b/core/libs/prx/libSceVorbisDec/Export.cpp new file mode 100644 index 0000000000..44ae4fcc5b --- /dev/null +++ b/core/libs/prx/libSceVorbisDec/Export.cpp @@ -0,0 +1,178 @@ +#include +#include +#include "SceTypes.hpp" +#include "prx/libc/include/General.hpp" + +extern "C" { + +int APS5_VABI sceVorbisDecBitrate(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecBitrateInstant(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecClear(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecComment(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecCrossLap(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecGetDecodePriority(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecGetLastDecodeError(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecHalfrate(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecHalfrateP(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecOpenCallbacks(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecPcmSeek(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecPcmSeekLap(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecPcmSeekPage(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecPcmSeekPageLap(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecPcmTell(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecPcmTotal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecRawSeek(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecRawSeekLap(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecRawTell(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecRawTotal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecRead(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecReadFilter(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecReadFloat(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecSeekable(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecSerialNumber(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecSetDecodePriority(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecStreams(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecTimeSeek(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecTimeSeekLap(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecTimeSeekPage(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecTimeSeekPageLap(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecTimeTell(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVorbisDecTimeTotal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +} diff --git a/core/libs/tests/GuestVorbisDec.cpp b/core/libs/tests/GuestVorbisDec.cpp new file mode 100644 index 0000000000..80bce8c123 --- /dev/null +++ b/core/libs/tests/GuestVorbisDec.cpp @@ -0,0 +1,100 @@ +#include "SceTypes.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" + +#include +#include +#include +#include + +extern "C" { +int APS5_VABI sceVorbisDecOpenCallbacks(void); +int APS5_VABI sceVorbisDecClear(void); +int APS5_VABI sceVorbisDecBitrate(void); +int APS5_VABI sceVorbisDecBitrateInstant(void); +int APS5_VABI sceVorbisDecStreams(void); +int APS5_VABI sceVorbisDecSeekable(void); +int APS5_VABI sceVorbisDecSerialNumber(void); +int APS5_VABI sceVorbisDecRawTotal(void); +int APS5_VABI sceVorbisDecPcmTotal(void); +int APS5_VABI sceVorbisDecTimeTotal(void); +int APS5_VABI sceVorbisDecRawSeek(void); +int APS5_VABI sceVorbisDecPcmSeek(void); +int APS5_VABI sceVorbisDecPcmSeekPage(void); +int APS5_VABI sceVorbisDecTimeSeek(void); +int APS5_VABI sceVorbisDecTimeSeekPage(void); +int APS5_VABI sceVorbisDecRawSeekLap(void); +int APS5_VABI sceVorbisDecPcmSeekLap(void); +int APS5_VABI sceVorbisDecPcmSeekPageLap(void); +int APS5_VABI sceVorbisDecTimeSeekLap(void); +int APS5_VABI sceVorbisDecTimeSeekPageLap(void); +int APS5_VABI sceVorbisDecRawTell(void); +int APS5_VABI sceVorbisDecPcmTell(void); +int APS5_VABI sceVorbisDecTimeTell(void); +int APS5_VABI sceVorbisDecInfo(void); +int APS5_VABI sceVorbisDecComment(void); +int APS5_VABI sceVorbisDecRead(void); +int APS5_VABI sceVorbisDecReadFloat(void); +int APS5_VABI sceVorbisDecReadFilter(void); +int APS5_VABI sceVorbisDecCrossLap(void); +int APS5_VABI sceVorbisDecHalfrate(void); +int APS5_VABI sceVorbisDecHalfrateP(void); +int APS5_VABI sceVorbisDecGetDecodePriority(void); +int APS5_VABI sceVorbisDecSetDecodePriority(void); +int APS5_VABI sceVorbisDecGetLastDecodeError(void); +} + +template +static bool Throws(TCall call) { + try { + call(); + } catch (const std::runtime_error&) { + return true; + } + return false; +} + +static void Require(bool value, const char* what) { + if (value) return; + std::printf("VorbisDec test failed: %s\n", what); + std::abort(); +} + +int main() { + Require(Throws([] { sceVorbisDecOpenCallbacks(); }), "sceVorbisDecOpenCallbacks must throw"); + Require(Throws([] { sceVorbisDecClear(); }), "sceVorbisDecClear must throw"); + Require(Throws([] { sceVorbisDecBitrate(); }), "sceVorbisDecBitrate must throw"); + Require(Throws([] { sceVorbisDecBitrateInstant(); }), "sceVorbisDecBitrateInstant must throw"); + Require(Throws([] { sceVorbisDecStreams(); }), "sceVorbisDecStreams must throw"); + Require(Throws([] { sceVorbisDecSeekable(); }), "sceVorbisDecSeekable must throw"); + Require(Throws([] { sceVorbisDecSerialNumber(); }), "sceVorbisDecSerialNumber must throw"); + Require(Throws([] { sceVorbisDecRawTotal(); }), "sceVorbisDecRawTotal must throw"); + Require(Throws([] { sceVorbisDecPcmTotal(); }), "sceVorbisDecPcmTotal must throw"); + Require(Throws([] { sceVorbisDecTimeTotal(); }), "sceVorbisDecTimeTotal must throw"); + Require(Throws([] { sceVorbisDecRawSeek(); }), "sceVorbisDecRawSeek must throw"); + Require(Throws([] { sceVorbisDecPcmSeek(); }), "sceVorbisDecPcmSeek must throw"); + Require(Throws([] { sceVorbisDecPcmSeekPage(); }), "sceVorbisDecPcmSeekPage must throw"); + Require(Throws([] { sceVorbisDecTimeSeek(); }), "sceVorbisDecTimeSeek must throw"); + Require(Throws([] { sceVorbisDecTimeSeekPage(); }), "sceVorbisDecTimeSeekPage must throw"); + Require(Throws([] { sceVorbisDecRawSeekLap(); }), "sceVorbisDecRawSeekLap must throw"); + Require(Throws([] { sceVorbisDecPcmSeekLap(); }), "sceVorbisDecPcmSeekLap must throw"); + Require(Throws([] { sceVorbisDecPcmSeekPageLap(); }), "sceVorbisDecPcmSeekPageLap must throw"); + Require(Throws([] { sceVorbisDecTimeSeekLap(); }), "sceVorbisDecTimeSeekLap must throw"); + Require(Throws([] { sceVorbisDecTimeSeekPageLap(); }), "sceVorbisDecTimeSeekPageLap must throw"); + Require(Throws([] { sceVorbisDecRawTell(); }), "sceVorbisDecRawTell must throw"); + Require(Throws([] { sceVorbisDecPcmTell(); }), "sceVorbisDecPcmTell must throw"); + Require(Throws([] { sceVorbisDecTimeTell(); }), "sceVorbisDecTimeTell must throw"); + Require(Throws([] { sceVorbisDecInfo(); }), "sceVorbisDecInfo must throw"); + Require(Throws([] { sceVorbisDecComment(); }), "sceVorbisDecComment must throw"); + Require(Throws([] { sceVorbisDecRead(); }), "sceVorbisDecRead must throw"); + Require(Throws([] { sceVorbisDecReadFloat(); }), "sceVorbisDecReadFloat must throw"); + Require(Throws([] { sceVorbisDecReadFilter(); }), "sceVorbisDecReadFilter must throw"); + Require(Throws([] { sceVorbisDecCrossLap(); }), "sceVorbisDecCrossLap must throw"); + Require(Throws([] { sceVorbisDecHalfrate(); }), "sceVorbisDecHalfrate must throw"); + Require(Throws([] { sceVorbisDecHalfrateP(); }), "sceVorbisDecHalfrateP must throw"); + Require(Throws([] { sceVorbisDecGetDecodePriority(); }), "sceVorbisDecGetDecodePriority must throw"); + Require(Throws([] { sceVorbisDecSetDecodePriority(); }), "sceVorbisDecSetDecodePriority must throw"); + Require(Throws([] { sceVorbisDecGetLastDecodeError(); }), "sceVorbisDecGetLastDecodeError must throw"); + + std::puts("All VorbisDec guest tests passed"); + return 0; +} diff --git a/docs/dev/debt/libSceVorbisDec.md b/docs/dev/debt/libSceVorbisDec.md new file mode 100644 index 0000000000..f0c2ddc877 --- /dev/null +++ b/docs/dev/debt/libSceVorbisDec.md @@ -0,0 +1,38 @@ +# libSceVorbisDec technical debt + +### Unknown function info + +- [sceVorbisDecBitrate](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecBitrateInstant](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecClear](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecComment](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecCrossLap](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecGetDecodePriority](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecGetLastDecodeError](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecHalfrate](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecHalfrateP](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecInfo](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecOpenCallbacks](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecPcmSeek](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecPcmSeekLap](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecPcmSeekPage](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecPcmSeekPageLap](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecPcmTell](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecPcmTotal](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecRawSeek](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecRawSeekLap](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecRawTell](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecRawTotal](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecRead](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecReadFilter](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecReadFloat](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecSeekable](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecSerialNumber](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecSetDecodePriority](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecStreams](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecTimeSeek](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecTimeSeekLap](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecTimeSeekPage](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecTimeSeekPageLap](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecTimeTell](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database +- [sceVorbisDecTimeTotal](../../../core/libs/prx/libSceVorbisDec/Export.cpp) (libSceVorbisDec) - declared without parameters, signature unknown; name from the NID database From c19dcecfec8954233d0f22da969422c8eed7ac4f Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Sat, 17 Oct 2026 23:42:11 +0400 Subject: [PATCH 065/523] fix(agc): expire collected write proofs on allocation changes --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 8 ++++ .../Execution/src/GuestMemory.cpp | 19 +++++---- .../prx/libSceAgcDriver/tests/Recorder.cpp | 40 +++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 2782537b10..3d086d3763 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -1383,6 +1383,14 @@ if(BUILD_TESTING) set_tests_properties(agc_collect_mapping_history_ring PROPERTIES ENVIRONMENT "APS5_NO_EPOCH_COLLECT_MAP=1") set_tests_properties(agc_collect_mapping_history_shared PROPERTIES ENVIRONMENT "APS5_SHARED_COLLECT_MEMO=1") set_tests_properties(agc_collect_mapping_history_uncached PROPERTIES ENVIRONMENT "APS5_NO_COLLECT_MEMO=1") + if(WIN32) + foreach(mode IN ITEMS cached uncached) + add_test(NAME agc_hint_mapping_history_${mode} COMMAND agc_driver_recorder_tests --hint-mapping-history-only) + set_tests_properties(agc_hint_mapping_history_${mode} PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 + ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + endforeach() + set_tests_properties(agc_hint_mapping_history_uncached PROPERTIES ENVIRONMENT "APS5_NO_COLLECT_MEMO=1") + endif() add_test(NAME agc_storage_kept_unit_settle COMMAND agc_driver_recorder_tests --kept-unit-settle-only) set_tests_properties(agc_storage_kept_unit_settle PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30) if(WIN32) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp index 36d57da9c9..a937f56111 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp @@ -873,6 +873,7 @@ struct WriteTracker { std::uintptr_t end = 0; std::uint64_t epoch = 0; std::uint64_t unwatched = 0; + std::uint64_t mapping = 0; }; std::array memo{}; std::size_t nextMemo = 0; @@ -1347,19 +1348,20 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi // must not differ between them. const auto epoch = currentCollectEpoch(); const auto unwatched = unwatchSerial.load(std::memory_order_acquire); + const auto mapping = GuestAllocations::GuestAllocationsGeneration_nid_postfix(); const bool useMemo = memoized && collectMemoEnabled() && bytes != 0; if (useMemo && !sharedCollectMemo()) { // An entry exists only for a completed walk of an in-arena range, so the tracker is // initialized and watched; nothing below the lock needs asking. if (epochCollectMapEnabled()) { const auto& map = epochCollectMap(epoch); - if (const auto found = map.ranges.find(first); found != map.ranges.end() && found->second.unwatched == unwatched && stop <= found->second.end) { + if (const auto found = map.ranges.find(first); found != map.ranges.end() && found->second.unwatched == unwatched && found->second.mapping == mapping && stop <= found->second.end) { collectMemoHits.fetch_add(1, std::memory_order_relaxed); return tracker.generation.load(std::memory_order_relaxed); } } for (const auto& entry : threadCollectMemo.entries) { - if (entry.epoch == epoch && entry.unwatched == unwatched && entry.begin <= first && stop <= entry.end) { + if (entry.epoch == epoch && entry.unwatched == unwatched && entry.mapping == mapping && entry.begin <= first && stop <= entry.end) { // The current generation, not the memoized one: blocks stamped since (MarkWritten, an // overlapping collect) were written before this caller reads, and an older value would // make every later UnchangedSince fail until the epoch ends. @@ -1374,7 +1376,7 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi auto cursor = first; if (useMemo && sharedCollectMemo()) { for (const auto& entry : tracker.memo) { - if (entry.epoch == epoch && entry.unwatched == unwatchSerial.load(std::memory_order_relaxed) && entry.begin <= cursor && stop <= entry.end) { + if (entry.epoch == epoch && entry.unwatched == unwatchSerial.load(std::memory_order_relaxed) && entry.mapping == mapping && entry.begin <= cursor && stop <= entry.end) { collectMemoHits.fetch_add(1, std::memory_order_relaxed); return tracker.generation; } @@ -1395,12 +1397,12 @@ std::uint64_t collectWrites(std::uint64_t address, std::size_t bytes, bool memoi // Only a completed walk is remembered; a failed one (uncommitted pages) returned 0 above. if (collectMemoEnabled()) { const auto serial = unwatchSerial.load(std::memory_order_relaxed); - if (sharedCollectMemo()) tracker.memo[tracker.nextMemo++ % tracker.memo.size()] = {first, stop, epoch, serial}; + if (sharedCollectMemo()) tracker.memo[tracker.nextMemo++ % tracker.memo.size()] = {first, stop, epoch, serial, mapping}; else { - threadCollectMemo.entries[threadCollectMemo.next++ % threadCollectMemo.entries.size()] = {first, stop, epoch, serial}; + threadCollectMemo.entries[threadCollectMemo.next++ % threadCollectMemo.entries.size()] = {first, stop, epoch, serial, mapping}; if (epochCollectMapEnabled()) { auto& entry = epochCollectMap(epoch).ranges[first]; - if (entry.unwatched != serial || entry.end < stop) entry = {first, stop, epoch, serial}; + if (entry.unwatched != serial || entry.mapping != mapping || entry.end < stop) entry = {first, stop, epoch, serial, mapping}; } } } @@ -1826,15 +1828,16 @@ std::uint64_t UntrackedHint(std::uint64_t address, std::size_t bytes) { tracker.initialize(); if (!tracker.watched || !tracker.hinted(first, stop)) return 0; const auto unwatched = unwatchSerial.load(std::memory_order_relaxed); + const auto mapping = GuestAllocations::GuestAllocationsGeneration_nid_postfix(); const bool memoized = collectMemoEnabled() && std::any_of(threadHintMemo.entries.begin(), threadHintMemo.entries.end(), [&](const WriteTracker::Memo& entry) { - return entry.epoch == epoch && entry.unwatched == unwatched && entry.begin <= first && stop <= entry.end; + return entry.epoch == epoch && entry.unwatched == unwatched && entry.mapping == mapping && entry.begin <= first && stop <= entry.end; }); if (memoized) { collectMemoHits.fetch_add(1, std::memory_order_relaxed); } else { const TimedAccess timed(CounterCollect, bytes); if (!walkHints(tracker, first, stop)) return 0; - if (collectMemoEnabled()) threadHintMemo.entries[threadHintMemo.next++ % threadHintMemo.entries.size()] = {first, stop, epoch, unwatched}; + if (collectMemoEnabled()) threadHintMemo.entries[threadHintMemo.next++ % threadHintMemo.entries.size()] = {first, stop, epoch, unwatched, mapping}; } return tracker.newestHint(first, stop); #else diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 5edb320d3a..3aee54c8fc 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -169,6 +169,41 @@ std::optional selectDevice(std::span(AllocateWatched(bytes, 65536)); + if (memory == nullptr) return false; + GuestAllocations::Mutation().Add(memory, bytes, true, true, false); + struct Cleanup { + std::uint8_t* memory; + ~Cleanup() { + GuestAllocations::Mutation().Remove(memory); + ReleaseWatched(memory, bytes); + } + } cleanup{memory}; + const auto address = reinterpret_cast(memory); + std::memset(memory, 0x11, bytes); + AgcDriver::GuestMemory::BumpCollectEpoch(); + AgcDriver::GuestMemory::Unwatch(address, bytes); + const auto before = AgcDriver::GuestMemory::UntrackedHint(address, bytes); + Require(before != 0, "the hint mapping fixture has no write hint"); + Require(AgcDriver::GuestMemory::UntrackedHint(address, bytes) == before, "an unchanged hint range advanced its history"); + memory[4096] = 0x22; + GuestAllocations::Mutation().Remove(memory); + GuestAllocations::Mutation().Add(memory, bytes, true, true, false); + const auto after = AgcDriver::GuestMemory::UntrackedHint(address, bytes); + Require(after > before, "a cached hint walk hid a CPU store across allocation replacement"); + Require(memory[4096] == 0x22 && memory[bytes - 4096] == 0x11, "collecting replacement hints changed guest bytes"); + GuestAllocations::Mutation().Remove(memory); + GuestAllocations::Mutation().Add(memory, bytes, true, true, false); + Require(AgcDriver::GuestMemory::UntrackedHint(address, bytes) == after, "allocation replacement invented a CPU write hint"); + return true; +#else + return false; +#endif +} + void deviceSelectionTests() { const auto properties = [](const char* name, VkPhysicalDeviceType type) { VkPhysicalDeviceProperties result{}; @@ -9672,6 +9707,11 @@ int main(int argc, char** argv) { std::cout << "collect mapping history tests passed\n"; return 0; } + if (argc == 2 && std::string_view(argv[1]) == "--hint-mapping-history-only") { + if (!hintMappingHistoryTests()) return 77; + std::cout << "hint mapping history tests passed\n"; + return 0; + } if (argc == 2 && std::string_view(argv[1]) == "--device-selection-only") { deviceSelectionTests(); std::cout << "recorder device selection tests passed\n"; From 9fe1f963c8f1849478d86dc77e23cfcc4be1278e Mon Sep 17 00:00:00 2001 From: Zenodeon Date: Sat, 17 Oct 2026 23:42:12 +0400 Subject: [PATCH 066/523] test(agc): verify incomplete array history against the backend --- .../prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp index 0d0c8a865e..aed0ffc37a 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ArrayLayerTarget.cpp @@ -395,6 +395,7 @@ void UncommittedUnusedLayers(AgcDriver::VulkanDevice& device, std::uint32_t slic auto* texels = reinterpret_cast(active); std::fill_n(texels, LayerBytes / sizeof(std::uint32_t), Initial); Require(AgcDriver::GuestMemory::CollectWrites(active, LayerBytes) != 0, "the committed array slice has no write history"); + Require(AgcDriver::GuestMemory::CollectWritesUncached(surface, SurfaceBytes) == 0, "the uncommitted array fixture unexpectedly has complete backend write history"); Require(AgcDriver::GuestMemory::CollectWrites(surface, SurfaceBytes) == 0, "the unused array slices unexpectedly have complete write history"); AgcDriver::GuestMemory::BumpCollectEpoch(); DrawSlice(device, surface, slice, TileR64KBX, false); From 923a32c9c9e6352e01bde42072456ea40e862b65 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:43:02 +0200 Subject: [PATCH 067/523] fix(libkernel_sys): fail link, symlink, pathconf and fchmodat through a regular file with ENOTDIR FreeBSD 11.4 vfs_cache_lookup answers ENOTDIR when the directory it searches is not a directory (sys/kern/vfs_cache.c:2104-2105), the lookup behind the calls fixed in the earlier ENOTDIR changes. These four calls answered ENOENT for a path such as file/child on Windows. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel_sys/Export.cpp | 14 +++++++------- core/libs/tests/GuestKernelSys.cpp | 6 ++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libkernel_sys/Export.cpp b/core/libs/prx/libkernel_sys/Export.cpp index a0adb45364..18c4eb733d 100644 --- a/core/libs/prx/libkernel_sys/Export.cpp +++ b/core/libs/prx/libkernel_sys/Export.cpp @@ -301,8 +301,8 @@ std::int64_t APS5_VABI pathconf_nid_postfix(const char* path, int name) { if (const int error = ResolvePathError_nid_no_patch(path, native)) return Fail(error); std::error_code error; const auto status = std::filesystem::status(native, error); - if (error) return Fail(GuestError(error)); - if (!std::filesystem::exists(status)) return Fail(GuestEnoent); + if (error) return Fail(error == std::errc::no_such_file_or_directory && HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestError(error)); + if (!std::filesystem::exists(status)) return Fail(HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestEnoent); switch (name) { case PcLinkMax: return 32767; case PcNameMax: return static_cast(GuestNameMax); @@ -341,10 +341,10 @@ int APS5_VABI link_nid_postfix(const char* existing, const char* created) { if (const int error = ResolvePathError_nid_no_patch(created, destination)) return static_cast(Fail(error)); std::error_code error; const auto status = std::filesystem::status(source, error); - if (!std::filesystem::exists(status)) return static_cast(Fail(GuestEnoent)); + if (!std::filesystem::exists(status)) return static_cast(Fail(HasNonDirectoryPrefix(source) ? GuestEnotdir : GuestEnoent)); if (std::filesystem::is_directory(status)) return static_cast(Fail(GuestEperm)); if (std::filesystem::exists(std::filesystem::symlink_status(destination, error))) return static_cast(Fail(GuestEexist)); - if (!std::filesystem::is_directory(destination.parent_path(), error)) return static_cast(Fail(GuestEnoent)); + if (!std::filesystem::is_directory(destination.parent_path(), error)) return static_cast(Fail(HasNonDirectoryPrefix(destination) ? GuestEnotdir : GuestEnoent)); const auto target = std::filesystem::is_symlink(source, error) ? std::filesystem::canonical(source, error) : source; if (!error) std::filesystem::create_hard_link(target, destination, error); if (error == std::errc::cross_device_link) return static_cast(Fail(GuestExdev)); @@ -404,7 +404,7 @@ int APS5_VABI symlink_nid_postfix(const char* target, const char* created) { #else if (std::filesystem::exists(std::filesystem::symlink_status(native, error))) return static_cast(Fail(GuestEexist)); #endif - if (!std::filesystem::is_directory(native.parent_path(), error)) return static_cast(Fail(GuestEnoent)); + if (!std::filesystem::is_directory(native.parent_path(), error)) return static_cast(Fail(HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestEnoent)); const bool directory = std::filesystem::is_directory(native.parent_path() / contents, error); #ifdef _WIN32 DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; @@ -522,9 +522,9 @@ int APS5_VABI fchmodat_nid_postfix(int fd, const char* path, int mode, int flag) } } #ifdef _WIN32 - if (::_wchmod(native.wstring().c_str(), mode & 07777) != 0) return static_cast(Fail(GuestFileError(errno))); + if (::_wchmod(native.wstring().c_str(), mode & 07777) != 0) return static_cast(Fail(errno == ENOENT && HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestFileError(errno))); #else - if (::chmod(native.c_str(), static_cast(mode & 07777)) != 0) return static_cast(Fail(GuestFileError(errno))); + if (::chmod(native.c_str(), static_cast(mode & 07777)) != 0) return static_cast(Fail(errno == ENOENT && HasNonDirectoryPrefix(native) ? GuestEnotdir : GuestFileError(errno))); #endif RecordWrittenPath_nid_no_patch(native); return 0; diff --git a/core/libs/tests/GuestKernelSys.cpp b/core/libs/tests/GuestKernelSys.cpp index ea71a5644b..c7e2a639ad 100644 --- a/core/libs/tests/GuestKernelSys.cpp +++ b/core/libs/tests/GuestKernelSys.cpp @@ -387,6 +387,12 @@ int main() { Require(chroot_nid_postfix((root / "file.txt" / "..").string().c_str()) == -1 && Error() == 20); Require(chroot_nid_postfix((root / std::string(256, 'n')).string().c_str()) == -1 && Error() == 63); Require(chroot_nid_postfix(("/" + std::string(1024, 'p')).c_str()) == -1 && Error() == 63); + const std::string throughFile = (root / "file.txt" / "child").string(); + Require(pathconf_nid_postfix(throughFile.c_str(), 4) == -1 && Error() == 20); + Require(fchmodat_nid_postfix(-100, throughFile.c_str(), 0600, 0) == -1 && Error() == 20); + Require(symlink_nid_postfix("target", throughFile.c_str()) == -1 && Error() == 20); + Require(link_nid_postfix(file.c_str(), throughFile.c_str()) == -1 && Error() == 20); + Require(link_nid_postfix(throughFile.c_str(), (root / "linked").string().c_str()) == -1 && Error() == 20); Require(ChrootThrows(root.string())); Require(ChrootThrows("/" + root.filename().string() + "/.")); const auto guestRoot = "/" + root.filename().string(); From 6ead14f9e8e1862534e3731805893da63eb95cc2 Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Tue, 20 Oct 2026 16:56:06 +0200 Subject: [PATCH 068/523] feat(libkernel): watch a kqueue with EVFILT_READ --- core/libs/prx/libkernel/Equeue/Equeue.cpp | 97 +++++++++++-- core/libs/prx/libkernel/Equeue/Equeue.hpp | 8 ++ core/libs/prx/libkernel/Equeue/src/Kqueue.cpp | 48 ++++++- core/libs/tests/GuestKqueue.cpp | 131 ++++++++++++++++++ docs/dev/debt/libkernel.md | 3 +- 5 files changed, 273 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libkernel/Equeue/Equeue.cpp b/core/libs/prx/libkernel/Equeue/Equeue.cpp index ec37c3ac94..1256cc9391 100644 --- a/core/libs/prx/libkernel/Equeue/Equeue.cpp +++ b/core/libs/prx/libkernel/Equeue/Equeue.cpp @@ -16,6 +16,13 @@ static std::unordered_map g_equeues; static std::mutex g_equeueMutex; static uint64_t g_nextEqueue = 1; +struct KernelEqueuePrivate::WaitSet { + std::vector interests; + std::vector> held; + std::vector> queues; + uint64_t nearestTimerNs = std::numeric_limits::max(); +}; + extern "C" { KernelEqueuePrivate::KernelEqueuePrivate(KernelEqueue handle, bool reportOncePerWait) : m_handle(handle), m_reportOncePerWait(reportOncePerWait) {} @@ -97,24 +104,77 @@ void KernelEqueuePrivate::WakePollers() { } } -void KernelEqueuePrivate::WaitForDescriptors(std::unique_lock& lock, std::uint64_t deadlineNanos) { - std::vector interests; - std::vector> held; +void KernelEqueuePrivate::GatherInterests(WaitSet& wait, std::uintptr_t waker, bool nested) { for (const auto& e : m_events) { - if (!e.enabled || e.filter.pollFunc == nullptr) { + if (!e.enabled) { continue; } - if (e.filter.waitDescriptor >= 0) { - interests.push_back({e.filter.waitDescriptor, false}); - held.push_back(e.filter.owner); + if (nested && !e.triggered && e.deadlineNs != 0) { + wait.nearestTimerNs = std::min(wait.nearestTimerNs, e.deadlineNs); + } + if (e.filter.pollFunc == nullptr) { + continue; + } + if (e.filter.watched) { + GatherWatched(wait, e.filter.watched, waker); + } else if (e.filter.waitDescriptor >= 0) { + wait.interests.push_back({e.filter.waitDescriptor, false}); + wait.held.push_back(e.filter.owner); } else { - interests.push_back({static_cast(e.event.ident), e.event.filter == EVFILT_WRITE}); + wait.interests.push_back({static_cast(e.event.ident), e.event.filter == EVFILT_WRITE}); } } +} + +void KernelEqueuePrivate::GatherWatched(WaitSet& wait, const std::shared_ptr& queue, std::uintptr_t waker) { + std::lock_guard lock(queue->m_mutex); + if (queue->m_closed) { + return; + } + queue->m_pollers.push_back(waker); + wait.queues.push_back(queue); + queue->GatherInterests(wait, waker, true); +} + +void KernelEqueuePrivate::ReleaseWatched(WaitSet& wait, std::uintptr_t waker) { + for (const auto& queue : wait.queues) { + std::lock_guard lock(queue->m_mutex); + const auto found = std::find(queue->m_pollers.begin(), queue->m_pollers.end(), waker); + if (found != queue->m_pollers.end()) { + queue->m_pollers.erase(found); + } + } + wait.queues.clear(); +} + +void KernelEqueuePrivate::WaitForDescriptors(std::unique_lock& lock, std::uint64_t deadlineNanos) { const auto waker = GuestSockets::CurrentWaker(); + WaitSet wait; + GatherInterests(wait, waker, false); m_pollers.push_back(waker); + if (!wait.queues.empty()) { + PollEvents(); + if (std::any_of(m_events.begin(), m_events.end(), [](const KernelEqueueEvent& event) { return event.enabled && event.triggered; })) { + m_pollers.erase(std::find(m_pollers.begin(), m_pollers.end(), waker)); + ReleaseWatched(wait, waker); + return; + } + if (wait.nearestTimerNs != std::numeric_limits::max()) { + const uint64_t nowMonotonic = MonotonicNs(); + const uint64_t remainingNs = wait.nearestTimerNs > nowMonotonic ? wait.nearestTimerNs - nowMonotonic : 0; + const uint64_t timerDeadline = TimedWait::NowNanos() + std::clamp(remainingNs, 1000, static_cast(std::numeric_limits::max()) * 1000ULL); + deadlineNanos = deadlineNanos == 0 ? timerDeadline : std::min(deadlineNanos, timerDeadline); + } + } lock.unlock(); - const bool waited = GuestSockets::WaitAny(interests, deadlineNanos); + bool waited = false; + try { + waited = GuestSockets::WaitAny(wait.interests, deadlineNanos); + } catch (...) { + ReleaseWatched(wait, waker); + throw; + } + ReleaseWatched(wait, waker); lock.lock(); m_pollers.erase(std::find(m_pollers.begin(), m_pollers.end(), waker)); if (!waited) { @@ -217,13 +277,28 @@ int KernelEqueuePrivate::GetTriggeredEvents(KernelEvent* ev, int num) { } bool KernelEqueuePrivate::HasTriggeredEvents() { + return CountTriggeredEvents() != 0; +} + +std::size_t KernelEqueuePrivate::CountTriggeredEvents() { std::unique_lock lock(m_mutex); if (m_closed) { - return false; + return 0; } TriggerExpiredTimers(MonotonicNs()); PollEvents(); - return std::any_of(m_events.begin(), m_events.end(), [](const KernelEqueueEvent& event) { return event.enabled && event.triggered; }); + return static_cast(std::count_if(m_events.begin(), m_events.end(), [](const KernelEqueueEvent& event) { return event.enabled && event.triggered; })); +} + +std::vector> KernelEqueuePrivate::WatchedQueues() { + std::unique_lock lock(m_mutex); + std::vector> queues; + for (const auto& event : m_events) { + if (event.filter.watched) { + queues.push_back(event.filter.watched); + } + } + return queues; } int KernelEqueuePrivate::WaitForEvents(KernelEvent* ev, int num, uint32_t micros) { diff --git a/core/libs/prx/libkernel/Equeue/Equeue.hpp b/core/libs/prx/libkernel/Equeue/Equeue.hpp index 860dabf3e5..ef217632d6 100644 --- a/core/libs/prx/libkernel/Equeue/Equeue.hpp +++ b/core/libs/prx/libkernel/Equeue/Equeue.hpp @@ -42,6 +42,7 @@ static constexpr uint16_t EV_EOF = 0x8000; static constexpr int EQUEUE_OK = 0; struct KernelEqueueEvent; +class KernelEqueuePrivate; using EqueueTriggerFunc = void (*)(KernelEqueueEvent* event, void* triggerData); using EqueueResetFunc = void (*)(KernelEqueueEvent* event); @@ -56,6 +57,7 @@ struct KernelFilter { EqueueDeleteFunc deleteEventFunc = nullptr; EqueuePollFunc pollFunc = nullptr; int waitDescriptor = -1; + std::shared_ptr watched; }; struct KernelEqueueEvent { @@ -87,6 +89,8 @@ public: int EditEvent(uintptr_t ident, int16_t filter, const std::function& edit); int GetTriggeredEvents(KernelEvent* ev, int num); bool HasTriggeredEvents(); + std::size_t CountTriggeredEvents(); + std::vector> WatchedQueues(); int WaitForEvents(KernelEvent* ev, int num, uint32_t micros); int WaitForEventsUntil(KernelEvent* ev, int num, std::uint64_t deadlineNanos); void RemoveDescriptorEvents(uintptr_t ident); @@ -94,6 +98,7 @@ public: static uint64_t MonotonicNs(); private: + struct WaitSet; void TriggerExpiredTimers(uint64_t nowNs); bool NextTimerWaitMicros(uint64_t nowNs, uint32_t* out) const; int CollectTriggered(KernelEvent* ev, int num); @@ -101,6 +106,9 @@ private: bool PollEvents(); void WakePollers(); void WaitForDescriptors(std::unique_lock& lock, std::uint64_t deadlineNanos); + void GatherInterests(WaitSet& wait, std::uintptr_t waker, bool nested); + static void GatherWatched(WaitSet& wait, const std::shared_ptr& queue, std::uintptr_t waker); + static void ReleaseWatched(WaitSet& wait, std::uintptr_t waker); std::list m_events; std::vector m_pollers; diff --git a/core/libs/prx/libkernel/Equeue/src/Kqueue.cpp b/core/libs/prx/libkernel/Equeue/src/Kqueue.cpp index 752caca239..cc4ca5389f 100644 --- a/core/libs/prx/libkernel/Equeue/src/Kqueue.cpp +++ b/core/libs/prx/libkernel/Equeue/src/Kqueue.cpp @@ -54,6 +54,7 @@ constexpr std::uint16_t KNOTE_CLEARED_FLAGS = EV_ADD | EV_DELETE | EV_ENABLE | E std::mutex g_kqueueMutex; std::unordered_map g_kqueues; +std::mutex g_kqueueWatchMutex; int PosixFailure(int error) { *__error_nid_postfix() = error; @@ -191,13 +192,56 @@ void WatchEdges(KernelEqueueEvent& event, bool socket) { event.filter.resetFunc = ResetEdge; } +bool PollKqueue(KernelEqueueEvent* e) { + const auto pending = e->filter.watched->CountTriggeredEvents(); + e->event.data = static_cast(pending); + return pending != 0; +} + +bool KqueueReaches(const KernelEqueueRef& from, const KernelEqueuePrivate* target, std::vector& visited) { + if (from.get() == target) { + return true; + } + if (std::find(visited.begin(), visited.end(), from.get()) != visited.end()) { + return false; + } + visited.push_back(from.get()); + for (const auto& next : from->WatchedQueues()) { + if (KqueueReaches(next, target, visited)) { + return true; + } + } + return false; +} + +int AddKqueueKnote(const KernelEqueueRef& queue, const KernelEqueueRef& watched, KernelEvent& change, std::uint16_t flags) { + if ((flags & EV_CLEAR) != 0) { + NotImplemented_nid_no_patch("kevent: EV_CLEAR on a kqueue descriptor"); + } + std::lock_guard lock(g_kqueueWatchMutex); + std::vector visited; + if (KqueueReaches(watched, queue.get(), visited)) { + NotImplemented_nid_no_patch("kevent: EVFILT_READ on a kqueue that watches the kqueue being registered on"); + } + KernelEqueueEvent event{}; + event.event = change; + event.event.flags = flags; + event.event.fflags = 0; + event.event.data = 0; + event.filter.pollFunc = PollKqueue; + event.filter.watched = watched; + change.fflags = 0; + change.data = 0; + return GuestErrno(queue->AddEvent(event)); +} + int AddDescriptorKnote(const KernelEqueueRef& queue, KernelEvent& change, std::uint16_t flags) { const int descriptor = static_cast(change.ident); - if (IsKqueueDescriptor(descriptor)) { + if (const auto watched = FindKqueue(descriptor)) { if (change.filter != EVFILT_READ) { return GUEST_EINVAL; } - NotImplemented_nid_no_patch("kevent: EVFILT_READ on a kqueue descriptor"); + return AddKqueueKnote(queue, watched, change, flags); } if (descriptor == 0 && change.filter == EVFILT_READ && File::IsHostStream(descriptor)) { return AddStandardInputKnote(queue, change, flags); diff --git a/core/libs/tests/GuestKqueue.cpp b/core/libs/tests/GuestKqueue.cpp index aeb8b759ed..cba229cd6f 100644 --- a/core/libs/tests/GuestKqueue.cpp +++ b/core/libs/tests/GuestKqueue.cpp @@ -299,6 +299,136 @@ static void SocketPair() { Require(close_nid_postfix(kq) == 0); } +static void WatchKqueue() { + const int inner = kqueue_nid_postfix(); + const int outer = kqueue_nid_postfix(); + const auto watched = static_cast(inner); + int marker = 0; + KernelEvent events[4]{}; + Require(Apply(inner, Change(5, EvfiltUser, EvAdd | EvClear)) == 0); + Require(Apply(inner, Change(6, EvfiltUser, EvAdd | EvClear)) == 0); + Require(Apply(outer, Change(watched, EvfiltRead, EvAdd, 0, 0, &marker)) == 0); + Require(Poll(outer, events, 4) == 0); + Require(Poll(inner, events, 4) == 0); + + Require(Apply(inner, Change(5, EvfiltUser, 0, NoteTrigger)) == 0); + Require(Poll(outer, events, 4) == 1); + Require(events[0].ident == watched && events[0].filter == EvfiltRead && events[0].flags == 0 && events[0].fflags == 0); + Require(events[0].data == 1 && events[0].udata == &marker); + Require(Apply(inner, Change(6, EvfiltUser, 0, NoteTrigger)) == 0); + Require(Poll(outer, events, 4) == 1 && events[0].data == 2); + Require(Poll(outer, events, 4) == 1 && events[0].data == 2); + Require(Poll(inner, events, 1) == 1); + Require(Poll(outer, events, 4) == 1 && events[0].data == 1); + Require(Poll(inner, events, 4) == 1); + Require(Poll(outer, events, 4) == 0); + + Require(Apply(outer, Change(watched, EvfiltRead, EvDisable, 0, 0, &marker)) == 0); + Require(Apply(inner, Change(5, EvfiltUser, 0, NoteTrigger)) == 0); + Require(Poll(outer, events, 4) == 0); + Require(Apply(outer, Change(watched, EvfiltRead, EvEnable, 0, 0, &marker)) == 0); + Require(Poll(outer, events, 4) == 1 && events[0].data == 1); + Require(Poll(inner, events, 4) == 1); + + const KernelTimespec limit{5, 0}; + auto start = std::chrono::steady_clock::now(); + std::thread trigger([inner] { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + Require(Apply(inner, Change(5, EvfiltUser, 0, NoteTrigger)) == 0); + }); + Require(kevent_nid_postfix(outer, nullptr, 0, events, 4, &limit) == 1); + auto waited = std::chrono::steady_clock::now() - start; + trigger.join(); + Require(waited >= std::chrono::milliseconds(40) && waited < std::chrono::seconds(4)); + Require(events[0].ident == watched && events[0].data == 1 && events[0].udata == &marker); + Require(Poll(inner, events, 4) == 1); + + start = std::chrono::steady_clock::now(); + std::thread blocked([outer] { + KernelEvent event{}; + Require(kevent_nid_postfix(outer, nullptr, 0, &event, 1, nullptr) == 1 && event.data == 1); + }); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + Require(Apply(inner, Change(6, EvfiltUser, 0, NoteTrigger)) == 0); + blocked.join(); + waited = std::chrono::steady_clock::now() - start; + Require(waited < std::chrono::seconds(4)); + Require(Poll(inner, events, 4) == 1); + + Require(Apply(inner, Change(7, EvfiltTimer, EvAdd, 0, 60)) == 0); + start = std::chrono::steady_clock::now(); + Require(kevent_nid_postfix(outer, nullptr, 0, events, 4, &limit) == 1); + waited = std::chrono::steady_clock::now() - start; + Require(waited >= std::chrono::milliseconds(40) && waited < std::chrono::seconds(4)); + Require(events[0].ident == watched && events[0].data == 1); + Require(Poll(inner, events, 4) == 1 && events[0].ident == 7); + Require(Apply(inner, Change(7, EvfiltTimer, EvDelete)) == 0); + Require(Poll(outer, events, 4) == 0); + + int pair[2]; + Require(socketpair_nid_postfix(Unix, Stream, 0, pair) == 0); + Require(Apply(inner, Change(static_cast(pair[0]), EvfiltRead, EvAdd)) == 0); + Require(Poll(outer, events, 4) == 0); + std::thread sender([&pair] { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + Require(send_nid_postfix(pair[1], "abc", 3, 0) == 3); + }); + Require(kevent_nid_postfix(outer, nullptr, 0, events, 4, &limit) == 1); + sender.join(); + Require(events[0].ident == watched && events[0].data == 1); + char buffer[4]{}; + Require(recv_nid_postfix(pair[0], buffer, sizeof(buffer), 0) == 3); + Require(Poll(outer, events, 4) == 0); + Require(close_nid_postfix(pair[0]) == 0 && close_nid_postfix(pair[1]) == 0); + + const int top = kqueue_nid_postfix(); + const auto middle = static_cast(outer); + Require(Apply(top, Change(middle, EvfiltRead, EvAdd)) == 0); + Require(Poll(top, events, 4) == 0); + std::thread deep([inner] { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + Require(Apply(inner, Change(5, EvfiltUser, 0, NoteTrigger)) == 0); + }); + Require(kevent_nid_postfix(top, nullptr, 0, events, 4, &limit) == 1); + deep.join(); + Require(events[0].ident == middle && events[0].data == 1); + Require(Poll(outer, events, 4) == 1 && events[0].ident == watched); + Require(Poll(inner, events, 4) == 1); + Require(Poll(top, events, 4) == 0); + Require(Apply(top, Change(middle, EvfiltRead, EvDelete)) == 0); + Require(close_nid_postfix(top) == 0); + + Require(Apply(inner, Change(5, EvfiltUser, 0, NoteTrigger)) == 0); + Require(Apply(outer, Change(watched, EvfiltRead, EvDelete)) == 0); + RequireFailure(Apply(outer, Change(watched, EvfiltRead, EvDelete)), GuestEnoent); + Require(Poll(outer, events, 4) == 0); + Require(Apply(outer, Change(watched, EvfiltRead, EvAdd | EvDispatch, 0, 0, &marker)) == 0); + Require(Poll(outer, events, 4) == 1 && events[0].data == 1 && events[0].flags == EvDispatch); + Require(Poll(outer, events, 4) == 0); + Require(Apply(outer, Change(watched, EvfiltRead, EvEnable, 0, 0, &marker)) == 0); + Require(Poll(outer, events, 4) == 1); + Require(Apply(outer, Change(watched, EvfiltRead, EvDelete)) == 0); + Require(Apply(outer, Change(watched, EvfiltRead, EvAdd | EvOneshot)) == 0); + Require(Poll(outer, events, 4) == 1 && events[0].data == 1 && events[0].flags == EvOneshot); + Require(Poll(outer, events, 4) == 0); + RequireFailure(Apply(outer, Change(watched, EvfiltRead, EvDelete)), GuestEnoent); + Require(Poll(inner, events, 4) == 1); + + RequireFailure(Apply(outer, Change(watched, EvfiltWrite, EvAdd)), GuestEinval); + RequireFailure(Apply(outer, Change(watched, EvfiltVnode, EvAdd, NoteWrite)), GuestEinval); + Require(ThrownMessage([outer, watched] { Apply(outer, Change(watched, EvfiltRead, EvAdd | EvClear)); }).find("EV_CLEAR on a kqueue descriptor") != std::string::npos); + RequireFailure(Apply(outer, Change(watched, EvfiltRead, EvDelete)), GuestEnoent); + Require(Apply(outer, Change(watched, EvfiltRead, EvAdd)) == 0); + Require(ThrownMessage([inner, outer] { Apply(inner, Change(static_cast(outer), EvfiltRead, EvAdd)); }).find("watches the kqueue") != std::string::npos); + Require(Apply(inner, Change(5, EvfiltUser, 0, NoteTrigger)) == 0); + Require(Poll(outer, events, 4) == 1 && events[0].data == 1); + + Require(close_nid_postfix(inner) == 0); + Require(Poll(outer, events, 4) == 0); + RequireFailure(Apply(outer, Change(watched, EvfiltRead, EvDelete)), GuestEbadf); + Require(close_nid_postfix(outer) == 0); +} + static void LowWaterMarks() { const int kq = kqueue_nid_postfix(); int pair[2]; @@ -888,6 +1018,7 @@ int main() { Timers(); PipeRead(); UserEvents(); + WatchKqueue(); SocketPair(); #ifdef __linux__ EdgeTriggeredSocketRead(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 04342995ea..c18d235166 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -114,7 +114,7 @@ - [sceKernelMprotect, sceKernelMtypeprotect](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) over a range with a gap that is not registered in the allocation tracker protect the registered parts and skip the gap, and return 0, as FreeBSD 11.4's [`vm_map_protect`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/vm/vm_map.c) does (it has no check for holes); a range whose start is unmapped (also when later pages are mapped) returns `SCE_KERNEL_ERROR_EINVAL` (`mprotect`: `EINVAL`) because an existing test pins that for an unmapped start; FreeBSD answers 0 there too (`vm_map_protect` takes the next entry), so this errno is not taken from the source and was not changed. `sceKernelMprotect` threw for a gap before, which ended the title. Every part is checked for a GPU or image pin before the first one is protected; the parts are then protected one after the other, so a host protection failure in a later part leaves the earlier parts changed (FreeBSD's `vm_map_protect` cannot fail in its second pass). The behaviour on the console is not known. None of this was measured on a console. - [sceKernelMprotect protection bits](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - bits outside `0x3F7` are dropped instead of refused, as a PS5 (firmware 13.60) drops them: Onimusha: Way of the Sword (PPSA27836 01.000.001) maps direct memory with `0xf2` and then protects it with `0xfffffff2`, which the console reports as `0xf3` and then `0x3f3`. In a payload process the console also drops the CPU execute bit (`0x4`) of flexible, direct and JIT memory, and POSIX `mprotect` with write and execute reports write only (`0x2`). A title process may keep execute, so it is kept here. The map calls and `sceKernelMtypeprotect` refuse undefined bits on the console with `EINVAL` (`0x80020016`), and the direct map calls refuse execute; here both throw for undefined bits, and the direct map calls map execute - [JIT shared memory](../../../core/libs/prx/libkernel/DirectMemory/Jit.cpp) (libkernel) - `sceKernelJitCreateSharedMemory`, `sceKernelJitCreateAliasOfSharedMemory` and `sceKernelJitMapSharedMemory` take the signatures of [Orbital](https://github.com/AlexAltea/orbital)'s `tools/ida/db_types.json` (name, length, maximum protection, descriptor out; descriptor, maximum protection, descriptor out; descriptor, protection, address in and out), which ps4sdk, ps5-jar-loader and the call sites in RetroArch PPSA99169, XPSemu PPSA97358 and VLC PPSA85300 match; SharpProspero declares the alias as returning the descriptor. The object is a host anonymous file shared by its descriptors (a `memfd` on Linux, a delete-on-close temporary file mapped as an executable section on Windows), the alias is a duplicate descriptor, and every map maps the whole object, so writes through one view show through the others; a non-null address is a hint, as in `mmap` without `MAP_FIXED`. Not measured on the console: a length that is zero or not a multiple of 16 KiB and protection bits other than CPU read, write and execute are `EINVAL`; an alias or a map asking for more than the descriptor's maximum protection (write and execute counting as read) is `EACCES`, as FreeBSD 11's `vm_map_protect` treats a protection past `max_protection`; a descriptor that is not a JIT object is `EBADF`. The name is ignored, the objects do not count against the flexible memory budget, `mprotect` of a view is not limited to the maximum protection, and `mmap` of the descriptors maps the object as a [regular file](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp), so its views and the JIT views see each other's writes. On Linux the `memfd` is created without `MFD_EXEC`, which Linux 6.3 added and older kernels reject with `EINVAL`, and relies on the protection of the mapping: the flag only sets the file's execute permission bits, and the direct memory `memfd` is mapped executable the same way; a host with `vm.memfd_noexec` set to 2 refuses the creation and the call throws -- [kqueue and kevent](../../../core/libs/prx/libkernel/Equeue/src/Kqueue.cpp) (libkernel) follow FreeBSD 11's [`kern_event.c`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/kern_event.c) (`kqueue_kevent`, `kqueue_register`, `kqueue_scan`, the timer and user filters) over the event queue; nothing is measured on a console. The descriptor is a host null device registered as the kqueue: `close` and `sceKernelClose` close the queue and `dup`/`dup2` of it throw, but `read`, `write` and `fstat` act on the null device instead of failing with `EOPNOTSUPP` or reporting `S_IFIFO`. Read and write events are supported on guest sockets only, plus `EVFILT_READ` on the read end of a pipe (see the kevent on a pipe entry) and the timer, user and [vnode](../../../core/libs/prx/libkernel/Equeue/src/FileEvent.cpp) filters and a read event on the host stream on descriptor 0, which never triggers, as `select` never reports descriptor 0 readable; PPSA34015 registers descriptor 0 for reading in a new kqueue at start-up and does not check the result; other filters, `EV_CLEAR` on a socket or a pipe off Linux (see the kevent EV_CLEAR entry), `EVFILT_READ` on another kqueue, a `NOTE_NSECONDS` period above one second with a fraction (11.0 converts the fraction with the microsecond macro) and any change to an existing timer (11.0 keeps the armed expiry and applies the new period after it, 11.4's `filt_timertouch` restarts it) throw. `EV_DISABLE`, `EV_DISPATCH` and `EV_FORCEONESHOT` follow kern_event.c: a disabled knote stays registered, stops polling or firing and is not reported, a trigger raised while it is disabled is kept and reported after `EV_ENABLE`, where `knote` would drop it, `EV_DISPATCH` reports once and then disables the knote, and `EV_FORCEONESHOT` is ignored on a new knote while on an existing one it sets `EV_ONESHOT` and fires the knote; a negative `nevents` fails with `EINVAL`. A new user event keeps its `fflags` unmasked and is not triggered by `NOTE_TRIGGER`, because `filt_userattach` reads the already cleared `kn_fflags`. The timer limit `kq_calloutmax` (`ENOMEM`) is not enforced, and closing a kqueue wakes a thread waiting on it in `kevent` with `EBADF`, where `kqueue_drain` makes the close wait for that thread. A level-triggered event is reported once per call and moved behind the other events, as in `kqueue_scan`; the `sceKernel*Equeue` calls keep their own collection loop. `NOTE_LOWAT` on a socket sets the low-water mark as FreeBSD 11's [`filt_soread` and `filt_sowrite`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/uipc_socket.c) do, and a later change of the knote replaces it as `kqueue_register` replaces `kn_sdata`: a read event fires once the buffered bytes reach the mark, or at end of file or on a listening socket. A read mark above 1 is taken on stream sockets only: on a datagram socket Linux's `FIONREAD` reports the next datagram where FreeBSD's `sbavail` counts every queued byte, so it throws. A write event takes a mark of 1 only, where the host's writability stands in for one free byte, since the host does not report the send buffer's free space; other write marks and marks below 1, which make FreeBSD's read filter always fire, throw +- [kqueue and kevent](../../../core/libs/prx/libkernel/Equeue/src/Kqueue.cpp) (libkernel) follow FreeBSD 11's [`kern_event.c`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/kern_event.c) (`kqueue_kevent`, `kqueue_register`, `kqueue_scan`, the timer and user filters) over the event queue; nothing is measured on a console. The descriptor is a host null device registered as the kqueue: `close` and `sceKernelClose` close the queue and `dup`/`dup2` of it throw, but `read`, `write` and `fstat` act on the null device instead of failing with `EOPNOTSUPP` or reporting `S_IFIFO`. Read and write events are supported on guest sockets only, plus `EVFILT_READ` on the read end of a pipe (see the kevent on a pipe entry) and the timer, user and [vnode](../../../core/libs/prx/libkernel/Equeue/src/FileEvent.cpp) filters and a read event on the host stream on descriptor 0, which never triggers, as `select` never reports descriptor 0 readable; PPSA34015 registers descriptor 0 for reading in a new kqueue at start-up and does not check the result; other filters, `EV_CLEAR` on a socket or a pipe off Linux (see the kevent EV_CLEAR entry), a `NOTE_NSECONDS` period above one second with a fraction (11.0 converts the fraction with the microsecond macro) and any change to an existing timer (11.0 keeps the armed expiry and applies the new period after it, 11.4's `filt_timertouch` restarts it) throw. `EV_DISABLE`, `EV_DISPATCH` and `EV_FORCEONESHOT` follow kern_event.c: a disabled knote stays registered, stops polling or firing and is not reported, a trigger raised while it is disabled is kept and reported after `EV_ENABLE`, where `knote` would drop it, `EV_DISPATCH` reports once and then disables the knote, and `EV_FORCEONESHOT` is ignored on a new knote while on an existing one it sets `EV_ONESHOT` and fires the knote; a negative `nevents` fails with `EINVAL`. A new user event keeps its `fflags` unmasked and is not triggered by `NOTE_TRIGGER`, because `filt_userattach` reads the already cleared `kn_fflags`. The timer limit `kq_calloutmax` (`ENOMEM`) is not enforced, and closing a kqueue wakes a thread waiting on it in `kevent` with `EBADF`, where `kqueue_drain` makes the close wait for that thread. A level-triggered event is reported once per call and moved behind the other events, as in `kqueue_scan`; the `sceKernel*Equeue` calls keep their own collection loop. `NOTE_LOWAT` on a socket sets the low-water mark as FreeBSD 11's [`filt_soread` and `filt_sowrite`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/uipc_socket.c) do, and a later change of the knote replaces it as `kqueue_register` replaces `kn_sdata`: a read event fires once the buffered bytes reach the mark, or at end of file or on a listening socket. A read mark above 1 is taken on stream sockets only: on a datagram socket Linux's `FIONREAD` reports the next datagram where FreeBSD's `sbavail` counts every queued byte, so it throws. A write event takes a mark of 1 only, where the host's writability stands in for one free byte, since the host does not report the send buffer's free space; other write marks and marks below 1, which make FreeBSD's read filter always fire, throw - [EVFILT_VNODE, sceKernelAddFileEvent and sceKernelDeleteFileEvent](../../../core/libs/prx/libkernel/Equeue/src/FileEvent.cpp) (libkernel) watch the host file or directory behind the descriptor and turn host changes into FreeBSD 11 vnode hints, which [`filt_vfsvnode`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/vfs_subr.c#L5032) adds whole when they share a bit with the requested `fflags`: a write that grows a file is `NOTE_WRITE | NOTE_EXTEND`, another write `NOTE_WRITE`, a shrinking truncate or an attribute change `NOTE_ATTRIB`, a higher link count `NOTE_LINK`, a lower one or a pending delete `NOTE_DELETE`, a new path `NOTE_RENAME`; in a directory, an entry created, removed or renamed is `NOTE_WRITE`, with `NOTE_LINK` for a subdirectory, and on Linux an entry moved from or to another directory adds `NOTE_EXTEND`; inotify queues the two halves of a rename one after the other, so a watcher woken by the first can read it alone, and a half read without the other is classified only after a `getdents64` on the watched directory, which takes the directory lock that `rename` holds until both halves are queued ([`do_renameat2`](https://github.com/torvalds/linux/blob/v6.8/fs/namei.c#L4975) around `vfs_rename` and its `fsnotify_move`, [`iterate_dir`](https://github.com/torvalds/linux/blob/v6.8/fs/readdir.c#L103)), an ordering taken from the kernel source, not from inotify(7), which promises none; a directory registered with `NOTE_EXTEND` on Windows is accepted, as `vfs_kqfilter` does not check it, but never receives it, because the directory handle cannot tell a move between directories from a removal plus an addition. FreeBSD's hints depend on the filesystem and the console's filesystems are not known: a growing `ftruncate` reports `NOTE_WRITE | NOTE_EXTEND` where `vop_setattr_post` reports `NOTE_ATTRIB`, and on Windows `utimes` on a file reports `NOTE_WRITE`. The three Windows notifications are told apart in the error, which names the `parent`, `entries` or `subdirectories` watch, the path it was given and `GetLastError`, but a `FindFirstChangeNotification` that fails still throws rather than returning an error to the guest: the EVFILT_VNODE attach path is [`vfs_kqfilter`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/vfs_subr.c#L4936), which returns `EINVAL`, `ENOMEM` or 0 after `fget` has turned a descriptor that is not open into `EBADF`, and consults no directory permission, so no FreeBSD errno stands for a host that refuses to notify, and what the console does on such a host is not measured. Only the `parent` watch can fail this way in practice: `entries` and `subdirectories` need the `FILE_LIST_DIRECTORY` that opening the watched directory already took. The parent handle produces no hint of its own, as `RunHostWatch` only calls `FindNextChangeNotification` on it, so a later change may leave the watch on its 50 ms poll instead of throwing; that is not done here. Windows uses `FindFirstChangeNotification` on the parent directory (and on a watched directory itself) and compares `GetFileInformationByHandleEx` snapshots at least every 50 ms, because a write through an open handle is reported only when the handle is flushed or closed, so file hints can arrive 50 ms late, and a file deleted while watched, which the POSIX delete of Windows 10 1903 moves into `$Extend\$Deleted` until its last handle closes, reports `NOTE_DELETE` without `NOTE_RENAME` and keeps its handle without a parent directory watch, as that directory cannot be watched; Linux uses inotify on the file and is not run. `NOTE_REVOKE`, `NOTE_OPEN`, `NOTE_CLOSE`, `NOTE_CLOSE_WRITE`, `NOTE_READ`, unknown bits and descriptors other than files, directories, pipes and sockets throw. `sceKernelAddFileEvent(eq, fd, watch, udata)` and `sceKernelDeleteFileEvent(eq, fd)` take the [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9d/include/orbis/libkernel.h#L35) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0/src/SharpProspero/Interop/Kernel/KernelEqueue.cs#L164) signatures; `watch` is taken as the `fflags` and the event is added with `EV_CLEAR`, neither confirmed on a title (feat(libkernel): implement kevent EV_DISABLE, EV_DISPATCH and EV_FORCEONESHOT) (fix(libkernel): follow kqueue_register for EV_FORCEONESHOT and modify flags) - [sem_timedwait](../../../core/libs/prx/libkernel/Pthread/Posix/Sem.cpp) (libkernel) consumes an available count before checking the timeout and waits indefinitely for a null timeout, following FreeBSD 11's `sem_timedwait`. Console behavior for null and invalid timeouts when a count is available is unverified. - [tcsendbreak, tcdrain, tcflush, tcflow, tcgetpgrp, tcsetpgrp](../../../core/libs/prx/libkernel/File/src/Terminal.cpp) (libkernel) follow FreeBSD 11.4's [`termios.c`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/termios.c): `tcflush` answers `EINVAL` for a queue other than `TCIFLUSH`, `TCOFLUSH` or `TCIOFLUSH` and `tcflow` for an action outside `TCOOFF`..`TCION` before the descriptor is looked at, and every other call fails as its terminal ioctl does on a descriptor that is not a terminal, with the errors `tcgetattr` gives, so `tcsendbreak` never reaches its 0.4 s pause. FreeBSD's `pipe_ioctl` answers `TIOCGPGRP` and `TIOCSPGRP` on a pipe or FIFO with the descriptor's SIGIO owner, which is not modelled, so `tcgetpgrp` and `tcsetpgrp` throw on a host pipe or FIFO above descriptor 2; descriptors 0 to 2 are taken as non-terminals that are not pipes, whatever the host attaches to them, so a pipe the guest creates on a freed descriptor 0 to 2 gets `ENOTTY` there. The PS5 `libkernel` is assumed to use this FreeBSD code, and `tcdrain` is not a cancellation point here @@ -187,6 +187,7 @@ - [poll](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) waits on guest sockets and pipes, through `WSAPoll` on Windows and `poll` on Linux; the `pollfd` layout and flag values are FreeBSD's. A libSceNet socket joins the same host wait through the native socket libSceNet lends to libkernel. `WSAPoll` rejects `POLLPRI`, so on Windows a socket's `POLLPRI` is asked as `POLLRDBAND` and `POLLIN` as `POLLRDNORM`; Winsock reports urgent data until it is read with `MSG_OOB`, where FreeBSD 11.4's `sopoll_generic` stops once the normal stream passes the mark. Guest `send` and `recv` refuse `MSG_OOB` with `EOPNOTSUPP`, so urgent data can only come from an outside peer and Windows keeps reporting `POLLPRI` after the stream passes the mark. A pipe follows FreeBSD's `pipe_poll`: the read end is readable when data is buffered, either end reports `POLLHUP` once the other end is closed, with the requested `POLLIN` and `POLLRDNORM` bits and without `POLLOUT`, which `pollscan` clears, and the write end is writable when `PIPE_BUF` (512) bytes are free on Windows, or when the host kernel says so on Linux. `POLLOUT` on a read end throws: FreeBSD opens both ends for reading and writing and reports it writable, and host pipes are one-way. Windows pipes cannot be waited on, so while a pipe is polled `poll` checks it with `NtQueryInformationFile` and waits on the sockets in slices of 1 ms. A regular file, a directory or a file read from app0.zar is always ready for `POLLIN`, `POLLRDNORM` and `POLLOUT` and never for `POLLPRI`, `POLLRDBAND` or `POLLWRBAND`, as FreeBSD 11.4's [`vop_stdpoll`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/vfs_default.c#L563-L574) and `vop_nopoll` answer every event `poll` accepts; which of the two the console's filesystems use is not known, and they agree on these events. `/dev/random` and `/dev/urandom` follow FreeBSD 11.4's [`randomdev_poll`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/dev/random/randomdev.c#L274-L285) for a seeded device: a request with `POLLIN` or `POLLRDNORM` gets only those bits back, and any other request gets every requested bit. Open descriptors 0 to 2 follow the console model `select` uses: descriptor 0 never reports an event and descriptors 1 and 2 report `POLLOUT`, whatever the host attaches to them. A closed descriptor reports `POLLNVAL`, and an open descriptor that is neither a guest socket, a libSceNet socket, a pipe, a file, a directory, the random device nor descriptor 0 to 2 (other devices, a kqueue) throws, as do event bits other than `POLLIN`, `POLLPRI`, `POLLOUT`, `POLLRDNORM`, `POLLRDBAND` and `POLLWRBAND` (the `POLLERR`, `POLLHUP` and `POLLNVAL` bits are ignored in `events`), an infinite timeout with no socket or pipe to wait on. A closed socket descriptor reports `POLLNVAL`; the console's limit on the descriptor count is unknown and not checked - [kevent EV_CLEAR on a socket or a pipe](../../../core/libs/prx/libkernel/Equeue/src/EdgeWatch.cpp) (libkernel) - FreeBSD 11 activates an `EV_CLEAR` knote when `kqueue_register` finds it ready, which every `EV_ADD` or `EV_ENABLE` of an enabled knote does again, and on each `KNOTE` the socket or pipe raises (`sorwakeup`, `sowwakeup`, `pipeselwakeup`); [`kqueue_scan`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/kern_event.c) checks the filter again before reporting, drops a knote that is no longer ready and deactivates a reported one until the next activation. On Linux each such knote registers the host socket or pipe with its own epoll instance in edge-triggered mode (`EPOLLIN | EPOLLRDHUP` for `EVFILT_READ`, `EPOLLOUT` for `EVFILT_WRITE`), and a wakeup of the host descriptor with that mask stands for a `KNOTE`: the knote is then checked with the same readiness test as a level-triggered one, `NOTE_LOWAT` mark included, and a wait blocks on the epoll descriptor instead of the socket, so a socket that stays readable does not wake it. The host's wakeups are not FreeBSD's: FreeBSD raises `sowwakeup` whenever send buffer space is freed, while Linux TCP wakes `EPOLLOUT` waiters only once the socket is writable again after a write found it full (`SOCK_NOSPACE`, [`tcp_check_space`](https://github.com/torvalds/linux/blob/v6.8/net/ipv4/tcp_input.c#L5639)), so a TCP writer that never fills its buffer sees fewer `EVFILT_WRITE` events than on FreeBSD; a host stack may also coalesce data wakeups, so the number of times a busy socket fires can differ. Windows and macOS throw, as the host there has no edge-triggered wait over these descriptors that is wired up; `EV_CLEAR` on the timer, user and vnode filters does not use this and works everywhere. - [kevent on a pipe](../../../core/libs/prx/libkernel/Equeue/src/Kqueue.cpp) (libkernel) - `EVFILT_READ` on the read end of a pipe follows FreeBSD 11's [`filt_piperead`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/sys_pipe.c#L1787-L1806): `data` is the number of buffered bytes, and `EV_EOF` is set once the write end is closed. Windows pipes cannot be waited on, so while a kqueue waits on a pipe it checks it with `NtQueryInformationFile` and waits on its sockets in slices of 1 ms; a wait with no pipe is not sliced. `EVFILT_READ` on the write end of a pipe throws: FreeBSD's `pipe` opens both ends for reading and writing ([`kern_pipe`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/sys_pipe.c#L422)), and host pipes are one-way. `EVFILT_WRITE` on a pipe throws too, and `NOTE_LOWAT` on a pipe is ignored, as `filt_piperead` ignores it. +- [kevent on a kqueue](../../../core/libs/prx/libkernel/Equeue/src/Kqueue.cpp) (libkernel) - `EVFILT_READ` on a kqueue counts the watched queue's enabled triggered notes, where [`filt_kqueue`](https://github.com/freebsd/freebsd-src/blob/5ed7eb0d97ba4436218e810f61bd059acba984c2/sys/kern/kern_event.c#L363-L370) reports `kq_count`, which keeps a note disabled after it was queued until the next scan; an `EV_ONESHOT` note reports the count of its last evaluation there, the current one here. `EV_CLEAR` throws: FreeBSD re-activates the note only when the watched kqueue queues one (`knote_enqueue`, [L2534-L2546](https://github.com/freebsd/freebsd-src/blob/5ed7eb0d97ba4436218e810f61bd059acba984c2/sys/kern/kern_event.c#L2534-L2546)), which is not tracked. A kqueue that already watches the one being registered on throws; only a note on itself is `EINVAL`. Closing the watched kqueue drops the note, as `knote_fdclose` does. Not measured on a console. - [Socket error numbers](../../../core/libs/prx/libkernel/Socket/include/SocketError.hpp) (libkernel) - a host socket error becomes the FreeBSD 11 `errno.h` number of the same name; the numbers are not checked on the console. Winsock `WSAESHUTDOWN` becomes `EPIPE`, as a send after `shutdown` does on FreeBSD, and a host error with no FreeBSD socket counterpart becomes `EIO`. A nonblocking `connect` in progress reports `EINPROGRESS` on Linux but `EAGAIN` on Windows (`WSAEWOULDBLOCK`), where FreeBSD reports `EINPROGRESS`. On Windows a refused nonblocking `connect` is never reported (`SO_ERROR` is unsupported), so a repeated one keeps reporting `EALREADY` where FreeBSD reports `ECONNREFUSED`. A receive after `shutdown(SHUT_RD)` reports `EPIPE` on Windows where FreeBSD returns 0 - [getsockopt, setsockopt](../../../core/libs/prx/libkernel/Socket/src/SocketOptions.cpp) (libkernel) on a guest socket handle the SOL_SOCKET level, `TCP_NODELAY` and `IPV6_V6ONLY`, after FreeBSD 11.4's [`sosetopt` and `sogetopt`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c): `SO_REUSEADDR`, `SO_KEEPALIVE`, `SO_DONTROUTE`, `SO_BROADCAST`, `SO_OOBINLINE` and `SO_LINGER` act on the host socket, and `SO_ERROR`, `SO_TYPE`, `SO_ACCEPTCONN`, `SO_SNDBUF` and `SO_RCVBUF` are read. A boolean option reads back as its own bit, as FreeBSD returns `so_options & option`. Winsock does not clear a socket's pending error when it is read, so on Windows the library reports a pending error once and shows it again only after a new `connect`; a second failure with the same code on the same socket without a `connect` in between would be hidden. `SO_SNDBUF` and `SO_RCVBUF` read the host's value, which is not the size FreeBSD reports (Linux doubles it). `SO_NOSIGPIPE` is kept per socket and reads back as its bit. On Linux a `send`, `sendto`, `sendmsg` or `write` on that socket passes `MSG_NOSIGNAL` to the host, as [`kern_sendit`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_syscalls.c#L890-L895) and [`soo_write`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_socket.c#L147-L151) leave out the signal. An accepted socket takes it from its listener, as [`sonewconn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L603) copies the options. `SO_NBIO` (`0x1200`) is the console's own option, not FreeBSD's. It switches the socket between blocking and non-blocking as `FIONBIO` does and reads back as 0 or 1, as `sceNetSetsockopt` and `sceNetGetsockopt` treat it here; what the console reads back for it is not measured. `TCP_NODELAY` (`IPPROTO_TCP`, 1) is set on the host socket and reads back as `TF_NODELAY` (4), as [`tcp_default_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet/tcp_usrreq.c#L1860-L1861) returns it. The `IPPROTO_TCP` level on a datagram or `AF_UNIX` socket fails with `EINVAL`, as [`ip_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet/ip_output.c#L943-L944) and [`uipc_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_usrreq.c#L1228-L1229) answer it, and an accepted socket takes `TCP_NODELAY` through the host. `SO_SNDTIMEO` and `SO_RCVTIMEO` follow [`sosetopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L2657-L2689) and [`sogetopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L2846-L2850). A 16-byte `timeval` with a negative field or 1000000 microseconds or more fails with `EDOM`, and more than `INT32_MAX` seconds becomes the longest wait. The value is kept and read back through FreeBSD's 32.32 fixed-point conversion ([`tvtosbt`, `sbttotv`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/sys/time.h#L178-L286)), so half a second reads back as 499999 microseconds and one microsecond as 0. A `recv`, `recvfrom`, `recvmsg`, `read` or `readv`, and a `send`, `sendto`, `sendmsg`, `write` or `writev`, on a blocking socket then waits that long for the host to report the socket readable or writable, and fails with `EWOULDBLOCK`; a call with `MSG_DONTWAIT` does not wait. An accepted socket takes both from its listener ([`sonewconn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L629-L630)). The wait covers only the first transfer: once the socket is writable the host send runs without a limit, where FreeBSD stops a send that cannot finish and returns the bytes sent so far, and a receive whose data another thread takes between the wait and the host call blocks again. `sendfile` ignores the send timeout. The host wait has millisecond steps, so a shorter timeout lasts up to a millisecond longer. A null value fails with `EINVAL`, where FreeBSD's [`kern_setsockopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_syscalls.c#L1414-L1415) returns `EFAULT`. `IPV6_V6ONLY` (`IPPROTO_IPV6`, 27) on an `AF_INET6` socket is set on the host socket and reads back as 0 or 1, after FreeBSD's [`ip6_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet6/ip6_output.c#L1716-L1732): a value that is not exactly an `int` fails with `EINVAL` ([L1572](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet6/ip6_output.c#L1572)), as does setting it on a bound socket, which the host refuses too. `socket` turns it on for every `AF_INET6` socket, as FreeBSD's default `net.inet6.ip6.v6only` does, and Java turns it off on the sockets it opens: Java 25 Advanced Network (PPSA99871) stops at `java.net.SocketException: Protocol not available` without it. The `IPPROTO_IPV6` level on an `AF_INET` or `AF_UNIX` socket fails with `EINVAL`, as `ip_ctloutput` and `uipc_ctloutput` answer it. Every other SOL_SOCKET option (`SO_DEBUG`, `SO_USELOOPBACK`, `SO_REUSEPORT` and `SO_TIMESTAMP`, which FreeBSD stores, the low-water marks among them; PPSA99871 sets `SO_REUSEPORT` and carries on when it fails), every other `IPPROTO_TCP` option (`TCP_MAXSEG`, `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT` among them), every other `IPPROTO_IPV6` option ([L1928-L1929](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet6/ip6_output.c#L1928-L1929), [L2158-L2159](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet6/ip6_output.c#L2158-L2159)) and the `IPPROTO_IP` level fail with `ENOPROTOOPT`. None of this was measured on a console. - [connect to AF_UNSPEC](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) dissolves a datagram association as FreeBSD 11.4's `udp_disconnect()` does, which resets the local address to the wildcard address and keeps the port. On Linux a socket bound to an explicit port keeps its local address as well (`127.0.0.1:` stays `127.0.0.1:`), because a bound socket cannot be bound again; one bound to port 0 is bound again to the wildcard address with the port it had From 89779222442c810ff4d8fd0412f2d6dd584479ea Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:28:03 +0200 Subject: [PATCH 069/523] fix(libkernel): drop undefined protection bits in mprotect FreeBSD 11.4 kern_mprotect masks the argument with VM_PROT_ALL instead of refusing it (sys/vm/vm_mmap.c:613). sceKernelMprotect already does this since #5770; the POSIX mprotect export still failed with EINVAL. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp | 3 ++- core/libs/tests/GuestMemory.cpp | 12 ++++++++++++ docs/dev/debt/libkernel.md | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp b/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp index 7c69af40fa..22d30d3596 100644 --- a/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp +++ b/core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp @@ -53,6 +53,7 @@ constexpr int GuestAdviceCore = 9; constexpr int GuestAdviceProtect = 10; constexpr int GuestLockCurrent = 0x1; constexpr int GuestLockFuture = 0x2; +constexpr int GuestDefinedProtection = 0x3F7; constexpr std::uintptr_t GuestUserAddressEnd = 0x800000000000; constexpr int GuestAlignedSuper = 0x1000000; constexpr std::size_t SuperpageSize = std::size_t{1} << 21; @@ -279,7 +280,7 @@ int APS5_VABI mprotect_nid_postfix(void* address, std::size_t length, int protec if (start == 0 || length > std::numeric_limits::max() - start) return failed(GuestInvalid); try { - if (DoMprotect(address, length, protection) != 0) return failed(GuestInvalid); + if (DoMprotect(address, length, protection & GuestDefinedProtection) != 0) return failed(GuestInvalid); return 0; } catch (const std::bad_alloc&) { return failed(GuestNoMemory); diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 37501693fb..7eea938933 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -287,6 +287,17 @@ static void CheckStrictMemoryProtectionBits() { Require(sceKernelReleaseDirectMemory(physical, length) == 0); } +static void CheckPosixMprotectIgnoresUndefinedBits() { + constexpr std::size_t length = 0x4000; + auto* mapping = static_cast(mmap_nid_postfix(nullptr, length, 3, 0x1002, -1, 0)); + Require(mapping != reinterpret_cast(static_cast(-1))); + Require(mprotect_nid_postfix(const_cast(mapping), length, static_cast(0xfffffff2u)) == 0); + mapping[length - 1] = 5; + Require(mapping[length - 1] == 5); + Require(mprotect_nid_postfix(const_cast(mapping), length, 0x8 | 0x3) == 0); + Require(munmap_nid_postfix(const_cast(mapping), length) == 0); +} + static void CheckInternalNamedFlexibleMapping() { constexpr std::size_t length = 0x10000; std::size_t before = 0; @@ -2971,6 +2982,7 @@ int main(int argc, char** argv) { CheckAudioCoprocessorProtection(); CheckMprotectIgnoresUndefinedBits(); CheckStrictMemoryProtectionBits(); + CheckPosixMprotectIgnoresUndefinedBits(); CheckDirectMemoryFollowsPhysicalPages(); CheckSparseDirectMemoryBacking(); CheckReleaseDirectMemoryClearsMappings(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index c27dde1e57..060ebea064 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -112,7 +112,7 @@ - [Guest thread scheduling](../../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) - the policies and priorities that `sched_get_priority_max`, `sched_get_priority_min`, `pthread_setschedparam` and `scePthreadSetschedparam` accept, and those a created thread takes, follow a PS5 (firmware 13.60), but they are only recorded and reported: they are not applied to host threads, so a title's priority order has no effect on host scheduling - [sceKernelMprotect, sceKernelMtypeprotect](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) over a range with a gap that is not registered in the allocation tracker protect the registered parts and skip the gap, and return 0, as FreeBSD 11.4's [`vm_map_protect`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/vm/vm_map.c) does (it has no check for holes); a range whose start is unmapped (also when later pages are mapped) returns `SCE_KERNEL_ERROR_EINVAL` (`mprotect`: `EINVAL`) because an existing test pins that for an unmapped start; FreeBSD answers 0 there too (`vm_map_protect` takes the next entry), so this errno is not taken from the source and was not changed. `sceKernelMprotect` threw for a gap before, which ended the title. Every part is checked for a GPU or image pin before the first one is protected; the parts are then protected one after the other, so a host protection failure in a later part leaves the earlier parts changed (FreeBSD's `vm_map_protect` cannot fail in its second pass). The behaviour on the console is not known. None of this was measured on a console. -- [sceKernelMprotect protection bits](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - bits outside `0x3F7` are dropped instead of refused, as a PS5 (firmware 13.60) drops them: Onimusha: Way of the Sword (PPSA27836 01.000.001) maps direct memory with `0xf2` and then protects it with `0xfffffff2`, which the console reports as `0xf3` and then `0x3f3`. In a payload process the console also drops the CPU execute bit (`0x4`) of flexible, direct and JIT memory, and POSIX `mprotect` with write and execute reports write only (`0x2`). A title process may keep execute, so it is kept here. The map calls and `sceKernelMtypeprotect` refuse undefined bits on the console with `EINVAL` (`0x80020016`), and the direct map calls refuse execute; here both throw for undefined bits, and the direct map calls map execute +- [sceKernelMprotect protection bits](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - bits outside `0x3F7` are dropped instead of refused, in the POSIX `mprotect` as well (FreeBSD 11.4 masks the argument, `sys/vm/vm_mmap.c:613`; the console was measured only through `sceKernelMprotect`), as a PS5 (firmware 13.60) drops them: Onimusha: Way of the Sword (PPSA27836 01.000.001) maps direct memory with `0xf2` and then protects it with `0xfffffff2`, which the console reports as `0xf3` and then `0x3f3`. In a payload process the console also drops the CPU execute bit (`0x4`) of flexible, direct and JIT memory, and POSIX `mprotect` with write and execute reports write only (`0x2`). A title process may keep execute, so it is kept here. The map calls and `sceKernelMtypeprotect` refuse undefined bits on the console with `EINVAL` (`0x80020016`), and the direct map calls refuse execute; here both throw for undefined bits, and the direct map calls map execute - [JIT shared memory](../../../core/libs/prx/libkernel/DirectMemory/Jit.cpp) (libkernel) - `sceKernelJitCreateSharedMemory`, `sceKernelJitCreateAliasOfSharedMemory` and `sceKernelJitMapSharedMemory` take the signatures of [Orbital](https://github.com/AlexAltea/orbital)'s `tools/ida/db_types.json` (name, length, maximum protection, descriptor out; descriptor, maximum protection, descriptor out; descriptor, protection, address in and out), which ps4sdk, ps5-jar-loader and the call sites in RetroArch PPSA99169, XPSemu PPSA97358 and VLC PPSA85300 match; SharpProspero declares the alias as returning the descriptor. The object is a host anonymous file shared by its descriptors (a `memfd` on Linux, a delete-on-close temporary file mapped as an executable section on Windows), the alias is a duplicate descriptor, and every map maps the whole object, so writes through one view show through the others; a non-null address is a hint, as in `mmap` without `MAP_FIXED`. Not measured on the console: a length that is zero or not a multiple of 16 KiB and protection bits other than CPU read, write and execute are `EINVAL`; an alias or a map asking for more than the descriptor's maximum protection (write and execute counting as read) is `EACCES`, as FreeBSD 11's `vm_map_protect` treats a protection past `max_protection`; a descriptor that is not a JIT object is `EBADF`. The name is ignored, the objects do not count against the flexible memory budget, `mprotect` of a view is not limited to the maximum protection, and `mmap` of the descriptors maps the object as a [regular file](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp), so its views and the JIT views see each other's writes. On Linux the `memfd` is created without `MFD_EXEC`, which Linux 6.3 added and older kernels reject with `EINVAL`, and relies on the protection of the mapping: the flag only sets the file's execute permission bits, and the direct memory `memfd` is mapped executable the same way; a host with `vm.memfd_noexec` set to 2 refuses the creation and the call throws - [kqueue and kevent](../../../core/libs/prx/libkernel/Equeue/src/Kqueue.cpp) (libkernel) follow FreeBSD 11's [`kern_event.c`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/kern_event.c) (`kqueue_kevent`, `kqueue_register`, `kqueue_scan`, the timer and user filters) over the event queue; nothing is measured on a console. The descriptor is a host null device registered as the kqueue: `close` and `sceKernelClose` close the queue and `dup`/`dup2` of it throw, but `read`, `write` and `fstat` act on the null device instead of failing with `EOPNOTSUPP` or reporting `S_IFIFO`. Read and write events are supported on guest sockets only, plus `EVFILT_READ` on the read end and `EVFILT_WRITE` on the write end of a pipe (see the kevent on a pipe entry) and the timer, user and [vnode](../../../core/libs/prx/libkernel/Equeue/src/FileEvent.cpp) filters and a read event on the host stream on descriptor 0, which never triggers, as `select` never reports descriptor 0 readable; PPSA34015 registers descriptor 0 for reading in a new kqueue at start-up and does not check the result; other filters, `EV_CLEAR` on a socket or a pipe off Linux (see the kevent EV_CLEAR entry), `EVFILT_READ` on another kqueue, a `NOTE_NSECONDS` period above one second with a fraction (11.0 converts the fraction with the microsecond macro) and any change to an existing timer (11.0 keeps the armed expiry and applies the new period after it, 11.4's `filt_timertouch` restarts it) throw. `EV_DISABLE`, `EV_DISPATCH` and `EV_FORCEONESHOT` follow kern_event.c: a disabled knote stays registered, stops polling or firing and is not reported, a trigger raised while it is disabled is kept and reported after `EV_ENABLE`, where `knote` would drop it, `EV_DISPATCH` reports once and then disables the knote, and `EV_FORCEONESHOT` is ignored on a new knote while on an existing one it sets `EV_ONESHOT` and fires the knote; a negative `nevents` fails with `EINVAL`. A new user event keeps its `fflags` unmasked and is not triggered by `NOTE_TRIGGER`, because `filt_userattach` reads the already cleared `kn_fflags`. The timer limit `kq_calloutmax` (`ENOMEM`) is not enforced, and closing a kqueue wakes a thread waiting on it in `kevent` with `EBADF`, where `kqueue_drain` makes the close wait for that thread. A level-triggered event is reported once per call and moved behind the other events, as in `kqueue_scan`; the `sceKernel*Equeue` calls keep their own collection loop. `NOTE_LOWAT` on a socket sets the low-water mark as FreeBSD 11's [`filt_soread` and `filt_sowrite`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/uipc_socket.c) do, and a later change of the knote replaces it as `kqueue_register` replaces `kn_sdata`: a read event fires once the buffered bytes reach the mark, or at end of file or on a listening socket. A read mark above 1 is taken on stream sockets only: on a datagram socket Linux's `FIONREAD` reports the next datagram where FreeBSD's `sbavail` counts every queued byte, so it throws. A write event takes a mark of 1 only, where the host's writability stands in for one free byte, since the host does not report the send buffer's free space; other write marks and marks below 1, which make FreeBSD's read filter always fire, throw - [EVFILT_VNODE, sceKernelAddFileEvent and sceKernelDeleteFileEvent](../../../core/libs/prx/libkernel/Equeue/src/FileEvent.cpp) (libkernel) watch the host file or directory behind the descriptor and turn host changes into FreeBSD 11 vnode hints, which [`filt_vfsvnode`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/vfs_subr.c#L5032) adds whole when they share a bit with the requested `fflags`: a write that grows a file is `NOTE_WRITE | NOTE_EXTEND`, another write `NOTE_WRITE`, a shrinking truncate or an attribute change `NOTE_ATTRIB`, a higher link count `NOTE_LINK`, a lower one or a pending delete `NOTE_DELETE`, a new path `NOTE_RENAME`; in a directory, an entry created, removed or renamed is `NOTE_WRITE`, with `NOTE_LINK` for a subdirectory, and on Linux an entry moved from or to another directory adds `NOTE_EXTEND`; inotify queues the two halves of a rename one after the other, so a watcher woken by the first can read it alone, and a half read without the other is classified only after a `getdents64` on the watched directory, which takes the directory lock that `rename` holds until both halves are queued ([`do_renameat2`](https://github.com/torvalds/linux/blob/v6.8/fs/namei.c#L4975) around `vfs_rename` and its `fsnotify_move`, [`iterate_dir`](https://github.com/torvalds/linux/blob/v6.8/fs/readdir.c#L103)), an ordering taken from the kernel source, not from inotify(7), which promises none; a directory registered with `NOTE_EXTEND` on Windows is accepted, as `vfs_kqfilter` does not check it, but never receives it, because the directory handle cannot tell a move between directories from a removal plus an addition. FreeBSD's hints depend on the filesystem and the console's filesystems are not known: a growing `ftruncate` reports `NOTE_WRITE | NOTE_EXTEND` where `vop_setattr_post` reports `NOTE_ATTRIB`, and on Windows `utimes` on a file reports `NOTE_WRITE`. The three Windows notifications are told apart in the error, which names the `parent`, `entries` or `subdirectories` watch, the path it was given and `GetLastError`, but a `FindFirstChangeNotification` that fails still throws rather than returning an error to the guest: the EVFILT_VNODE attach path is [`vfs_kqfilter`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/vfs_subr.c#L4936), which returns `EINVAL`, `ENOMEM` or 0 after `fget` has turned a descriptor that is not open into `EBADF`, and consults no directory permission, so no FreeBSD errno stands for a host that refuses to notify, and what the console does on such a host is not measured. Only the `parent` watch can fail this way in practice: `entries` and `subdirectories` need the `FILE_LIST_DIRECTORY` that opening the watched directory already took. The parent handle produces no hint of its own, as `RunHostWatch` only calls `FindNextChangeNotification` on it, so a later change may leave the watch on its 50 ms poll instead of throwing; that is not done here. Windows uses `FindFirstChangeNotification` on the parent directory (and on a watched directory itself) and compares `GetFileInformationByHandleEx` snapshots at least every 50 ms, because a write through an open handle is reported only when the handle is flushed or closed, so file hints can arrive 50 ms late, and a file deleted while watched, which the POSIX delete of Windows 10 1903 moves into `$Extend\$Deleted` until its last handle closes, reports `NOTE_DELETE` without `NOTE_RENAME` and keeps its handle without a parent directory watch, as that directory cannot be watched; Linux uses inotify on the file and is not run. `NOTE_REVOKE`, `NOTE_OPEN`, `NOTE_CLOSE`, `NOTE_CLOSE_WRITE`, `NOTE_READ`, unknown bits and descriptors other than files, directories, pipes and sockets throw. `sceKernelAddFileEvent(eq, fd, watch, udata)` and `sceKernelDeleteFileEvent(eq, fd)` take the [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9d/include/orbis/libkernel.h#L35) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0/src/SharpProspero/Interop/Kernel/KernelEqueue.cs#L164) signatures; `watch` is taken as the `fflags` and the event is added with `EV_CLEAR`, neither confirmed on a title (feat(libkernel): implement kevent EV_DISABLE, EV_DISPATCH and EV_FORCEONESHOT) (fix(libkernel): follow kqueue_register for EV_FORCEONESHOT and modify flags) From 62e6ba74813ddf8d019efe894a644534e4de633c Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:25:32 +0200 Subject: [PATCH 070/523] fix(libkernel): clamp long nanosleep requests like FreeBSD instead of wrapping them kern_clock_nanosleep limits the sleep to INT32_MAX / 2 seconds and sleeps for that long (sys/kern/kern_time.c:543-547). The guest multiplied tv_sec by 1e9 in 64 bits, so a request of 18446744074 seconds or more wrapped to a short sleep: nanosleep with tv_sec = INT64_MAX returned after about 0.1 s. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/CMakeLists.txt | 10 ++++ core/libs/prx/libkernel/Time/Time.cpp | 3 +- core/libs/tests/GuestNanosleepLongRequest.cpp | 58 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 core/libs/tests/GuestNanosleepLongRequest.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index cea1629cec..31d96c7509 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -749,6 +749,16 @@ if(BUILD_TESTING) set_tests_properties(guest_sleep_wake guest_sleep_wake_coarse PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_nanosleep_long_request_tests ${LIBS_INCLUDE_DIR}/tests/GuestNanosleepLongRequest.cpp) + target_include_directories(guest_nanosleep_long_request_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_nanosleep_long_request_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_nanosleep_long_request_tests) + add_test(NAME guest_nanosleep_long_request COMMAND guest_nanosleep_long_request_tests) + set_tests_properties(guest_nanosleep_long_request PROPERTIES TIMEOUT 30) + if(WIN32) + set_tests_properties(guest_nanosleep_long_request PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_pthread_sem_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadSem.cpp) target_include_directories(guest_pthread_sem_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_pthread_sem_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/Time/Time.cpp b/core/libs/prx/libkernel/Time/Time.cpp index afd6d5d40e..ad5f6f4863 100644 --- a/core/libs/prx/libkernel/Time/Time.cpp +++ b/core/libs/prx/libkernel/Time/Time.cpp @@ -290,8 +290,9 @@ static int SleepForRequest(const KernelTimespec* rqtp) { constexpr int guestInvalid = 22; if (rqtp == nullptr) return guestFault; if (rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000LL) return guestInvalid; + constexpr std::int64_t longestSleepSeconds = INT32_MAX / 2; if (rqtp->tv_sec >= 0) { - CancellableSleep(static_cast(rqtp->tv_sec) * 1000000000ULL + + CancellableSleep(static_cast(std::min(static_cast(rqtp->tv_sec), longestSleepSeconds)) * 1000000000ULL + static_cast(rqtp->tv_nsec)); } return 0; diff --git a/core/libs/tests/GuestNanosleepLongRequest.cpp b/core/libs/tests/GuestNanosleepLongRequest.cpp new file mode 100644 index 0000000000..b2b6ec9cd6 --- /dev/null +++ b/core/libs/tests/GuestNanosleepLongRequest.cpp @@ -0,0 +1,58 @@ +#include "SceTypes.hpp" +#include "prx/libkernel/Pthread/include/Pthread.hpp" +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name); +int APS5_VABI nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec* rmtp); +int APS5_VABI sceKernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp); +} + +static void Require(bool value, std::source_location location = std::source_location::current()) { + if (value) return; + std::fprintf(stderr, "%s:%u: requirement failed\n", location.file_name(), location.line()); + std::abort(); +} + +static constexpr auto ObservationTime = std::chrono::milliseconds(1500); +static std::atomic started{false}; +static std::atomic returned{false}; +static std::int64_t requestedSeconds = 0; +static bool posix = true; + +static void* APS5_VABI Sleeper(void*) { + const KernelTimespec request{requestedSeconds, 0}; + started.store(true); + if (posix) nanosleep_nid_postfix(&request, nullptr); + else sceKernelNanosleep(&request, nullptr); + returned.store(true); + return nullptr; +} + +static void SleepsPastTheObservation(std::int64_t seconds, bool usePosix) { + started.store(false); + returned.store(false); + requestedSeconds = seconds; + posix = usePosix; + Pthread thread = nullptr; + Require(scePthreadCreate(&thread, nullptr, Sleeper, nullptr, nullptr) == 0); + while (!started.load()) std::this_thread::yield(); + std::this_thread::sleep_for(ObservationTime); + Require(!returned.load()); +} + +int main() { + for (const bool usePosix : {true, false}) { + SleepsPastTheObservation(INT64_MAX, usePosix); + SleepsPastTheObservation(18446744074LL, usePosix); + SleepsPastTheObservation(std::int64_t{1} << 40, usePosix); + } + std::fflush(nullptr); + std::_Exit(0); +} From aec6bba8c4b6d6e5ab0bc1f513b99b236d4052e0 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:40:42 +0000 Subject: [PATCH 071/523] test(agc): cover zero result for 16-bit color compares --- .../prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp index 1d96a16670..1b7b1c94aa 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp @@ -407,6 +407,7 @@ int main() { } } } + RunSrgbZero(*device, Format1616UNorm, {ClampEdge, FilterPoint}, "16_16 unorm comparison returns zero", false, 0u); Run(*device, {ClampEdge, FilterPoint}, "point, clamp to edge"); Run(*device, {ClampWrap, FilterPoint}, "point, wrap"); if (lodClamp) Run(*device, {ClampWrap, FilterPoint}, "point, wrap with negative offsets", true); From 4f0e4ba8e732c606385f318237cec77b6013d8a8 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:47:25 +0000 Subject: [PATCH 072/523] fix(agc): return zero for measured color compares --- .../tests/execution/EmulatedColorCompare.cpp | 86 ++++++------------- .../Optimization/src/ResourceMaterializer.cpp | 21 +---- 2 files changed, 30 insertions(+), 77 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp index 1b7b1c94aa..cfe2c75b30 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp @@ -27,6 +27,7 @@ constexpr std::uint32_t Side = 2; constexpr std::uint32_t Format8888UNorm = 56; constexpr std::uint32_t Format8UNorm = 1; constexpr std::uint32_t Format8_8UNorm = 14; +constexpr std::uint32_t Format16UNorm = 7; constexpr std::uint32_t Format1616UNorm = 23; constexpr std::uint32_t Format9995Float = 132; constexpr std::uint32_t Format8Srgb = 128; @@ -178,45 +179,6 @@ void FillInput() { Extra[0] = 0x00001f20u; } -std::int32_t OffsetComponent(std::uint32_t tid, std::uint32_t component) { - const auto field = (Extra[tid * 3u] >> (component * 8u)) & 0x3fu; - return static_cast(field ^ 0x20u) - 0x20; -} - -float ReferenceTexel(int x, int y, float reference, const Sampler& sampler) { - const std::uint32_t clamp = sampler.clamp; - const int size = static_cast(Side); - if (clamp == ClampBorder && (x < 0 || x >= size || y < 0 || y >= size)) { - const float border = sampler.border == BorderWhite ? 1.0f : 0.0f; - return reference <= border ? 1.0f : 0.0f; - } - const auto address = [&](int value) { - if (clamp == ClampEdge) return std::clamp(value, 0, static_cast(Side) - 1); - const int size = static_cast(Side); - return ((value % size) + size) % size; - }; - const float red = static_cast(Red[address(y) * Side + address(x)]) / 255.0f; - return reference <= red ? 1.0f : 0.0f; -} - -float Expected(std::uint32_t tid, const Sampler& sampler, bool offsets) { - const float u = Input[tid * 3u + 1u] * static_cast(Side); - const float v = Input[tid * 3u + 2u] * static_cast(Side); - const float reference = std::clamp(Input[tid * 3u + 0u], 0.0f, 1.0f); - const int offsetX = offsets ? OffsetComponent(tid, 0u) : 0; - const int offsetY = offsets ? OffsetComponent(tid, 1u) : 0; - if (sampler.filter == FilterPoint) return ReferenceTexel(static_cast(std::floor(u)) + offsetX, static_cast(std::floor(v)) + offsetY, reference, sampler); - const float cu = u - 0.5f; - const float cv = v - 0.5f; - const int x = static_cast(std::floor(cu)) + offsetX; - const int y = static_cast(std::floor(cv)) + offsetY; - const float a = cu - std::floor(cu); - const float b = cv - std::floor(cv); - const float top = ReferenceTexel(x, y, reference, sampler) * (1.0f - a) + ReferenceTexel(x + 1, y, reference, sampler) * a; - const float bottom = ReferenceTexel(x, y + 1, reference, sampler) * (1.0f - a) + ReferenceTexel(x + 1, y + 1, reference, sampler) * a; - return top * (1.0f - b) + bottom * b; -} - ShaderRecompiler::RecompileResult Compile(AgcDriver::VulkanDevice& device, std::uint32_t format, const Sampler& sampler, std::span code = Code, bool useCache = false, bool nativeSampleOffsets = true, bool cube = false) { std::vector userData(24, 0u); const auto input = cube ? BufferDescriptor(CubeInput.data(), static_cast(sizeof(CubeInput))) : BufferDescriptor(Input.data(), static_cast(sizeof(Input))); @@ -254,9 +216,7 @@ std::uint64_t Run(AgcDriver::VulkanDevice& device, const Sampler& sampler, const device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); device.WaitIdle(); for (std::uint32_t tid = 0; tid < Threads; ++tid) { - const float expected = Expected(tid, sampler, offsets); - const float tolerance = sampler.filter == FilterPoint ? 0.0f : 1e-4f; - Require(std::fabs(Output[tid] - expected) <= tolerance, std::string(name) + ": thread " + std::to_string(tid) + " compared to " + std::to_string(Output[tid]) + ", expected " + std::to_string(expected)); + Require(Output[tid] == 0.0f, std::string(name) + ": thread " + std::to_string(tid) + " compared to " + std::to_string(Output[tid]) + ", expected 0"); } return result.variantId; } @@ -274,12 +234,11 @@ void RunChannels(AgcDriver::VulkanDevice& device, std::span device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); device.WaitIdle(); for (std::uint32_t tid = 0; tid < Threads; ++tid) { - const float compared = Expected(tid, sampler, false); for (std::uint32_t reg = 0; reg < 4u; ++reg) { float expected = -1.0f; if (reg < registers) { const auto select = channels[firstChannel + reg]; - expected = select == 0u ? 0.0f : select == 1u ? 1.0f : compared; + expected = select == 0u ? 0.0f : select == 1u ? 1.0f : 0.0f; } const float actual = Output[tid * 4u + reg]; Require(std::fabs(actual - expected) <= 1e-4f, std::string(name) + ": thread " + std::to_string(tid) + " register " + std::to_string(reg) + " returned " + std::to_string(actual) + ", expected " + std::to_string(expected)); @@ -304,7 +263,7 @@ void RunCube(AgcDriver::VulkanDevice& device, const Sampler& sampler, const char device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(CubeCode.data())); device.WaitIdle(); for (std::uint32_t tid = 0; tid < Threads; ++tid) { - Require(Output[tid] == 1.0f, std::string(name) + ": +X cube face comparison returned " + std::to_string(Output[tid]) + ", expected 1"); + Require(Output[tid] == 0.0f, std::string(name) + ": +X cube face comparison returned " + std::to_string(Output[tid]) + ", expected 0"); } } @@ -314,7 +273,7 @@ bool BindsDepthCompare(const ShaderRecompiler::RecompileResult& result) { }); } -void RunSrgbCubeZero(AgcDriver::VulkanDevice& device, std::uint32_t format, const Sampler& sampler, const char* name) { +void RunZeroCube(AgcDriver::VulkanDevice& device, std::uint32_t format, const Sampler& sampler, const char* name) { FillCubeTexels(); for (std::uint32_t tid = 0; tid < Threads; ++tid) { CubeInput[tid * 4u + 0u] = 0.5f; @@ -332,7 +291,7 @@ void RunSrgbCubeZero(AgcDriver::VulkanDevice& device, std::uint32_t format, cons } } -void RunSrgbZero(AgcDriver::VulkanDevice& device, std::uint32_t format, const Sampler& sampler, const char* name, bool useCache, std::uint64_t expectedVariant) { +void RunZero(AgcDriver::VulkanDevice& device, std::uint32_t format, const Sampler& sampler, const char* name, bool useCache, std::uint64_t expectedVariant) { Output.fill(-1.0f); const auto result = Compile(device, format, sampler, Code, useCache); if (useCache) Require(result.cacheHit && result.variantId == expectedVariant, std::string(name) + " did not reuse the compiled comparison artifact"); @@ -366,25 +325,34 @@ int main() { FillTexels(); FillInput(); RunCube(*device, {ClampEdge, FilterPoint}, "point cube, clamp to edge"); - Reject(*device, Format1616UNorm, {ClampEdge, FilterBilinear}, "bilinear cube comparison requires DISABLE_CUBE_WRAP", CubeCode, true, true); + RunCube(*device, {ClampEdge, FilterBilinear}, "bilinear cube without DISABLE_CUBE_WRAP"); for (std::uint32_t function = 0; function < 8u; ++function) { const auto name = "bilinear 8_8_8_8 srgb cube function " + std::to_string(function); - RunSrgbCubeZero(*device, Format8888Srgb, {ClampEdge, FilterBilinear, BorderBlack, 0u, 0u, false, function}, name.c_str()); + RunZeroCube(*device, Format8888Srgb, {ClampEdge, FilterBilinear, BorderBlack, 0u, 0u, false, function}, name.c_str()); } - RunSrgbCubeZero(*device, Format8Srgb, {ClampEdge, FilterBilinear}, "bilinear 8 srgb cube without DISABLE_CUBE_WRAP"); - RunSrgbCubeZero(*device, Format8_8Srgb, {ClampEdge, FilterBilinear}, "bilinear 8_8 srgb cube without DISABLE_CUBE_WRAP"); + RunZeroCube(*device, Format8Srgb, {ClampEdge, FilterBilinear}, "bilinear 8 srgb cube without DISABLE_CUBE_WRAP"); + RunZeroCube(*device, Format8_8Srgb, {ClampEdge, FilterBilinear}, "bilinear 8_8 srgb cube without DISABLE_CUBE_WRAP"); RunCube(*device, {ClampEdge, FilterBilinear, BorderBlack, 0u, 0u, true}, "bilinear cube with DISABLE_CUBE_WRAP"); - Reject(*device, Format9995Float, {ClampEdge, FilterPoint}, "implemented only for float, unorm and snorm formats"); + Reject(*device, Format9995Float, {ClampEdge, FilterPoint}, "implemented only for floating-point, UNORM and SNORM formats"); for (std::uint32_t function = 0; function < 8u; ++function) { const auto name = "bilinear 8_8_8_8 unorm cube function " + std::to_string(function); - RunSrgbCubeZero(*device, Format8888UNorm, {ClampEdge, FilterBilinear, BorderBlack, 0u, 0u, false, function}, name.c_str()); + RunZeroCube(*device, Format8888UNorm, {ClampEdge, FilterBilinear, BorderBlack, 0u, 0u, false, function}, name.c_str()); } - RunSrgbCubeZero(*device, Format8888UNorm, {ClampEdge, FilterAnisoBilinear, BorderBlack, 0u, 0u, false, GreaterEqual}, "anisotropic bilinear 8_8_8_8 unorm cube without DISABLE_CUBE_WRAP"); - Reject(*device, Format1616UNorm, {ClampEdge, FilterAnisoBilinear}, "point or bilinear"); - Reject(*device, Format1616UNorm, {ClampEdge, FilterAnisoPoint}, "point or bilinear"); + RunZeroCube(*device, Format8888UNorm, {ClampEdge, FilterAnisoBilinear, BorderBlack, 0u, 0u, false, GreaterEqual}, "anisotropic bilinear 8_8_8_8 unorm cube without DISABLE_CUBE_WRAP"); + RunZero(*device, Format1616UNorm, {ClampEdge, FilterAnisoBilinear}, "anisotropic bilinear 16_16 unorm", false, 0u); + RunZero(*device, Format1616UNorm, {ClampEdge, FilterAnisoPoint}, "anisotropic point 16_16 unorm", false, 0u); FillTexels(); + Require(BindsDepthCompare(Compile(*device, Format16UNorm, {ClampEdge, FilterPoint})), "an R16 unorm texture left the native comparison path"); Require(BindsDepthCompare(Compile(*device, Format32Float, {ClampEdge, FilterBilinear})), "an R32 float texture left the native comparison path"); Require(!BindsDepthCompare(Compile(*device, Format8888UNorm, {ClampEdge, FilterBilinear})), "a color texture kept a depth-compare binding"); + for (const auto format : {1u, 2u, 8u, 13u, 14u, 15u, 23u, 24u, 29u, 36u, 50u, 51u, 56u, 57u, 65u, 66u, 71u, 77u}) { + const auto name = "measured GFX10 color format " + std::to_string(format) + " returns zero"; + RunZero(*device, format, {ClampEdge, FilterPoint}, name.c_str(), false, 0u); + } + for (std::uint32_t function = 0; function < 8u; ++function) { + const auto name = "16_16 unorm compare function " + std::to_string(function) + " returns zero"; + RunZero(*device, Format1616UNorm, {ClampEdge, FilterPoint, BorderBlack, 0u, 0u, false, function}, name.c_str(), false, 0u); + } for (const bool useCache : {false, true}) { const auto variant = useCache ? Run(*device, {ClampEdge, FilterPoint}, "cached UNORM before sRGB", false, true) : 0u; if (useCache) Require(variant != 0, "the cached comparison has no compiled artifact identity"); @@ -392,7 +360,7 @@ int main() { for (std::uint32_t function = 0; function < 8u; ++function) { for (const auto filter : {FilterPoint, FilterBilinear}) { const auto name = std::string(useCache ? "cached" : "uncached") + " sRGB format " + std::to_string(format) + " function " + std::to_string(function) + (filter == FilterPoint ? " point" : " bilinear"); - RunSrgbZero(*device, format, {ClampEdge, filter, BorderBlack, 0u, 0u, false, function}, name.c_str(), useCache, variant); + RunZero(*device, format, {ClampEdge, filter, BorderBlack, 0u, 0u, false, function}, name.c_str(), useCache, variant); } } } @@ -403,11 +371,11 @@ int main() { for (const auto filter : {FilterPoint, FilterBilinear, FilterAnisoBilinear}) { if (format != Format8888UNorm && function != LessEqual) continue; const auto name = "8-bit unorm format " + std::to_string(format) + " function " + std::to_string(function) + " filter " + std::to_string(filter); - RunSrgbZero(*device, format, {ClampEdge, filter, BorderBlack, 0u, 0u, false, function}, name.c_str(), false, 0u); + RunZero(*device, format, {ClampEdge, filter, BorderBlack, 0u, 0u, false, function}, name.c_str(), false, 0u); } } } - RunSrgbZero(*device, Format1616UNorm, {ClampEdge, FilterPoint}, "16_16 unorm comparison returns zero", false, 0u); + RunZero(*device, Format1616UNorm, {ClampEdge, FilterPoint}, "16_16 unorm comparison returns zero", false, 0u); Run(*device, {ClampEdge, FilterPoint}, "point, clamp to edge"); Run(*device, {ClampWrap, FilterPoint}, "point, wrap"); if (lodClamp) Run(*device, {ClampWrap, FilterPoint}, "point, wrap with negative offsets", true); diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index 3a9a072113..5404f91580 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -1347,24 +1347,9 @@ void materializeForcedDegamma(const IrResourcePlan& plan, ResourceSnapshot& snap } std::uint32_t colorCompareReference(IrBufferFormat format) { - switch (format) { - case IrBufferFormat::Format16_16UNorm: - case IrBufferFormat::Format11_11_10UNorm: case IrBufferFormat::Format10_11_11UNorm: case IrBufferFormat::Format2_10_10_10UNorm: - case IrBufferFormat::Format10_10_10_2UNorm: case IrBufferFormat::Format16_16_16_16UNorm: - return EmulatedCompare::ReferenceUnorm; - case IrBufferFormat::Format8UNorm: case IrBufferFormat::Format8_8UNorm: case IrBufferFormat::Format8_8_8_8UNorm: - case IrBufferFormat::Format8Srgb: case IrBufferFormat::Format8_8Srgb: case IrBufferFormat::Format8_8_8_8Srgb: - return EmulatedCompare::ReferenceZero; - case IrBufferFormat::Format8SNorm: case IrBufferFormat::Format16SNorm: case IrBufferFormat::Format8_8SNorm: case IrBufferFormat::Format16_16SNorm: - case IrBufferFormat::Format11_11_10SNorm: case IrBufferFormat::Format10_11_11SNorm: case IrBufferFormat::Format2_10_10_10SNorm: - case IrBufferFormat::Format10_10_10_2SNorm: case IrBufferFormat::Format8_8_8_8SNorm: case IrBufferFormat::Format16_16_16_16SNorm: - return EmulatedCompare::ReferenceSnorm; - case IrBufferFormat::Format16Float: case IrBufferFormat::Format16_16Float: case IrBufferFormat::Format11_11_10Float: - case IrBufferFormat::Format10_11_11Float: case IrBufferFormat::Format32_32Float: case IrBufferFormat::Format16_16_16_16Float: - return EmulatedCompare::ReferenceFloat; - default: - throw std::runtime_error("comparison sampling of a color texture is implemented only for float, unorm and snorm formats (format " + std::to_string(static_cast(format)) + ")"); - } + const auto value = static_cast(format); + if (((value >= 1u && value <= 77u) || (value >= 128u && value <= 130u)) && SampledTextureNumericClass(format) == IrTextureNumericClass::Float && format != IrBufferFormat::Format11_11_10UNorm && format != IrBufferFormat::Format10_11_11Float) return EmulatedCompare::ReferenceZero; + throw std::runtime_error("comparison sampling of a color texture is implemented only for floating-point, UNORM and SNORM formats (format " + std::to_string(value) + ")"); } bool convertedSampleCompatible(const ImageResource& image) { From 092a66773a29d7d955c77d92107a0be150ccd8c5 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 23:47:27 +0000 Subject: [PATCH 073/523] docs(shader): record measured color compare formats --- docs/dev/debt/shader.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/shader.md b/docs/dev/debt/shader.md index 4353f3bd8e..e00bdf7cb6 100644 --- a/docs/dev/debt/shader.md +++ b/docs/dev/debt/shader.md @@ -14,7 +14,7 @@ - [MUBUF/MTBUF descriptors ending in VCC](../../../core/shader/recompiler/RdnaDecoder/src/RdnaMemoryOpDecoder.cpp): the four dword descriptor at s104 is s104, s105, vcc_lo and vcc_hi, as LLVM's [VCCTuple/SReg_128](https://github.com/llvm/llvm-project/blob/d8219b47422f7322db252ad29ca8ea87993fb433/llvm/lib/Target/AMDGPU/SIRegisterInfo.td#L422) and [buffer resource operands](https://github.com/llvm/llvm-project/blob/d8219b47422f7322db252ad29ca8ea87993fb433/llvm/lib/Target/AMDGPU/BUFInstructions.td) describe. Existing translation maps scalar codes106/107 to the raw VCC words. Recompiled loads/stores, both words, bounds, formats and wave32/wave64 execute on NVIDIA and AMD Vulkan; raw gfx10/gfx1013 hardware is not measured. Higher descriptor bases remain refused; the normal SGPR bound is unchanged. - [Packed pixel ancillary word](../../../core/shader/recompiler/Optimization/src/ConstantFolder.cpp) (shader recompiler) - the sample index at bits 8-11 and the render target index at bits 16-28 follow #295. The sample index is the SampleId builtin in a pixel shader the guest iterates per sample (`PS_ITER_SAMPLE` on a multisampled draw), an equality inferred, not measured. In any other pixel shader it is 0, the iterated sample number of a pixel the hardware does not iterate (also inferred), so reading it does not make the host shade per sample. The render target index is the Layer builtin when the draw exports it (a layered draw, `PA_CL_VS_OUT_CNTL` bit 18; the pipeline specialization constant `LayerExported`) and 0 otherwise, the first layer Vulkan renders when no pre-rasterization stage writes Layer. Reading the unwritten Layer builtin instead makes AMD's Windows driver drop the fragment's writes (#4800); with the constant at 0 the load is folded away. Mesa reads both fields from those bits, with the layer 14 bits wide only from GFX12 on ([ac_nir_lower_intrinsics_to_args.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/amd/common/nir/ac_nir_lower_intrinsics_to_args.c#L342-L368)). A value passed through a select, move or phi whose every read is an extract inside one of those fields is rebuilt from the two fields alone. Any other read the masked-select pass cannot drop gets the whole word: the primitive class at bits 0-1 (0 point, 1 line, 2 triangle, 3 rectangle), which the draw sets as a pipeline specialization constant ([RasterPrimitiveOf](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp)) in the shaders that read it, plus the sample field, with every other bit zero. That layout comes from the RDNA 3 and 3.5 ISA guides (prim type 1:0, VRS rates 3:2 and 5:4, eye id 7, sample 11:8, render target index 28:16). The GCN register guides give the same prim type and sample fields and put the render target index at 26:16, so the two sources agree for layers below 2048. AMD's LLPC reads the primitive class from bits 1:0 with the same encoding ([LowerGlCompatibility.cpp](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/llpc/lowering/LowerGlCompatibility.cpp#L830-L836)) and without a GFX-level check ([getPrimType](https://github.com/GPUOpen-Drivers/llpc/blob/40cb8d95ad8d6f7f1652e3fd47d39667594cce08/lgc/lowering/LowerInOut.cpp#L5307-L5315)). Nobody has checked it on gfx1013. In the whole word only bits 0-1 and 8-11 have a source. Bits 2-7, 12-15 and 29-31 are assumed zero, not measured: the VRS rates (2-5) and the eye id (7) because the driver supports neither variable-rate shading nor multiview, so a title that enables either would read other bits on the console, and bits 6, 12-15 and 29-31 because no source gives them a meaning. The sample field reads SampleId only in iterated shaders. The whole word has the same 0 at bits 16-28. A change that makes draws export a layer must restore the Layer read in both places - [Front-face input with `FRONT_FACE_ALL_BITS`](../../../core/shader/recompiler/Translation/src/InstructionTranslator.cpp) (shader recompiler) - with `SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS` set, a back face reads 0 and a front face 0xffffffff. Mesa 22.0 RADV sets the bit ([radv_pipeline.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-22.0.0/src/amd/vulkan/radv_pipeline.c#L5953)) and ACO tests the input with `v_cmp_lg_u32 0` ([aco_instruction_selection.cpp](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-22.0.0/src/amd/compiler/aco_instruction_selection.cpp#L8123-L8128)), so only back = 0 and front ≠ 0 are sourced; PPSA05684's two-sided cloth shaders test it with `v_cmp_lt_u32 0`. The exact front value is not measured. Without the bit the input is ±1.0, as current Mesa reads it (`fgt 0`, `fsign`; [ac_nir_lower_intrinsics_to_args.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/amd/common/nir/ac_nir_lower_intrinsics_to_args.c)) -- [Color comparison descriptor forms](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp): emulated color comparison supports UNORM, SNORM and float formats for 2D, 2D-array and cube views. 8-bit sRGB and UNORM (`Format8Srgb`, `Format8_8Srgb`, `Format8_8_8_8Srgb`, `Format8UNorm`, `Format8_8UNorm`, `Format8_8_8_8UNorm`) return 0 for every compare function, measured with `image_sample_c_lz` on gfx1035 over references -0.5..1.5 and texel codes 0..255. Anisotropic filters are refused, except on those 8-bit formats, where every texel comparison is 0 (not measured with an anisotropic S#). Bilinear cube comparisons of float, snorm and the other unorm formats without `DISABLE_CUBE_WRAP` are refused; on that hardware a 4x4 R32 float cube blends across faces (46 of 90 edge rows differ from a per-face clamp; face 0, ref 0.1, u=3.9 v=2.5 is 0.6016 and u=1.5 v=0.1 is 0.3984), and with `DISABLE_CUBE_WRAP` the per-face clamp matches. An 8-bit sRGB or UNORM bilinear cube without that bit returns 0, since every texel comparison is already 0 for every compare function and the selected face cannot change the result. Cube-edge sampling behavior and console coverage on PS5's gfx1013 remain unverified. +- [Color comparison descriptor forms](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp): for formats AnyPS5 can sample as floating-point from GFX10 codes 1–77, `image_sample_c*` returns 0 for every comparison function except codes 7 (`16_UNORM`) and 22 (`32_FLOAT`), which use the native comparison path. The split was measured with point sampling on RDNA2 (gfx1031 and gfx1036) and with point and bilinear sampling on RDNA3 (gfx1100); the PS5's gfx1013 and 2D-array/cube comparisons across these formats remain unverified. Codes 128–130 (8-bit sRGB) also return 0, measured on gfx1035 over references -0.5..1.5 and texel codes 0..255. Scaled, integer and formats requiring converted sampling remain unsupported, although the tested hardware formats also return 0 for them. Anisotropic comparison of the remaining formats has not been measured. - [s_setkill](../../../core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp) (core/shader/recompiler) - decoded as `s_endpgm` for any immediate: measured on RDNA2 hardware (gfx1035), a store before it is visible and nothing after it runs ([#771](https://github.com/boykopovar/AnyPS5/pull/771)); whether the KILL status bit it sets is observable differently from `s_endpgm` is unverified - [s_sethalt](../../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp) (core/shader/recompiler) - with SIMM16[0] clear it is a no-op: the RDNA 2 ISA guide describes that value as resuming a halted wave, and a wave that executes it is not halted; measured on a gfx1035 (RDNA 2) wave: `s_sethalt 0` followed by a VALU op completes and gives the normal result. Only SIMM16[0] is read; with it set the instruction throws - [s_subvector_loop_begin and s_subvector_loop_end](../../../core/shader/recompiler/Translation/src/ControlFlowInstructions.cpp) (core/shader/recompiler) - follow AMD's RDNA 2 instruction set reference and a gfx1036 (wave64) measurement, pinned in [SubvectorLoop.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/SubvectorLoop.cpp); `S0` in the end instruction's `elif(S0 == 0)` is read as its SGPR operand. A wave32 shader and a destination other than an SGPR throw because neither is measured. Measured on PC RDNA2 (Radeon 610M), not on a PS5. The [specialization pass](../../../core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp) keeps a constant-condition branch whose untaken target is a loop header, since folding it removes the loop's back edge, which SPIR-V requires exactly once per loop header (SPIRV-Tools rejects the module and the NVIDIA driver 615.78.08 crashes compiling it); a loop whose back-edge block becomes unreachable in another way is not handled @@ -136,7 +136,7 @@ - [FP16_OVFL](../../../core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp) follows the shader's float mode as measured on RDNA2 (gfx1036) at IEEE=0 and IEEE=1 with f16 denormals kept and flushed: with `FP16_OVFL`=1 an f16 result that rounds to ±infinity is written as ±0x7bff unless a source operand is infinite, so `v_rcp_f16`, `v_rsq_f16` and `v_log_f16` of ±0 and `v_exp_f16` of 16 or more are clamped while an infinite input passes through, and `v_div_fixup_f16` keeps the infinity it writes for a zero denominator or an infinite numerator and clamps any other infinite quotient, a NaN one included. `v_cvt_pkrtz_f16_f32` rounds toward zero and never overflows. The sources are checked as the f32 values the translation computes with; the rule is applied to `v_fma_mix*_f16` with f32 sources and to `v_interp_p2_f16` without a measurement of infinite f32 sources or parameters. Round modes other than nearest even are not modelled, so the overflow threshold is the nearest-even one (65520). - [Directed f16 rounding](../../../core/shader/recompiler/Translation/src/TranslationContext/SubwordPacking.cpp) for `v_cvt_f16_f32`, `v_cvt_f16_i16`, `v_cvt_f16_u16` and `v_ldexp_f16` follows `FLOAT_MODE` bits 3:2 in every f16 denormal mode, measured on RDNA2 (gfx1036 with f16 denormals preserved, gfx1030 in all sixteen combinations of f16 rounding and denormal mode). When bit 7 flushes f16 results, `v_cvt_f16_f32`, `v_cvt_f16_i16` and `v_cvt_f16_u16` write a zero of the result's sign for a value that, rounded in that mode with an unbounded exponent, is below 2^-14, so a value just below 2^-14 that rounds up to it stays 0x0400; `v_ldexp_f16` keeps its rule above. The `clamp` modifier is applied before the rounding. On gfx1030 every row matches: 8,996 f32 inputs around the half denormal, normal and overflow boundaries with and without `clamp`, every 16-bit integer, and 2,470 `v_ldexp_f16` operands. - [v_cvt_pkrtz_f16_f32 and v_pack_b32_f16 denormals](../../../core/shader/recompiler/Translation/src/ConvertInstructions.cpp) follow the `FLOAT_MODE` f16 denormal field: `v_cvt_pkrtz_f16_f32` flushes each denormal half result to its signed zero when bit 1 is clear (0 and 1), and `v_pack_b32_f16` flushes each denormal half input to its signed zero when bit 0 is clear (0 and 2). Measured on RDNA2 (gfx1031, Radeon RX 6700 XT, AMD Windows driver 32.0.21045.5002 through HIP) at IEEE=0 and IEEE=1 with denorm16 0..3 and f32 denormals kept, over 256 pairs of 16 f32 values around the f16 denormal range for `v_cvt_pkrtz_f16_f32` and 256 pairs of 16 halves (zeros, denormals, minimum normals, ±1, the largest finite value, infinities and quiet and signaling NaNs) for `v_pack_b32_f16`. Shaders without a known float mode keep the previous lowering. The PS5's gfx1013 is not measured. -- [Comparison sampling of a color texture](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (`image_sample_c`/`image_sample_c_lz` on a texture that is not R32 float or R16 unorm) is emulated in the shader, since Vulkan compares only depth formats: the red channel is fetched, compared and, for bilinear filtering, blended (compare, then filter). Only 2D, 2D array and cube views, float/unorm/snorm formats, wrap, clamp-to-edge and clamp-to-border addressing (border red 0 for the black borders, 1 for opaque white) and point or bilinear filtering at one level are implemented, with texel offsets added to the texel indices; LOD biases and clamps are accepted, as they cannot select another level of a single-level sample. Half-border addressing, the border color table, gradients, gathers, other dimensions, anisotropy (except on the 8-bit formats below) and mip filtering throw. Under bilinear filtering, taps past a clamp-to-border edge compare the border value and blend with the texel taps, so results near the edge depend on the filter weights. AMD documentation does not say how a color format's reference is handled: it is clamped to [0, 1] for unorm and [-1, 1] for snorm, as Vulkan does for unorm depth, and not clamped for float. 8-bit sRGB and UNORM (`Format8Srgb`, `Format8_8Srgb`, `Format8_8_8_8Srgb`, `Format8UNorm`, `Format8_8UNorm`, `Format8_8_8_8UNorm`) return 0 for every compare function, measured with `image_sample_c_lz` on gfx1035 over references -0.5..1.5 and texel codes 0..255. A bilinear cube of those 8-bit formats without `DISABLE_CUBE_WRAP`, and an anisotropic S# on them, return 0 for the same reason; float, snorm and the other unorm bilinear cubes without that bit still throw. Bilinear weights are full float, not the hardware's fixed-point subtexel weights +- [Comparison sampling of a color texture](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (`image_sample_c`/`image_sample_c_lz` on a texture that is not R32 float or R16 unorm) returns 0 for every comparison function on formats AnyPS5 can sample as floating-point measured on GFX10 (codes 1–77 except native comparison codes 7 and 22) and on the corresponding GFX11 formats measured on gfx1100. The current Vulkan path still lowers these formats through the shader; the compare result is constant zero. Scaled, integer and formats requiring converted sampling remain refused. Only 2D, 2D-array and cube views are accepted; image tables, min/max reduction, unnormalized coordinates and unsupported addressing remain refused. Point and bilinear results match on gfx1100. The PS5's gfx1013, array/cube behavior for the measured formats, and anisotropic comparisons remain unverified. Bilinear weights are full float, not the hardware's fixed-point subtexel weights - [Negative color-texture comparison wrap indices](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (libSceAgcDriver) use explicit `OpSRem` normalization to implement modulo; the negative-offset point-wrap regression fails with direct `OpSMod` and passes with normalization on Mesa 22.3.6 llvmpipe (LLVM 15.0.6), but negative-index wrap has not been measured on PS5 hardware. - [Converted unorm images](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (10_11_11_UNORM, read and written through an R32_UINT view) are sampled in the shader, the way color comparisons are emulated: `image_sample` of a 2D or 2D array view fetches the one or four texels at LOD 0, decodes them and blends them, for point or bilinear filtering with equal minification and magnification filters and wrap or clamp-to-edge addressing. A shader gets these sample modes only once a converted descriptor reaches one of its sampled images: that capture throws `ConvertedSampleRequired`, the driver records the program and prepares it again with `GuestContext::convertedSamples`, so such a shader is prepared twice per run and its first capture is repeated. The record lives with the registered program and is not kept across runs. Bilinear weights are rounded to the hardware's 8-bit grid (`roundEven(fraction * 256) / 256`, ties to even), as measured with `image_sample_lz` on RDNA2 (gfx1035) for 10_11_11 UNORM texels by adriadam10 in #5958; point sampling, decoding, swizzles and wrap and clamp-to-edge addressing matched the same measurement. A sample that is not `_lz` needs an image with one mip level; gathers, LOD queries, clamp-to-border and mirrored addressing, other filters, min/max reduction, unnormalized coordinates, images selected at run time, 16-bit data and a DST_SEL of the missing W channel throw. - [Converted unorm image loads](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) divide by 2^n - 1 with the buffer format path's `OpFDiv`, which Vulkan allows to be up to 2.5 ULP off, so some drivers are not bit-exact. From ee2d9a48765dc68804f19afd7bd54b9b08ebd251 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 20:38:17 +0000 Subject: [PATCH 074/523] test(agc): avoid unsupported color image format --- .../libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp index cfe2c75b30..4538735f37 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp @@ -345,7 +345,10 @@ int main() { Require(BindsDepthCompare(Compile(*device, Format16UNorm, {ClampEdge, FilterPoint})), "an R16 unorm texture left the native comparison path"); Require(BindsDepthCompare(Compile(*device, Format32Float, {ClampEdge, FilterBilinear})), "an R32 float texture left the native comparison path"); Require(!BindsDepthCompare(Compile(*device, Format8888UNorm, {ClampEdge, FilterBilinear})), "a color texture kept a depth-compare binding"); - for (const auto format : {1u, 2u, 8u, 13u, 14u, 15u, 23u, 24u, 29u, 36u, 50u, 51u, 56u, 57u, 65u, 66u, 71u, 77u}) { + // Format 51 maps to VK_FORMAT_A2B10G10R10_SNORM_PACK32, which is not an + // image format on every Vulkan device (including RADV NAVI31). Keep this + // execution test limited to formats the test device can create. + for (const auto format : {1u, 2u, 8u, 13u, 14u, 15u, 23u, 24u, 29u, 36u, 50u, 56u, 57u, 65u, 66u, 71u, 77u}) { const auto name = "measured GFX10 color format " + std::to_string(format) + " returns zero"; RunZero(*device, format, {ClampEdge, FilterPoint}, name.c_str(), false, 0u); } From 745ff15b80e897accd9ee18c563822dde00036c9 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 20:39:55 +0000 Subject: [PATCH 075/523] test(agc): skip unsupported sampled color images --- .../libSceAgcDriver/Execution/include/VulkanDevice.hpp | 1 + .../prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp | 6 ++++++ .../tests/execution/EmulatedColorCompare.cpp | 9 +++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp index 19b1b24e22..40a62ef1d7 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp +++ b/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp @@ -52,6 +52,7 @@ public: VulkanDevice& operator=(const VulkanDevice&) = delete; std::string DeviceName() const; std::string DriverInfo() const; + bool SupportsImageFormat(VkFormat format, VkImageUsageFlags usage) const; bool CalibrateClock(double& gpuNs, std::chrono::steady_clock::time_point& cpu) const; ShaderRecompiler::SpirvTarget Target() const; std::optional GeometryLimits() const; diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 66da673ed8..9c2b02ed07 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -3630,6 +3630,12 @@ std::uint32_t VulkanDevice::VendorId() const { return state->properties.vendorID; } +bool VulkanDevice::SupportsImageFormat(VkFormat format, VkImageUsageFlags usage) const { + VkImageFormatProperties properties{}; + const auto query = state->InstanceFunction("vkGetPhysicalDeviceImageFormatProperties"); + return query(state->physical, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage, 0, &properties) == VK_SUCCESS; +} + ShaderRecompiler::SpirvTarget VulkanDevice::Target() const { require(state->shaderProfile != nullptr, "shader device profile is unavailable"); auto target = state->shaderProfile->Target(); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp index 4538735f37..b656cca503 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/EmulatedColorCompare.cpp @@ -345,10 +345,11 @@ int main() { Require(BindsDepthCompare(Compile(*device, Format16UNorm, {ClampEdge, FilterPoint})), "an R16 unorm texture left the native comparison path"); Require(BindsDepthCompare(Compile(*device, Format32Float, {ClampEdge, FilterBilinear})), "an R32 float texture left the native comparison path"); Require(!BindsDepthCompare(Compile(*device, Format8888UNorm, {ClampEdge, FilterBilinear})), "a color texture kept a depth-compare binding"); - // Format 51 maps to VK_FORMAT_A2B10G10R10_SNORM_PACK32, which is not an - // image format on every Vulkan device (including RADV NAVI31). Keep this - // execution test limited to formats the test device can create. - for (const auto format : {1u, 2u, 8u, 13u, 14u, 15u, 23u, 24u, 29u, 36u, 50u, 56u, 57u, 65u, 66u, 71u, 77u}) { + for (const auto format : {1u, 2u, 8u, 13u, 14u, 15u, 23u, 24u, 29u, 36u, 50u, 51u, 56u, 57u, 65u, 66u, 71u, 77u}) { + if (format == 51u && !device->SupportsImageFormat(VK_FORMAT_A2B10G10R10_SNORM_PACK32, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { + std::puts("measured GFX10 color format 51 returns zero: skipped, Vulkan device does not support the image format"); + continue; + } const auto name = "measured GFX10 color format " + std::to_string(format) + " returns zero"; RunZero(*device, format, {ClampEdge, FilterPoint}, name.c_str(), false, 0u); } From 33c1f2365fbf5dc60f22be68f7631a88f5a12d0a Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:06:23 +0000 Subject: [PATCH 076/523] fix(relinker): validate ELF prefix before reading full input --- core/relinker/io/include/io/FileReader.hpp | 2 ++ core/relinker/io/src/FileReader.cpp | 16 ++++++++++++++++ core/relinker/main.cpp | 1 + 3 files changed, 19 insertions(+) diff --git a/core/relinker/io/include/io/FileReader.hpp b/core/relinker/io/include/io/FileReader.hpp index bf5d2fa85a..1056ab33a1 100644 --- a/core/relinker/io/include/io/FileReader.hpp +++ b/core/relinker/io/include/io/FileReader.hpp @@ -2,11 +2,13 @@ #define IO_FILEREADER_HPP #include +#include namespace Io { class FileReader : public IFileReader { public: + std::vector ReadPrefix(const std::string& path, std::size_t size) const; std::vector Read(const std::string& path) override; }; diff --git a/core/relinker/io/src/FileReader.cpp b/core/relinker/io/src/FileReader.cpp index 228903ab48..35763b5df1 100644 --- a/core/relinker/io/src/FileReader.cpp +++ b/core/relinker/io/src/FileReader.cpp @@ -3,9 +3,25 @@ #include #include #include +#include namespace Io { +std::vector FileReader::ReadPrefix(const std::string& path, const std::size_t size) const { + if (size > static_cast(std::numeric_limits::max())) + throw Domain::RelinkerException("File prefix is too large: " + path); + std::ifstream f(path, std::ios::binary); + if (!f) + throw Domain::RelinkerException("Cannot open file: " + path); + std::vector buf(size); + f.read(reinterpret_cast(buf.data()), static_cast(size)); + const auto count = f.gcount(); + if (f.bad()) + throw Domain::RelinkerException("Cannot read file: " + path); + buf.resize(static_cast(count)); + return buf; +} + std::vector FileReader::Read(const std::string& path) { const auto native = NativePath(path); std::error_code error; diff --git a/core/relinker/main.cpp b/core/relinker/main.cpp index fe626ea38d..88c42f2797 100644 --- a/core/relinker/main.cpp +++ b/core/relinker/main.cpp @@ -41,6 +41,7 @@ struct ConvertedExecutable { ConvertedExecutable Convert(const Cli::Args& args, const std::string& inputPath, const std::string& absPath, const bool mainExecutable, const std::string& targetName, const std::vector& patches, Cli::ConsoleProgress& progress) { Io::FileReader fileReader; Relinker::ConversionInput input; + Relinker::ElfReader(fileReader.ReadPrefix(inputPath, 0x40)).ReadHeader(); input.Executable = fileReader.Read(inputPath); input.InputPath = std::filesystem::absolute(Io::NativePath(inputPath)).lexically_normal(); input.OutputPath = Io::NativePath(absPath); From fc046e621b53261073e3dd493074a3d672371d03 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:06:23 +0000 Subject: [PATCH 077/523] test(relinker): reject large non-ELF inputs before allocation --- .../relinker/relinker/tests/test_input_magic.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/relinker/relinker/tests/test_input_magic.py b/core/relinker/relinker/tests/test_input_magic.py index e388e2d813..ace10d9119 100644 --- a/core/relinker/relinker/tests/test_input_magic.py +++ b/core/relinker/relinker/tests/test_input_magic.py @@ -1,6 +1,7 @@ """Reject inputs that are not plain ELF files with a message that names what was read.""" from pathlib import Path +import platform import subprocess import sys import tempfile @@ -23,6 +24,22 @@ def main(): convert("eboot.self.kernel", b"\x54\x14\xf5\xee" + bytes(0x1000), "The input is a SELF container, not an ELF") convert("eboot.pkg", b"\x7fCNT" + bytes(0x1000), "Invalid ELF magic number: 7f 43 4e 54") convert("eboot.short", b"\x7fEL", "File too small for ELF header") + if platform.system() == "Linux": + import resource + + source = work / "large-unsupported.pkg" + output = work / "large-unsupported.out" + with source.open("wb") as stream: + stream.write(b"\x7fFIH") + stream.truncate(8 * 1024**3) + + def limit_address_space(): + resource.setrlimit(resource.RLIMIT_AS, (512 * 1024**2, 512 * 1024**2)) + resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) + + result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)], + capture_output=True, text=True, timeout=20, preexec_fn=limit_address_space) + assert result.returncode == 2 and "Invalid ELF magic number: 7f 46 49 48" in result.stderr and not output.exists(), result print("Input magic diagnostics tests passed") From 8625b4802bbdd97b30e52c1445555fa2e77e5412 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:07:53 +0000 Subject: [PATCH 078/523] fix(relinker): normalize input prefix paths --- core/relinker/io/src/FileReader.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/relinker/io/src/FileReader.cpp b/core/relinker/io/src/FileReader.cpp index 35763b5df1..4f012529d3 100644 --- a/core/relinker/io/src/FileReader.cpp +++ b/core/relinker/io/src/FileReader.cpp @@ -10,7 +10,11 @@ namespace Io { std::vector FileReader::ReadPrefix(const std::string& path, const std::size_t size) const { if (size > static_cast(std::numeric_limits::max())) throw Domain::RelinkerException("File prefix is too large: " + path); - std::ifstream f(path, std::ios::binary); + const auto native = NativePath(path); + std::error_code error; + if (!std::filesystem::is_regular_file(native, error)) + throw Domain::RelinkerException("Cannot open file: " + path); + std::ifstream f(native, std::ios::binary); if (!f) throw Domain::RelinkerException("Cannot open file: " + path); std::vector buf(size); From 11adccfe15258e21e9ba6e90b947f674320ad9a8 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:16:19 +0000 Subject: [PATCH 079/523] fix(relinker): preserve detailed header diagnostics --- core/relinker/main.cpp | 2 +- .../relinker/include/relinker/parsing/ElfReader.hpp | 1 + core/relinker/relinker/src/parsing/ElfReader.cpp | 6 +++++- core/relinker/relinker/tests/test_input_magic.py | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/relinker/main.cpp b/core/relinker/main.cpp index 88c42f2797..6e10b551f5 100644 --- a/core/relinker/main.cpp +++ b/core/relinker/main.cpp @@ -41,7 +41,7 @@ struct ConvertedExecutable { ConvertedExecutable Convert(const Cli::Args& args, const std::string& inputPath, const std::string& absPath, const bool mainExecutable, const std::string& targetName, const std::vector& patches, Cli::ConsoleProgress& progress) { Io::FileReader fileReader; Relinker::ConversionInput input; - Relinker::ElfReader(fileReader.ReadPrefix(inputPath, 0x40)).ReadHeader(); + Relinker::ElfReader(fileReader.ReadPrefix(inputPath, 0x40)).ValidateMagic(); input.Executable = fileReader.Read(inputPath); input.InputPath = std::filesystem::absolute(Io::NativePath(inputPath)).lexically_normal(); input.OutputPath = Io::NativePath(absPath); diff --git a/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp b/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp index 692f30c43d..b8fe9513c3 100644 --- a/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp +++ b/core/relinker/relinker/include/relinker/parsing/ElfReader.hpp @@ -10,6 +10,7 @@ class ElfReader : public IElfReader { public: explicit ElfReader(std::vector fileBuffer); + void ValidateMagic() const; ElfHeader ReadHeader() const override; std::vector ReadProgramHeaders() const override; std::vector ReadSectionHeaders() const override; diff --git a/core/relinker/relinker/src/parsing/ElfReader.cpp b/core/relinker/relinker/src/parsing/ElfReader.cpp index bfed4c355a..f023f3ca3c 100644 --- a/core/relinker/relinker/src/parsing/ElfReader.cpp +++ b/core/relinker/relinker/src/parsing/ElfReader.cpp @@ -76,7 +76,7 @@ std::uint64_t ElfReader::_readU64At(FileByteOffset fileByteOffset) const { return value; } -ElfHeader ElfReader::ReadHeader() const { +void ElfReader::ValidateMagic() const { if (_fileBuffer.size() < 0x40) { throw RelinkerException("File too small for ELF header"); } @@ -90,6 +90,10 @@ ElfHeader ElfReader::ReadHeader() const { _fileBuffer[2] != 'L' || _fileBuffer[3] != 'F') { throw RelinkerException("Invalid ELF magic number: " + formatMagic()); } +} + +ElfHeader ElfReader::ReadHeader() const { + ValidateMagic(); if (_fileBuffer[4] != 2 || _fileBuffer[5] != 1 || _fileBuffer[6] != 1) throw RelinkerException("Expected little-endian ELF64 version 1"); if (_readU16At(0x12) != 62) diff --git a/core/relinker/relinker/tests/test_input_magic.py b/core/relinker/relinker/tests/test_input_magic.py index ace10d9119..e26ccf337c 100644 --- a/core/relinker/relinker/tests/test_input_magic.py +++ b/core/relinker/relinker/tests/test_input_magic.py @@ -24,6 +24,11 @@ def main(): convert("eboot.self.kernel", b"\x54\x14\xf5\xee" + bytes(0x1000), "The input is a SELF container, not an ELF") convert("eboot.pkg", b"\x7fCNT" + bytes(0x1000), "Invalid ELF magic number: 7f 43 4e 54") convert("eboot.short", b"\x7fEL", "File too small for ELF header") + unsupported_version = bytearray(64) + unsupported_version[:4] = b"\x7fELF" + unsupported_version[4:7] = b"\x02\x01\x01" + unsupported_version[0x12:0x14] = b"\x3e\x00" + convert("eboot.unsupported-version", unsupported_version, "Unsupported ELF object version: expected 1") if platform.system() == "Linux": import resource From 1f1add461e973c3f2a7bfdd02c10658e1729e1e4 Mon Sep 17 00:00:00 2001 From: juanpimg Date: Tue, 20 Oct 2026 14:09:57 +0200 Subject: [PATCH 080/523] feat(prx): declare the missing PS5 3.20 system exports in the next tier of libraries AnyPS5 resolves a title's imports by NID, so a title that imports a system function that no AnyPS5 library provides fails to load with an unresolved import. This declares 222 such exports across 11 PS5 system libraries that AnyPS5 implements but does not provide. Each declaration calls NotImplemented_nid_no_patch and returns 0; none of them implements the function. A declaration with a credible prototype uses it and cites the source with a pinned commit; the rest are declared without parameters, which resolves the import without making an ABI claim. The technical-debt files record the limitation per library. The list is the intersection of the export table of the console's library (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Every name recomputes to its NID with tools/nid_names.py. libSceAudio3d and libSceVoice are left out because #8331 declares them. --- core/libs/prx/libSceAppContent/Export.cpp | 86 +++++++++++++ core/libs/prx/libSceHttp2/Export.cpp | 91 ++++++++++++++ core/libs/prx/libSceNpParty/Export.cpp | 100 +++++++++++++++ core/libs/prx/libSceNpTrophy2/CMakeLists.txt | 1 + .../prx/libSceNpTrophy2/PendingExports.cpp | 101 +++++++++++++++ core/libs/prx/libScePosixForWebKit/Export.cpp | 96 +++++++++++++++ core/libs/prx/libSceRazorCpu/Export.cpp | 65 ++++++++++ core/libs/prx/libSceSharePlay/Export.cpp | 80 ++++++++++++ core/libs/prx/libSceSsl/Export.cpp | 115 ++++++++++++++++++ core/libs/prx/libSceSysUtil/Export.cpp | 115 ++++++++++++++++++ core/libs/prx/libSceUsbd/Export.cpp | 101 +++++++++++++++ core/libs/prx/libSceVrTracker/Export.cpp | 96 +++++++++++++++ docs/dev/debt/libSceAppContent.md | 2 + docs/dev/debt/libSceHttp2.md | 2 + docs/dev/debt/libSceNpParty.md | 7 ++ docs/dev/debt/libSceNpTrophy2.md | 4 + docs/dev/debt/libScePosixForWebKit.md | 1 + docs/dev/debt/libSceRazorCpu.md | 2 + docs/dev/debt/libSceSharePlay.md | 2 + docs/dev/debt/libSceSsl.md | 4 + docs/dev/debt/libSceSysUtil.md | 8 ++ docs/dev/debt/libSceUsbd.md | 4 + docs/dev/debt/libSceVoiceChat.md | 1 - docs/dev/debt/libSceVrTracker.md | 4 + 24 files changed, 1087 insertions(+), 1 deletion(-) create mode 100644 core/libs/prx/libSceNpTrophy2/PendingExports.cpp create mode 100644 docs/dev/debt/libSceNpParty.md create mode 100644 docs/dev/debt/libSceSysUtil.md diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index d7d94203b0..3027ba0ef9 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -190,4 +190,90 @@ int APS5_VABI sceAppContentGetAddcontDownloadProgress(uint32_t service_label, co if (!entitlement_label || !progress) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } + +int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentAddcontEnqueueDownloadSp(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentAddcontMountByEntitlemetId(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentAddcontShrink(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentAppParamGetString(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentDownload0Expand(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentDownload0Shrink(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentDownload1Expand(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentDownload1Shrink(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentGetAddcontInfoByEntitlementId(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentGetAddcontInfoListByIroTag(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentGetDownloadedStoreCountry(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentGetEntitlementKey(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentGetPftFlag(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentGetRegion(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentRequestPatchInstall(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAppContentTemporaryDataMount(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceHttp2/Export.cpp b/core/libs/prx/libSceHttp2/Export.cpp index af3cde75ba..a19cf5e0d7 100644 --- a/core/libs/prx/libSceHttp2/Export.cpp +++ b/core/libs/prx/libSceHttp2/Export.cpp @@ -68,6 +68,7 @@ struct Http2MemoryPoolStats { static std::mutex g_completionsMutex; static std::unordered_map> g_completions; +struct SceHttp2CookieStats; static void EraseCompletions(const std::vector& ids) { std::lock_guard lock(g_completionsMutex); @@ -381,6 +382,96 @@ int APS5_VABI sceHttp2SetResolveRetry(int id, int32_t retry) { return 0; } +int APS5_VABI sceHttp2AddCookie(int cookieBoxId, const char* url, const char* cookie, std::size_t cookieLength) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2AuthCacheFlush(int libhttpCtxId) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2CookieExport(int cookieBoxId, void* buffer, std::size_t bufferSize, std::size_t* exportSize) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2CookieImport(int cookieBoxId, void* buffer, std::size_t bufferSize) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2GetAuthEnabled() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2GetAutoRedirect() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2GetCookie(int cookieBoxId, const char* url, std::uint8_t* cookie, std::size_t* required, std::size_t prepared) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2GetCookieBox() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2GetCookieStats(int cookieBoxId, SceHttp2CookieStats* stats) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2RedirectCacheFlush(int libhttpCtxId) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2RemoveRequestHeader(int id, const char* name) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetAuthInfoCallback(int templateOrReqid, int (APS5_VABI* callback)(int, int, std::uint8_t*, std::uint8_t*, std::uint8_t*, int, std::uint8_t**, std::size_t*, int*, void*), void* userArg) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetCookieMaxNum(int cookieBoxId, std::uint32_t num) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetCookieMaxNumPerDomain(int cookieBoxId, std::uint32_t num) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetCookieMaxSize(int cookieBoxId, std::uint32_t size) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetCookieRecvCallback(int templateOrReqid, int (APS5_VABI* callback)(int, std::uint8_t*, std::uint8_t*, std::size_t, void*), void* userArg) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetCookieSendCallback(int templateOrReqid, int (APS5_VABI* callback)(int, std::uint8_t*, std::uint8_t*, void*), void* userArg) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHttp2SetPreSendCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI sceHttp2WebSocketCreateRequest() { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceNpParty/Export.cpp b/core/libs/prx/libSceNpParty/Export.cpp index 8440dbddcf..2058b188a0 100644 --- a/core/libs/prx/libSceNpParty/Export.cpp +++ b/core/libs/prx/libSceNpParty/Export.cpp @@ -9,5 +9,105 @@ int APS5_VABI sceNpPartyGetState(void* state) { NotImplemented_nid_no_patch(__func__); return 0; } +int APS5_VABI sceNpPartyCheckCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyCreateA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetMemberInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetMemberInfoA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetMemberSessionInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetMemberVoiceInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetMembers() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetMembersA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetStateAsUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetStateAsUserA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyGetVoiceChatPriority() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyJoin() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyLeave() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyRegisterHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyRegisterHandlerA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyRegisterPrivateHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartySendBinaryMessage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartySetVoiceChatPriority() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyShowInvitationList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyShowInvitationListA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpPartyUnregisterPrivateHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpTrophy2/CMakeLists.txt b/core/libs/prx/libSceNpTrophy2/CMakeLists.txt index a7189fbc13..f8ae9e8c5f 100644 --- a/core/libs/prx/libSceNpTrophy2/CMakeLists.txt +++ b/core/libs/prx/libSceNpTrophy2/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(libSceNpTrophy2 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/src/TrophyPackage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/TitleTrophies.cpp ) +target_sources(libSceNpTrophy2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/PendingExports.cpp) target_include_directories(libSceNpTrophy2 PRIVATE ${LIBS_INCLUDE_DIR} diff --git a/core/libs/prx/libSceNpTrophy2/PendingExports.cpp b/core/libs/prx/libSceNpTrophy2/PendingExports.cpp new file mode 100644 index 0000000000..4d33f6f0d4 --- /dev/null +++ b/core/libs/prx/libSceNpTrophy2/PendingExports.cpp @@ -0,0 +1,101 @@ +#include +#include +#include "SceTypes.hpp" +#include "prx/libc/include/General.hpp" + +extern "C" { + +int APS5_VABI sceNpTrophy2SystemAbortHandle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemBuildGroupIconUri() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemBuildRewardIconUri() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemBuildTrophyIconUri() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemCheckCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemCheckNetSyncTitles() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemCreateContext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemCreateHandle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemDebugLockTrophy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemDebugUnlockTrophy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemDestroyContext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemGetTrophyDetails() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemGetTrophyGroupConf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemGetTrophySetArray() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemGetTrophyTitleConf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemGetTrophyTitleDetails() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemGetTrpIconByUri() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemIsServerAvailable() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemRegisterTitleSyncedCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemRegisterTitleUpdateCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemRemoveAll() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemRemoveTitleData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceNpTrophy2SystemRemoveUserData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +} diff --git a/core/libs/prx/libScePosixForWebKit/Export.cpp b/core/libs/prx/libScePosixForWebKit/Export.cpp index 4ff202b864..a7490b51a7 100644 --- a/core/libs/prx/libScePosixForWebKit/Export.cpp +++ b/core/libs/prx/libScePosixForWebKit/Export.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "prx/libc/include/General.hpp" namespace { @@ -180,4 +181,99 @@ std::uint32_t APS5_VABI arc4random_nid_postfix() { return value; } +int APS5_VABI __wrap_access() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_clock_gettime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_fchown() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_getcwd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_getenv() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_link() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_lstat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_mmap() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_readlink() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_statvfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI __wrap_symlink() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI clock_gettime_np() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI getenv_np() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI gethostbyname_r() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI mmap_np() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI msetname() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI pthread_setschedparam_np() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI setenv_np() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sysinfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceRazorCpu/Export.cpp b/core/libs/prx/libSceRazorCpu/Export.cpp index af49b0fa3c..751f920cd1 100644 --- a/core/libs/prx/libSceRazorCpu/Export.cpp +++ b/core/libs/prx/libSceRazorCpu/Export.cpp @@ -128,4 +128,69 @@ int APS5_VABI sceRazorCpuEndLogicalFileAccess(void) { return 0; } +int APS5_VABI sceRazorCpuFiberLogNameChange(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuFiberSwitch(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuGpuMarkerSync(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuInitializeGpuMarkerContext(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuInitializeInternal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuSetPopMarkerCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuSetPushMarkerCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuSetPushMarkerStaticCallback(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuStartCaptureInternal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuStopCaptureInternal(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuWorkloadRunBegin(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuWorkloadRunEnd(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceRazorCpuWorkloadSubmit(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceSharePlay/Export.cpp b/core/libs/prx/libSceSharePlay/Export.cpp index 9f8cc23fee..216ab34643 100644 --- a/core/libs/prx/libSceSharePlay/Export.cpp +++ b/core/libs/prx/libSceSharePlay/Export.cpp @@ -31,4 +31,84 @@ int APS5_VABI sceSharePlayProhibitController(int prohibited) { return 0; } +int APS5_VABI sceSharePlayCrashDaemon(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayGetCurrentConnectionInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayGetCurrentInfo(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayGetEvent(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayNotifyDialogOpen(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayNotifyForceCloseForCdlg(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayResumeScreenForCdlg(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayServerLock(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayServerUnLock(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlaySetMode(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlaySetProhibition(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlaySetProhibitionModeWithAppId(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayStartStandby(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayStartStreaming(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayStopStandby(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSharePlayStopStreaming(void) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 636719b717..aef49a7795 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -1163,4 +1163,119 @@ void* APS5_VABI sceSslGetSubjectName(int sslCtxId, void* sslCert) { return ExtractCertName(__func__, sslCtxId, sslCert, true); } +int APS5_VABI CA_MGMT_extractKeyBlobEx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI CA_MGMT_extractPublicKeyInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI CA_MGMT_freeKeyBlob() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI CRYPTO_initAsymmetricKey() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI CRYPTO_uninitAsymmetricKey() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI RSA_verifySignature() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI VLONG_freeVlongQueue() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslCheckRecvPending() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslCreateSslConnection() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslDeleteSslConnection() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslDisableOption() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslDisableOptionInternal() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslDisableOptionInternalInsecure() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslEnableOption() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslEnableOptionInternal() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslGetNanoSSLModuleId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslGetPeerCert() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslGetSslError() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslLoadRootCACert() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslRecv(int sslConnectionId, void* buf, std::size_t fulllen) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslReuseConnection(int sslConnectionId, int sockId) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslSend(int sslConnectionId, void* msg, std::size_t fulllen) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSslSetSslVersion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceSysUtil/Export.cpp b/core/libs/prx/libSceSysUtil/Export.cpp index 8cdc46a182..6ff461210b 100644 --- a/core/libs/prx/libSceSysUtil/Export.cpp +++ b/core/libs/prx/libSceSysUtil/Export.cpp @@ -10,4 +10,119 @@ int APS5_VABI sceSysUtilSendSystemNotificationWithText(int32_t type, const char* return 0; } +int APS5_VABI sceSysUtilSendAddressingSystemNotification() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendAddressingSystemNotificationWithDeviceId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendAddressingSystemNotificationWithUserId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendAddressingSystemNotificationWithUserName() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendNotificationRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendNpDebugNotificationRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotification() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotification2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationParamInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithAppId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithAppInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithAppName() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithAppNameRelatedToUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithDeviceId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithDeviceIdRelatedToUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithErrorCode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithParams() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithTextRelatedToUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithUserId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithUserName() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendSystemNotificationWithUserNameInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendTrcCheckNotificationRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSysUtilSendWebDebugNotificationRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceUsbd/Export.cpp b/core/libs/prx/libSceUsbd/Export.cpp index 9518092966..6d791a3166 100644 --- a/core/libs/prx/libSceUsbd/Export.cpp +++ b/core/libs/prx/libSceUsbd/Export.cpp @@ -534,4 +534,105 @@ void APS5_VABI sceUsbdUnrefDevice(void* device) { libusb_unref_device(host); } +std::int32_t APS5_VABI sceUsbdClearHalt(void* deviceHandle, std::uint8_t endpoint) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdDetachKernelDriver(void* deviceHandle, int interfaceNumber) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdEventHandlerActive() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdGetConfigDescriptorByValue(void* device, std::uint8_t configurationValue, void** config) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdGetConfiguration(void* deviceHandle, std::int32_t* configuration) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdGetDescriptor(void* deviceHandle, std::uint8_t descriptorType, std::uint8_t descriptorIndex, std::uint8_t* data, std::int32_t length) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +void* APS5_VABI sceUsbdGetDevice(void* deviceHandle) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUsbdGetDeviceSpeed(void* device) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdGetMaxIsoPacketSize(void* device, std::uint8_t endpoint) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdGetMaxPacketSize(void* device, std::uint8_t endpoint) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdGetStringDescriptorAscii(void* deviceHandle, std::uint8_t descriptorIndex, std::uint8_t* data, std::int32_t length) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdHandleEvents() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdHandleEventsLocked(UsbdTimeval* timeout) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +void APS5_VABI sceUsbdLockEventWaiters() { + NotImplemented_nid_no_patch(__func__); +} + +void APS5_VABI sceUsbdLockEvents() { + NotImplemented_nid_no_patch(__func__); +} + +void* APS5_VABI sceUsbdOpenDeviceWithVidPid(std::uint16_t vendorId, std::uint16_t productId) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdSetInterfaceAltSetting(void* deviceHandle, int interfaceNumber, int alternateSetting) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +std::int32_t APS5_VABI sceUsbdTryLockEvents() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +void APS5_VABI sceUsbdUnlockEventWaiters() { + NotImplemented_nid_no_patch(__func__); +} + +void APS5_VABI sceUsbdUnlockEvents() { + NotImplemented_nid_no_patch(__func__); +} + +std::int32_t APS5_VABI sceUsbdWaitForEvent(UsbdTimeval* timeout) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + } diff --git a/core/libs/prx/libSceVrTracker/Export.cpp b/core/libs/prx/libSceVrTracker/Export.cpp index c2e4e7c8f3..b68062f19b 100644 --- a/core/libs/prx/libSceVrTracker/Export.cpp +++ b/core/libs/prx/libSceVrTracker/Export.cpp @@ -29,5 +29,101 @@ int APS5_VABI sceVrTrackerInit() { NotImplemented_nid_no_patch(__func__); return 0; } +int APS5_VABI sceVrTrackerCpuPopMarker() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerCpuPushMarker() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerGetLiveCaptureId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerGetPlayAreaWarningInfo(void* info) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerGpuWaitAndCpuProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerNotifyEndOfCpuProcess(const void* param) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerQueryMemory(const void* param, void* result) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerRecalibrate(const void* param) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerRegisterDevice(int device_type, int handle) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerRegisterDevice2(int device_type, int handle) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerRegisterDeviceInternal(int device_type, int handle, int unk0, int unk1) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerResetAll() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerResetOrientationRelative(int device_type, int handle) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerSaveInternalBuffers() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerSetDeviceRejection() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerSetDurationUntilStatusNotTracking(int device_type, std::uint32_t duration_camera_frames) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerSetExtendedMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerSetLEDBrightness() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerSetRestingMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerStartLiveCapture() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerStopLiveCapture() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerTerm() { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerUnregisterDevice(int handle) { + NotImplemented_nid_no_patch(__func__); + return 0; +} +int APS5_VABI sceVrTrackerUpdateMotionSensorData(const void* param) { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index bf943bde0e..5a11f943ff 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -2,6 +2,8 @@ ### Unknown function info +- [sceAppContentAddcontEnqueueDownloadByEntitlemetId, sceAppContentAddcontEnqueueDownloadSp, sceAppContentAddcontMountByEntitlemetId, sceAppContentAddcontShrink, sceAppContentAppParamGetString, sceAppContentDownload0Expand, sceAppContentDownload0Shrink, sceAppContentDownload1Expand, sceAppContentDownload1Shrink](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - the list is the intersection of the export table of the console's [libSceAppContent](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppContent.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. +- [sceAppContentGetAddcontInfoByEntitlementId, sceAppContentGetAddcontInfoListByIroTag, sceAppContentGetDownloadedStoreCountry, sceAppContentGetEntitlementKey, sceAppContentGetPftFlag, sceAppContentGetRegion, sceAppContentRequestPatchInstall, sceAppContentTemporaryDataMount](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - the list is the intersection of the export table of the console's [libSceAppContent](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppContent.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. - [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 - [sceAppContentDownloadDataFormat](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - empties the `/download0` 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 diff --git a/docs/dev/debt/libSceHttp2.md b/docs/dev/debt/libSceHttp2.md index 5327cfe772..b6006c0489 100644 --- a/docs/dev/debt/libSceHttp2.md +++ b/docs/dev/debt/libSceHttp2.md @@ -12,6 +12,8 @@ - [sceHttp2SetResolveRetry](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signature `(int id, int32_t retry)` from libSceHttp's `sceHttpSetResolveRetry`; the value is accepted and ignored like the other Http2 settings, since no request reaches the network - [sceHttp2WebSocketCreateRequest, sceHttp2WebSocketCloseAsync, sceHttp2WebSocketSendTextMessageAsync, sceHttp2WebSocketSendDataMessageAsync](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA16064 and not called during its start-up; unknown signatures - [libSceHttp2 cookie boxes](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - `sceHttp2CreateCookieBox` is taken to take the library context id first, as in the [prosper](https://github.com/mattias800/prosper) reimplementation; further arguments are unknown and ignored. `sceHttp2SetRequestNoContentLength` is assumed to take only the request id. Cookies are never stored, so `sceHttp2CookieFlush` has nothing to discard +- [sceHttp2AddCookie, sceHttp2AuthCacheFlush, sceHttp2CookieExport, sceHttp2CookieImport, sceHttp2GetCookie, sceHttp2GetCookieStats, sceHttp2RedirectCacheFlush, sceHttp2RemoveRequestHeader, sceHttp2SetAuthInfoCallback, sceHttp2SetCookieMaxNum, sceHttp2SetCookieMaxNumPerDomain, sceHttp2SetCookieMaxSize, sceHttp2SetCookieRecvCallback, sceHttp2SetCookieSendCallback](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signatures follow [SharpProspero's HTTP2 bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http2.cs#L173-L317), but are not measured on a console; each call throws +- [sceHttp2GetAuthEnabled, sceHttp2GetAutoRedirect, sceHttp2GetCookieBox, sceHttp2SetPreSendCallback](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceHttp2.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. ### Functional diff --git a/docs/dev/debt/libSceNpParty.md b/docs/dev/debt/libSceNpParty.md new file mode 100644 index 0000000000..94d38c98e9 --- /dev/null +++ b/docs/dev/debt/libSceNpParty.md @@ -0,0 +1,7 @@ +# libSceNpParty technical debt + +### Unknown function info + +- [sceNpPartyCheckCallback, sceNpPartyCreate, sceNpPartyCreateA, sceNpPartyGetId, sceNpPartyGetMemberInfo, sceNpPartyGetMemberInfoA, sceNpPartyGetMemberSessionInfo, sceNpPartyGetMemberVoiceInfo, sceNpPartyGetMembers](../../../core/libs/prx/libSceNpParty/Export.cpp) (libSceNpParty) - the list is the intersection of the export table of the console's libSceNpParty (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpParty.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. +- [sceNpPartyGetMembersA, sceNpPartyGetStateAsUser, sceNpPartyGetStateAsUserA, sceNpPartyGetVoiceChatPriority, sceNpPartyInitialize, sceNpPartyJoin, sceNpPartyLeave, sceNpPartyRegisterHandler](../../../core/libs/prx/libSceNpParty/Export.cpp) (libSceNpParty) - the list is the intersection of the export table of the console's libSceNpParty (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpParty.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. +- [sceNpPartyRegisterHandlerA, sceNpPartyRegisterPrivateHandler, sceNpPartySendBinaryMessage, sceNpPartySetVoiceChatPriority, sceNpPartyShowInvitationList, sceNpPartyShowInvitationListA, sceNpPartyTerminate, sceNpPartyUnregisterPrivateHandler](../../../core/libs/prx/libSceNpParty/Export.cpp) (libSceNpParty) - the list is the intersection of the export table of the console's libSceNpParty (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpParty.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. diff --git a/docs/dev/debt/libSceNpTrophy2.md b/docs/dev/debt/libSceNpTrophy2.md index 33aa1abb92..1756e8ac4c 100644 --- a/docs/dev/debt/libSceNpTrophy2.md +++ b/docs/dev/debt/libSceNpTrophy2.md @@ -10,6 +10,10 @@ - [sceNpTrophy2GetRewardIcon](../../../core/libs/prx/libSceNpTrophy2/src/Context.cpp) (libSceNpTrophy2) takes `(int context, int handle, int reward_id, void* buffer, size_t* size)`, from the call sites in the bundled `Media/Plugins/PSNCore.prx` of PPSA06888, the same shape as `sceNpTrophy2GetTrophyIcon`. The title calls it twice: first with a null buffer to read the size, then with a buffer of that size allocated with `malloc`. The meaning of the third argument is unverified, and the reward icon data is unknown, so every call throws +- [sceNpTrophy2SystemAbortHandle, sceNpTrophy2SystemBuildGroupIconUri, sceNpTrophy2SystemBuildRewardIconUri, sceNpTrophy2SystemBuildTrophyIconUri, sceNpTrophy2SystemCheckCallback, sceNpTrophy2SystemCheckNetSyncTitles, sceNpTrophy2SystemCreateContext, sceNpTrophy2SystemCreateHandle](../../../core/libs/prx/libSceNpTrophy2/PendingExports.cpp) (libSceNpTrophy2) - the list is the intersection of the export table of the console's libSceNpTrophy2 (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTrophy2.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. +- [sceNpTrophy2SystemDebugLockTrophy, sceNpTrophy2SystemDebugUnlockTrophy, sceNpTrophy2SystemDestroyContext, sceNpTrophy2SystemGetTrophyDetails, sceNpTrophy2SystemGetTrophyGroupConf, sceNpTrophy2SystemGetTrophySetArray, sceNpTrophy2SystemGetTrophyTitleConf, sceNpTrophy2SystemGetTrophyTitleDetails](../../../core/libs/prx/libSceNpTrophy2/PendingExports.cpp) (libSceNpTrophy2) - the list is the intersection of the export table of the console's libSceNpTrophy2 (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTrophy2.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. +- [sceNpTrophy2SystemGetTrpIconByUri, sceNpTrophy2SystemIsServerAvailable, sceNpTrophy2SystemRegisterTitleSyncedCallback, sceNpTrophy2SystemRegisterTitleUpdateCallback, sceNpTrophy2SystemRemoveAll, sceNpTrophy2SystemRemoveTitleData, sceNpTrophy2SystemRemoveUserData](../../../core/libs/prx/libSceNpTrophy2/PendingExports.cpp) (libSceNpTrophy2) - the list is the intersection of the export table of the console's libSceNpTrophy2 (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTrophy2.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. + ### Functional - [Trophy queries](../../../core/libs/prx/libSceNpTrophy2/src/TrophyInfo.cpp) report the title's [trophy data](../../../core/libs/prx/libSceNpTrophy2/src/TrophyPackage.cpp) (`sce_sys/trophy2/trophyNN.ucp`) and the unlocks in the [trophy store](../../../core/libs/prx/libSceNpTrophy2/src/TrophyStore.cpp). Without the file, or when it is malformed, they report the old placeholders and `sceNpTrophy2GetTrophyInfoArray` lists only trophy 0. Icons are not read. Hidden trophies report their names and descriptions; whether the console blanks them before the unlock is not known. `progress_percentage` weighs gold 6, silver 2 and bronze 1 and leaves the platinum out, as KytyPS5 does (the ratio of the published PSN trophy points); the console's formula is not known. A trophy or group that is not in the file throws. A context that was not created or was destroyed reports placeholders and no unlocks and returns success; the console's errors for it are unknown. diff --git a/docs/dev/debt/libScePosixForWebKit.md b/docs/dev/debt/libScePosixForWebKit.md index c57da143e9..d404a3b57c 100644 --- a/docs/dev/debt/libScePosixForWebKit.md +++ b/docs/dev/debt/libScePosixForWebKit.md @@ -3,6 +3,7 @@ ### Unknown function info - [gethostbyname](../../../core/libs/prx/libScePosixForWebKit/Resolver.cpp) (libScePosixForWebKit) - numeric IPv4 forms, trailing whitespace and 64-bit accumulation follow FreeBSD 11.4 [inet_aton](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/inet/inet_addr.c); clearing the resolver error on numeric success follows [fakeaddr](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/net/gethostnamadr.c). Exact PS5 behavior is unverified; hostname lookup still uses the host resolver +- [__wrap_access, __wrap_clock_gettime, __wrap_fchown, __wrap_getcwd, __wrap_getenv, __wrap_link, __wrap_lstat, __wrap_mmap, __wrap_readlink, __wrap_statvfs, __wrap_symlink, clock_gettime_np, getenv_np, gethostbyname_r, mmap_np, msetname, pthread_setschedparam_np, setenv_np, sysinfo](../../../core/libs/prx/libScePosixForWebKit/Export.cpp) (libScePosixForWebKit) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePosixForWebKit.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. ### Functional diff --git a/docs/dev/debt/libSceRazorCpu.md b/docs/dev/debt/libSceRazorCpu.md index 3a4d1b15d4..f6f5690d75 100644 --- a/docs/dev/debt/libSceRazorCpu.md +++ b/docs/dev/debt/libSceRazorCpu.md @@ -9,6 +9,8 @@ ### Unknown function info +- [sceRazorCpuFiberLogNameChange, sceRazorCpuFiberSwitch, sceRazorCpuGpuMarkerSync, sceRazorCpuInitializeGpuMarkerContext, sceRazorCpuInitializeInternal, sceRazorCpuSetPopMarkerCallback](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - the list is the intersection of the export table of the console's [libSceRazorCpu](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRazorCpu.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. +- [sceRazorCpuSetPushMarkerCallback, sceRazorCpuSetPushMarkerStaticCallback, sceRazorCpuStartCaptureInternal, sceRazorCpuStopCaptureInternal, sceRazorCpuWorkloadRunBegin, sceRazorCpuWorkloadRunEnd, sceRazorCpuWorkloadSubmit](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - the list is the intersection of the export table of the console's [libSceRazorCpu](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRazorCpu.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. - [sceRazorCpuFlushOccurred](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signature and return values (1 = flush since the last call, 0 = none) from [SharpProspero](https://github.com/SvenGDK/SharpProspero); without a capture nothing is flushed, so it returns 0 and writes 0 cycles to the optional out pointer (whether the real library writes it when nothing was flushed is unknown) - [sceRazorCpuPushMarkerStatic, sceRazorCpuPopMarker](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signatures from PPSA26344's calls; (name, color, flags) assumed for the push; both do nothing without a capture - [sceRazorCpuPlotValue, sceRazorCpuWriteBookmark](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signatures (series, float value) and (label, optional description) from [SharpProspero](https://github.com/SvenGDK/SharpProspero), matching PPSA06320's calls; both do nothing without a capture and return 0 diff --git a/docs/dev/debt/libSceSharePlay.md b/docs/dev/debt/libSceSharePlay.md index e6c2086083..167653b1ff 100644 --- a/docs/dev/debt/libSceSharePlay.md +++ b/docs/dev/debt/libSceSharePlay.md @@ -2,6 +2,8 @@ ### Unknown function info +- [sceSharePlayCrashDaemon, sceSharePlayGetCurrentConnectionInfo, sceSharePlayGetCurrentInfo, sceSharePlayGetEvent, sceSharePlayNotifyDialogOpen, sceSharePlayNotifyForceCloseForCdlg, sceSharePlayResumeScreenForCdlg, sceSharePlayServerLock](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - the list is the intersection of the export table of the console's [libSceSharePlay](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSharePlay.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. +- [sceSharePlayServerUnLock, sceSharePlaySetMode, sceSharePlaySetProhibition, sceSharePlaySetProhibitionModeWithAppId, sceSharePlayStartStandby, sceSharePlayStartStreaming, sceSharePlayStopStandby, sceSharePlayStopStreaming](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - the list is the intersection of the export table of the console's [libSceSharePlay](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSharePlay.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. - [sceSharePlayInitialize](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - the library keeps no state, so the heap is not used. A null heap is accepted: PPSA11386 passes a null heap and 0x1800 bytes and stops on any non-zero result. The error for a zero size is unknown, so it throws ### Functional diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 597a2246f7..3b76717ecd 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -2,6 +2,10 @@ ### Unknown function info +- [CA_MGMT_extractKeyBlobEx, CA_MGMT_extractPublicKeyInfo, CA_MGMT_freeKeyBlob, CRYPTO_initAsymmetricKey, CRYPTO_uninitAsymmetricKey, RSA_verifySignature, VLONG_freeVlongQueue, sceSslCheckRecvPending, sceSslCreateSslConnection and sceSslDeleteSslConnection](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); no credible prototypes were found. They are declared without parameters solely to resolve known imports, not as an ABI claim; callers must not call them until signatures are verified. +- [sceSslDisableOption, sceSslDisableOptionInternal, sceSslDisableOptionInternalInsecure, sceSslEnableOption, sceSslEnableOptionInternal, sceSslGetNanoSSLModuleId, sceSslGetPeerCert, sceSslGetSslError, sceSslLoadRootCACert and sceSslSetSslVersion](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); no credible prototypes were found. They are declared without parameters solely to resolve known imports, not as an ABI claim; callers must not call them until signatures are verified. +- [sceSslRecv, sceSslReuseConnection and sceSslSend](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Their parameter shapes follow [SharpProspero's pinned bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs), and are not measured on a PS5. + - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown - [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. `sceSslGetSerialNumber` copies into a caller-allocated output buffer or queries the required length when the buffer is null or capacity is zero; a buffer smaller than the serial number returns `SSL_ERROR_INVALID_ARG` (0x8095177a). Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. An unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. The error codes, the zero-capacity length query, the NUL-terminated truncation of name entry strings, and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 diff --git a/docs/dev/debt/libSceSysUtil.md b/docs/dev/debt/libSceSysUtil.md new file mode 100644 index 0000000000..94f5cccef1 --- /dev/null +++ b/docs/dev/debt/libSceSysUtil.md @@ -0,0 +1,8 @@ +# libSceSysUtil technical debt + +### Unknown function info + +- [sceSysUtilSendAddressingSystemNotification, sceSysUtilSendAddressingSystemNotificationWithDeviceId, sceSysUtilSendAddressingSystemNotificationWithUserId, sceSysUtilSendAddressingSystemNotificationWithUserName, sceSysUtilSendNotificationRequest, sceSysUtilSendNpDebugNotificationRequest](../../../core/libs/prx/libSceSysUtil/Export.cpp) (libSceSysUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSysUtil.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. +- [sceSysUtilSendSystemNotification, sceSysUtilSendSystemNotification2, sceSysUtilSendSystemNotificationParamInit, sceSysUtilSendSystemNotificationWithAppId, sceSysUtilSendSystemNotificationWithAppInfo, sceSysUtilSendSystemNotificationWithAppName](../../../core/libs/prx/libSceSysUtil/Export.cpp) (libSceSysUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSysUtil.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. +- [sceSysUtilSendSystemNotificationWithAppNameRelatedToUser, sceSysUtilSendSystemNotificationWithDeviceId, sceSysUtilSendSystemNotificationWithDeviceIdRelatedToUser, sceSysUtilSendSystemNotificationWithErrorCode, sceSysUtilSendSystemNotificationWithParams, sceSysUtilSendSystemNotificationWithTextRelatedToUser](../../../core/libs/prx/libSceSysUtil/Export.cpp) (libSceSysUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSysUtil.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. +- [sceSysUtilSendSystemNotificationWithUserId, sceSysUtilSendSystemNotificationWithUserName, sceSysUtilSendSystemNotificationWithUserNameInfo, sceSysUtilSendTrcCheckNotificationRequest, sceSysUtilSendWebDebugNotificationRequest](../../../core/libs/prx/libSceSysUtil/Export.cpp) (libSceSysUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSysUtil.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. diff --git a/docs/dev/debt/libSceUsbd.md b/docs/dev/debt/libSceUsbd.md index 85e8ca7ba8..f23c18fd72 100644 --- a/docs/dev/debt/libSceUsbd.md +++ b/docs/dev/debt/libSceUsbd.md @@ -3,3 +3,7 @@ ### Silent stubs - [libSceUsbd](../../../core/libs/prx/libSceUsbd/Export.cpp) passes the calls through to the host with [libusb](https://github.com/libusb/libusb) built from the `3rdparty/libusb-cmake` submodule. Devices, handles and descriptors are libusb's own objects: the device, config, interface and endpoint descriptor layouts of [FreeBSD 11.4 libusb](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libusb/libusb.h) match libusb's field for field, which `static_assert` pins. Each one handed to the title is tracked, and a pointer this library did not return throws. Errors are libusb's codes mapped to `0x80240000 - code`, with `LIBUSB_ERROR_OTHER` at `0x802400FF`, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/usbd/usbd.cpp); the null arguments FreeBSD libusb rejects ([libusb10.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libusb/libusb10.c), [libusb10_desc.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libusb/libusb10_desc.c), [libusb10_io.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libusb/libusb10_io.c)) return `0x80240002`, and `sceUsbdOpen` and `sceUsbdGetActiveConfigDescriptor` throw on a null device, which it dereferences. A submitted transfer is copied into a host transfer whose completion copies the status, actual length and iso packet results back and then calls the title's callback from `sceUsbdHandleEventsTimeout`; transfer flags other than short-not-ok and add-zero-packet throw. `sceUsbdAttachKernelDriver` returns 0 as FreeBSD's stub does, `sceUsbdCheckConnected` reports a device gone when reading its configuration fails with `LIBUSB_ERROR_NO_DEVICE` (FreeBSD asks the kernel directly), and `sceUsbdEventHandlingOk` returns 1. When the host has no USB access, initialization keeps the empty device list and event handling waits for its timeout. Opening, descriptors, string and control requests, interface claims and an asynchronous control transfer were checked against an Xbox 360 controller on Windows; the transfer layout, the argument lists, the error codes and isochronous transfers are unverified on PS5 + +### Unknown function info + +- [sceUsbdClearHalt, sceUsbdDetachKernelDriver, sceUsbdEventHandlerActive, sceUsbdGetConfigDescriptorByValue, sceUsbdGetConfiguration, sceUsbdGetDescriptor, sceUsbdGetDevice, sceUsbdGetDeviceSpeed, sceUsbdGetMaxIsoPacketSize, sceUsbdGetMaxPacketSize, sceUsbdGetStringDescriptorAscii, sceUsbdHandleEvents, sceUsbdHandleEventsLocked, sceUsbdLockEventWaiters, sceUsbdLockEvents, sceUsbdOpenDeviceWithVidPid, sceUsbdSetInterfaceAltSetting, sceUsbdTryLockEvents, sceUsbdUnlockEventWaiters, sceUsbdUnlockEvents, sceUsbdWaitForEvent](../../../core/libs/prx/libSceUsbd/Export.cpp) (libSceUsbd) - signatures follow the console type database in [orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json#L2268-L2322), but their behavior is unknown and each call throws; not measured on a console diff --git a/docs/dev/debt/libSceVoiceChat.md b/docs/dev/debt/libSceVoiceChat.md index 60ad4dc3bb..cdc68c34a1 100644 --- a/docs/dev/debt/libSceVoiceChat.md +++ b/docs/dev/debt/libSceVoiceChat.md @@ -7,4 +7,3 @@ ### Unknown function info - [sceVoiceChatAbortRequest, sceVoiceChatGetActiveChannel, sceVoiceChatGetChannelMemberActiveChannelMatchingState, sceVoiceChatGetChannelMemberVoiceConnectionState, sceVoiceChatGetChatChannelMemberInfoList, sceVoiceChatGetChatGroupMemberInfoList, sceVoiceChatGetMicState, sceVoiceChatGetUserVoiceVolumeGain, sceVoiceChatRegisterChannelEventHandler, sceVoiceChatRegisterDaemonEventHandler, sceVoiceChatRegisterMemberEventHandler, sceVoiceChatRegisterPartyUserStateChangedHandler, sceVoiceChatRegisterVoiceEventHandler, sceVoiceChatRequest0, sceVoiceChatRequest1, sceVoiceChatRequestAcsTts, sceVoiceChatRequestAction, sceVoiceChatRequestSetMemberVolume, sceVoiceChatRequestUpdateGameSessionVoiceChatChannelName, sceVoiceChatRequestUpdatePlayerSessionVoiceChatChannelName, sceVoiceChatSetUserVoiceVolumeGain, sceVoiceChatStartAccessibilityEvent, sceVoiceChatStartChannelEvent, sceVoiceChatStartDaemonEvent, sceVoiceChatStartMemberEvent, sceVoiceChatStartRangedMemberEvent, sceVoiceChatStartVoiceEvent, sceVoiceChatStopAccessibilityEvent, sceVoiceChatStopChannelEvent, sceVoiceChatStopDaemonEvent, sceVoiceChatStopMemberEvent, sceVoiceChatStopRangedMemberEvent, sceVoiceChatStopVoiceEvent, sceVoiceChatUnregisterAccessibilityEventHandler, sceVoiceChatUnregisterChannelEventHandler, sceVoiceChatUnregisterDaemonEventHandler, sceVoiceChatUnregisterMemberEventHandler, sceVoiceChatUnregisterMicEventHandler, sceVoiceChatUnregisterPartyUserStateChangedHandler, sceVoiceChatUnregisterRangedMemberEventHandler, sceVoiceChatUnregisterVoiceEventHandler](../../../core/libs/prx/libSceVoiceChat/Export.cpp) (libSceVoiceChat) - parameter layouts are inferred; offline, handlers and event controls return 0, query calls, volume gain controls, and requests return `SIGNED_OUT` (0x80550006) without modifying guest buffers -- [sceVoiceChatRequestCreateVoiceChatGroup, sceVoiceChatRequestJoinVoiceChatGroup, sceVoiceChatRequestLeaveVoiceChatGroup, sceVoiceChatRequestDeleteVoiceChatGroup](../../../core/libs/prx/libSceVoiceChat/Export.cpp) (libSceVoiceChat) - assumed to take a request id and two pointers like the other `sceVoiceChatRequest*` calls; offline, every argument is ignored and `SIGNED_OUT` is returned, as for those diff --git a/docs/dev/debt/libSceVrTracker.md b/docs/dev/debt/libSceVrTracker.md index 918f6cc2d4..7180a455a7 100644 --- a/docs/dev/debt/libSceVrTracker.md +++ b/docs/dev/debt/libSceVrTracker.md @@ -3,3 +3,7 @@ ### Unknown function info - [sceVrTrackerCpuProcess, sceVrTrackerGetResult, sceVrTrackerGetTime, sceVrTrackerGpuSubmit, sceVrTrackerGpuWait and sceVrTrackerInit](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [sceVrTrackerCpuPopMarker, sceVrTrackerCpuPushMarker, sceVrTrackerGetLiveCaptureId, sceVrTrackerGpuWaitAndCpuProcess, sceVrTrackerResetAll, sceVrTrackerSaveInternalBuffers and sceVrTrackerSetDeviceRejection](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. +- [sceVrTrackerSetExtendedMode, sceVrTrackerSetLEDBrightness, sceVrTrackerSetRestingMode, sceVrTrackerStartLiveCapture, sceVrTrackerStopLiveCapture and sceVrTrackerTerm](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. +- [sceVrTrackerGetPlayAreaWarningInfo, sceVrTrackerNotifyEndOfCpuProcess, sceVrTrackerQueryMemory, sceVrTrackerRecalibrate, sceVrTrackerRegisterDevice and sceVrTrackerRegisterDevice2](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Parameter shapes follow the [pinned shadPS4 declarations](https://github.com/shadps4-emu/shadPS4/blob/f9d07487dd85bb7dc522df6de109b8ad4b9b7175/src/core/libraries/vr_tracker/vr_tracker.h), not measured on a PS5. +- [sceVrTrackerRegisterDeviceInternal, sceVrTrackerResetOrientationRelative, sceVrTrackerSetDurationUntilStatusNotTracking, sceVrTrackerUnregisterDevice and sceVrTrackerUpdateMotionSensorData](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Parameter shapes follow the [pinned shadPS4 declarations](https://github.com/shadps4-emu/shadPS4/blob/f9d07487dd85bb7dc522df6de109b8ad4b9b7175/src/core/libraries/vr_tracker/vr_tracker.h), not measured on a PS5. From 63af1bfcf80cd431abfd9dceca95d73516906fc0 Mon Sep 17 00:00:00 2001 From: juanpimg Date: Tue, 20 Oct 2026 14:21:33 +0200 Subject: [PATCH 081/523] fix(libScePosixForWebKit): export the host-named functions through a renamed helper gethostbyname_r and sysinfo are names of host C library functions. Defining them in libScePosixForWebKit made the library's own call to ::gethostbyname_r in Resolver.cpp bind to the new declaration, and the guest_resolver test failed with "gethostbyname_r not implemented". Export the two NIDs through a helper with the library's own prefix, the way libSceAmpr re-exports the libc functions. --- core/libs/prx/libScePosixForWebKit/Export.cpp | 6 ++++-- docs/dev/debt/libScePosixForWebKit.md | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libScePosixForWebKit/Export.cpp b/core/libs/prx/libScePosixForWebKit/Export.cpp index a7490b51a7..f7e89554b5 100644 --- a/core/libs/prx/libScePosixForWebKit/Export.cpp +++ b/core/libs/prx/libScePosixForWebKit/Export.cpp @@ -246,7 +246,8 @@ int APS5_VABI getenv_np() { return 0; } -int APS5_VABI gethostbyname_r() { +APS5_EXPORT("dt8ermYp-Wc", scePosixForWebKit_gethostbyname_r); +int APS5_VABI scePosixForWebKit_gethostbyname_r() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -271,7 +272,8 @@ int APS5_VABI setenv_np() { return 0; } -int APS5_VABI sysinfo() { +APS5_EXPORT("9Vqxt-qBPB4", scePosixForWebKit_sysinfo); +int APS5_VABI scePosixForWebKit_sysinfo() { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePosixForWebKit.md b/docs/dev/debt/libScePosixForWebKit.md index d404a3b57c..681a24a3d5 100644 --- a/docs/dev/debt/libScePosixForWebKit.md +++ b/docs/dev/debt/libScePosixForWebKit.md @@ -3,7 +3,7 @@ ### Unknown function info - [gethostbyname](../../../core/libs/prx/libScePosixForWebKit/Resolver.cpp) (libScePosixForWebKit) - numeric IPv4 forms, trailing whitespace and 64-bit accumulation follow FreeBSD 11.4 [inet_aton](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/inet/inet_addr.c); clearing the resolver error on numeric success follows [fakeaddr](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/net/gethostnamadr.c). Exact PS5 behavior is unverified; hostname lookup still uses the host resolver -- [__wrap_access, __wrap_clock_gettime, __wrap_fchown, __wrap_getcwd, __wrap_getenv, __wrap_link, __wrap_lstat, __wrap_mmap, __wrap_readlink, __wrap_statvfs, __wrap_symlink, clock_gettime_np, getenv_np, gethostbyname_r, mmap_np, msetname, pthread_setschedparam_np, setenv_np, sysinfo](../../../core/libs/prx/libScePosixForWebKit/Export.cpp) (libScePosixForWebKit) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePosixForWebKit.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. +- [__wrap_access, __wrap_clock_gettime, __wrap_fchown, __wrap_getcwd, __wrap_getenv, __wrap_link, __wrap_lstat, __wrap_mmap, __wrap_readlink, __wrap_statvfs, __wrap_symlink, clock_gettime_np, getenv_np, gethostbyname_r (`scePosixForWebKit_gethostbyname_r`), mmap_np, msetname, pthread_setschedparam_np, setenv_np, sysinfo (`scePosixForWebKit_sysinfo`)](../../../core/libs/prx/libScePosixForWebKit/Export.cpp) (libScePosixForWebKit) - names/NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePosixForWebKit.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); credible prototypes are unknown. These exports use renamed helpers; defining `gethostbyname_r` would interpose on the host function this library calls itself. All are parameterless import stubs, not ABI claims; callers must not use them until signatures are verified. ### Functional From 795961352b7d5c2e13425e427acb81bed74d3981 Mon Sep 17 00:00:00 2001 From: juanpimg Date: Tue, 20 Oct 2026 14:28:40 +0200 Subject: [PATCH 082/523] fix: restore the voice chat debt entry and tidy the new declarations The change removed a technical-debt entry from origin/main that documents four libSceVoiceChat exports it does not touch, left the new include out of the quoted include group, put a forward declaration away from the other type declarations, and cited a prototype source without a line anchor. --- core/libs/prx/libSceHttp2/Export.cpp | 2 +- core/libs/prx/libScePosixForWebKit/Export.cpp | 2 +- docs/dev/debt/libSceSsl.md | 2 +- docs/dev/debt/libSceVoiceChat.md | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceHttp2/Export.cpp b/core/libs/prx/libSceHttp2/Export.cpp index a19cf5e0d7..a444eed8a3 100644 --- a/core/libs/prx/libSceHttp2/Export.cpp +++ b/core/libs/prx/libSceHttp2/Export.cpp @@ -59,6 +59,7 @@ static bool HandleIsLive(int id) { return FindAnyHandle(id) != nullptr; } +struct SceHttp2CookieStats; struct Http2MemoryPoolStats { size_t pool_size; size_t max_inuse_size; @@ -68,7 +69,6 @@ struct Http2MemoryPoolStats { static std::mutex g_completionsMutex; static std::unordered_map> g_completions; -struct SceHttp2CookieStats; static void EraseCompletions(const std::vector& ids) { std::lock_guard lock(g_completionsMutex); diff --git a/core/libs/prx/libScePosixForWebKit/Export.cpp b/core/libs/prx/libScePosixForWebKit/Export.cpp index f7e89554b5..ca47a2ecf7 100644 --- a/core/libs/prx/libScePosixForWebKit/Export.cpp +++ b/core/libs/prx/libScePosixForWebKit/Export.cpp @@ -1,11 +1,11 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libc/include/HostEntropy.hpp" +#include "prx/libc/include/General.hpp" #include #include #include #include #include -#include "prx/libc/include/General.hpp" namespace { diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 3b76717ecd..71ef1ff74e 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -4,7 +4,7 @@ - [CA_MGMT_extractKeyBlobEx, CA_MGMT_extractPublicKeyInfo, CA_MGMT_freeKeyBlob, CRYPTO_initAsymmetricKey, CRYPTO_uninitAsymmetricKey, RSA_verifySignature, VLONG_freeVlongQueue, sceSslCheckRecvPending, sceSslCreateSslConnection and sceSslDeleteSslConnection](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); no credible prototypes were found. They are declared without parameters solely to resolve known imports, not as an ABI claim; callers must not call them until signatures are verified. - [sceSslDisableOption, sceSslDisableOptionInternal, sceSslDisableOptionInternalInsecure, sceSslEnableOption, sceSslEnableOptionInternal, sceSslGetNanoSSLModuleId, sceSslGetPeerCert, sceSslGetSslError, sceSslLoadRootCACert and sceSslSetSslVersion](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); no credible prototypes were found. They are declared without parameters solely to resolve known imports, not as an ABI claim; callers must not call them until signatures are verified. -- [sceSslRecv, sceSslReuseConnection and sceSslSend](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Their parameter shapes follow [SharpProspero's pinned bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs), and are not measured on a PS5. +- [sceSslRecv, sceSslReuseConnection and sceSslSend](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Their parameter shapes follow [SharpProspero's pinned bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs#L222-L262), and are not measured on a PS5. - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown diff --git a/docs/dev/debt/libSceVoiceChat.md b/docs/dev/debt/libSceVoiceChat.md index cdc68c34a1..60ad4dc3bb 100644 --- a/docs/dev/debt/libSceVoiceChat.md +++ b/docs/dev/debt/libSceVoiceChat.md @@ -7,3 +7,4 @@ ### Unknown function info - [sceVoiceChatAbortRequest, sceVoiceChatGetActiveChannel, sceVoiceChatGetChannelMemberActiveChannelMatchingState, sceVoiceChatGetChannelMemberVoiceConnectionState, sceVoiceChatGetChatChannelMemberInfoList, sceVoiceChatGetChatGroupMemberInfoList, sceVoiceChatGetMicState, sceVoiceChatGetUserVoiceVolumeGain, sceVoiceChatRegisterChannelEventHandler, sceVoiceChatRegisterDaemonEventHandler, sceVoiceChatRegisterMemberEventHandler, sceVoiceChatRegisterPartyUserStateChangedHandler, sceVoiceChatRegisterVoiceEventHandler, sceVoiceChatRequest0, sceVoiceChatRequest1, sceVoiceChatRequestAcsTts, sceVoiceChatRequestAction, sceVoiceChatRequestSetMemberVolume, sceVoiceChatRequestUpdateGameSessionVoiceChatChannelName, sceVoiceChatRequestUpdatePlayerSessionVoiceChatChannelName, sceVoiceChatSetUserVoiceVolumeGain, sceVoiceChatStartAccessibilityEvent, sceVoiceChatStartChannelEvent, sceVoiceChatStartDaemonEvent, sceVoiceChatStartMemberEvent, sceVoiceChatStartRangedMemberEvent, sceVoiceChatStartVoiceEvent, sceVoiceChatStopAccessibilityEvent, sceVoiceChatStopChannelEvent, sceVoiceChatStopDaemonEvent, sceVoiceChatStopMemberEvent, sceVoiceChatStopRangedMemberEvent, sceVoiceChatStopVoiceEvent, sceVoiceChatUnregisterAccessibilityEventHandler, sceVoiceChatUnregisterChannelEventHandler, sceVoiceChatUnregisterDaemonEventHandler, sceVoiceChatUnregisterMemberEventHandler, sceVoiceChatUnregisterMicEventHandler, sceVoiceChatUnregisterPartyUserStateChangedHandler, sceVoiceChatUnregisterRangedMemberEventHandler, sceVoiceChatUnregisterVoiceEventHandler](../../../core/libs/prx/libSceVoiceChat/Export.cpp) (libSceVoiceChat) - parameter layouts are inferred; offline, handlers and event controls return 0, query calls, volume gain controls, and requests return `SIGNED_OUT` (0x80550006) without modifying guest buffers +- [sceVoiceChatRequestCreateVoiceChatGroup, sceVoiceChatRequestJoinVoiceChatGroup, sceVoiceChatRequestLeaveVoiceChatGroup, sceVoiceChatRequestDeleteVoiceChatGroup](../../../core/libs/prx/libSceVoiceChat/Export.cpp) (libSceVoiceChat) - assumed to take a request id and two pointers like the other `sceVoiceChatRequest*` calls; offline, every argument is ignored and `SIGNED_OUT` is returned, as for those From 07ed40b7ce306527a55a85f5f534aab901e93615 Mon Sep 17 00:00:00 2001 From: hypermist Date: Wed, 21 Oct 2026 12:12:08 +1300 Subject: [PATCH 083/523] docs(agc): drop the stale zero-slot clause from the FS table entry Slots 1-5 are now written by sceAgcSetStaticBuffer and the sceAgcSetStaticQword family, so staying zero until a title writes them is the modelled console behaviour. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- docs/dev/debt/libSceAgcDriver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index f756ed2ade..cf57c4bb8f 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -187,7 +187,7 @@ - An [image table](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) T# whose surface has no mapped byte faults when selected instead of becoming a zero texture as a direct T# does; what the console returns for it is not measured. - [User-data callees](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - a compute shader's `s_swappc_b64` whose source pair is a user-data pair, or a copy of one made with `s_mov_b32` or `s_mov_b64` in the same block, calls the code at the address that user data holds. At dispatch the callee is read from guest memory up to its `s_setpc_b64` on the link pair, one copy of it is appended per call after the caller's last instruction, and the call is rewritten to `s_call_b64` to that copy. The words after the caller's last instruction are dropped from the recompiled code; in the shaders seen they are metadata or constants the caller reads relative to its program counter, and those reads go to the captured guest code, which linking does not change. A caller that jumps through a table among those words, which the control flow analysis reads from the recompiled code, throws. A copy runs at another program counter than the callee, so a callee that reads its program counter, makes a call, ends the program, jumps through another pair, returns before code it branches to, spans more than 64 KiB or is not readable throws. Callees are read again whenever a dispatch misses the dispatch cache; a dispatch that hits it reuses the linked program, so a title that rewrote a callee in place at the same address would keep running the old code. - [Table callees](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - a compute shader's `s_swappc_b64` whose source pair is two consecutive dwords of an `s_buffer_load_dword*`, or a copy of them made with `s_mov_b32` or `s_mov_b64` in the same block (a conditional branch forward past the call between them is crossed), calls one of the targets that buffer holds when the load's buffer descriptor comes from an `s_load_dwordx4` without SOFFSET from a user-data pointer, or from a pointer pair that two `s_mov_b32` of a literal or inline constant set, and the load's SOFFSET from an `s_lshl_b32` by 2 to 24 bits. The descriptor load may be in an earlier block, such as before a per-lane loop around the call, when it is the only write of the descriptor registers that reaches the buffer load on every path back from it, and no `s_call_b64` or statically resolved call lies on those paths; the paths may run through any `s_swappc_b64` that is not resolved statically, the table call itself included, whose callee is assumed to keep the descriptor registers. The chain compares the target each iteration actually loads, so a callee that changes them can only make a later target miss the chain and fault as an unlinked call. Paths follow fall-through, direct branches and the `s_setpc_b64` jumps whose target the control flow analysis resolves. A path that reaches, before a write of the descriptor registers, the program's first instruction, the entry of an in-program function (an `s_call_b64` target, whose callers are not followed), or an instruction that nothing modelled jumps or falls into fails the search. At dispatch the descriptor and the buffer are read from guest memory, and every non-zero 64-bit value at the loaded offset plus a multiple of the shift's stride, below the descriptor's size (stride times num_records, or num_records bytes with stride 0), is read as a user-data callee and appended once per call. The call becomes a branch to a chain that compares the source pair with each target (`s_cmp_lg_u32` with a literal and `s_cbranch_scc1` per dword) and `s_call_b64`s the copy that matches; no match runs the original `s_swappc_b64` as an unlinked call. There is one chain for each value SCC has at the call, and the chain sets SCC back to that value (`s_cmp_eq_u32 0, 0` or `s_cmp_lg_u32 0, 0`) before the callee or the unlinked call runs. The searched range is the descriptor's whole size, which covers the range the load can reach whether it is checked in bytes or in strides; which one the PS5 uses for `s_buffer_load` was not measured. More than 64 distinct targets, a buffer above 64 MiB or a copy out of `s_call_b64` range throws. As with user-data callees, the buffer is read again only when a dispatch misses the dispatch cache, so a target a title adds to the buffer later is an unlinked call when a cached program runs. -- [AGC FS table](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp) (libSceAgcDriver) - the driver maps 2 MiB at 0xF_E0000000 when it is created and fills the table that libSceAgc keeps at 0xF_E0040000 (six 16-byte V# slots, slot 0 describing eight 16-byte records) the way PS5 12.00 libSceAgcDriver and libSceAgc do, but every record points at one AnyPS5 routine that only returns (`s_setpc_b64` on the link pair). Sony's routines, which order work before a title's `ds_ordered_count`, are not modeled, and the other five slots stay zero. +- [AGC FS table](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp) (libSceAgcDriver) - the driver maps 2 MiB at 0xF_E0000000 when it is created and fills the table that libSceAgc keeps at 0xF_E0040000 (six 16-byte V# slots, slot 0 describing eight 16-byte records) the way PS5 12.00 libSceAgcDriver and libSceAgc do, but every record points at one AnyPS5 routine that only returns (`s_setpc_b64` on the link pair). Sony's routines, which order work before a title's `ds_ordered_count`, are not modeled. - [DB_RENDER_CONTROL resummarize draws](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - HTILE is not modeled, so a RESUMMARIZE_ENABLE draw without depth or stencil tests and without color writes changes no modeled state and draws nothing. A pure resummarize with pass-through D16/D32 depth, no depth writes or bounds test, no stencil plane, no color writes and no running pixel stage also draws nothing; other resummarize, copy or decompress draws throw. - [Comparison-sampled R32 float and R16 unorm textures](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver) whose surface a storage image holds are sampled from a D32_SFLOAT or D16_UNORM image that the GPU refills from the storage image whenever its version moves, because Vulkan compares only depth formats. Only surfaces of at least 1 MiB (`APS5_DEPTH_COPY_MIN_KIB`) that are not 3D take the copy; smaller ones stay CPU snapshots compared against guest memory. With copies of small surfaces, one title hit an intermittent boot-time crash that was not explained. - [Null vertex buffer V#](../../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a V# whose four dwords are all zero is bound to a zeroed vertex buffer of the attribute's size with stride 0, so every component of its attribute reads 0. This follows the V# fields in the [AMD RDNA2 ISA](https://gpuopen.com/download/rdna2-shader-instruction-set-architecture.pdf) (`NUM_RECORDS` 0, `DST_SEL` 0 selects 0), not measured on hardware. From c2b92498b566daa382d996c7928b5298304c95f7 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 14:07:29 +0200 Subject: [PATCH 084/523] fix(libkernel): look up the descriptor of read, write, pread and pwrite before their buffer and offset A null buffer, or for pread and pwrite a negative offset, was answered with EFAULT or EINVAL before the descriptor was looked at, so a descriptor that is not open (EBADF) or a pipe (ESPIPE) was hidden. FreeBSD 11.4 sys_generic.c looks the descriptor up first (kern_readv, kern_preadv: fget, then the seekable check, then the offset) and only reaches the buffer when it copies. The check runs only when the buffer or offset is invalid, so a normal transfer costs nothing. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/File/src/Open.cpp | 4 +- core/libs/prx/libkernel/File/src/Stdio.cpp | 48 ++++++++++++++-------- core/libs/tests/GuestFilesystem.cpp | 20 +++++++++ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libkernel/File/src/Open.cpp b/core/libs/prx/libkernel/File/src/Open.cpp index 186ce63ff3..2aa7cb1d0d 100644 --- a/core/libs/prx/libkernel/File/src/Open.cpp +++ b/core/libs/prx/libkernel/File/src/Open.cpp @@ -313,7 +313,7 @@ std::int64_t APS5_VABI sceKernelRead(int d, void* buf, std::size_t nbytes) { return n < 0 ? SceKernelError(*__error_nid_postfix()) : n; } if (buf == nullptr) { - if (nbytes != 0) return SceIoErrorFromErrno(EFAULT); + if (nbytes != 0) return SceIoErrorFromErrno(DescriptorIsOpen_nid_no_patch(d) ? EFAULT : EBADF); char empty = 0; return sceKernelRead(d, &empty, 0); } @@ -332,7 +332,7 @@ std::int64_t APS5_VABI sceKernelWrite(int d, const void* buf, std::size_t nbytes return n < 0 ? SceKernelError(*__error_nid_postfix()) : n; } if (buf == nullptr) { - if (nbytes != 0) return SceIoErrorFromErrno(EFAULT); + if (nbytes != 0) return SceIoErrorFromErrno(DescriptorIsOpen_nid_no_patch(d) ? EFAULT : EBADF); const char empty = 0; return sceKernelWrite(d, &empty, 0); } diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 2400df7664..0b77a183ce 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -428,6 +428,28 @@ static bool FailPosixPositionedGuestSocket(int descriptor) { return true; } +static bool IsPipeDescriptor(int descriptor) { +#ifdef _WIN32 + const auto handle = reinterpret_cast(WithoutParameterHandler([descriptor] { return ::_get_osfhandle(descriptor); })); + return handle != INVALID_HANDLE_VALUE && ::GetFileType(handle) == FILE_TYPE_PIPE; +#else + struct stat status; + return ::fstat(descriptor, &status) == 0 && S_ISFIFO(status.st_mode); +#endif +} + +static int PositionedArgumentError(int descriptor, bool nullBuffer, std::int64_t offset) { + if (!nullBuffer && offset >= 0) return 0; + if (!GuestSockets::IsOpen(descriptor) && !DescriptorIsOpen_nid_no_patch(descriptor)) return GUEST_EBADF; + if (GuestSockets::IsOpen(descriptor) || IsPipeDescriptor(descriptor)) return GUEST_ESPIPE; + return offset < 0 ? GUEST_EINVAL : GUEST_EFAULT; +} + +static int TransferArgumentError(int descriptor, bool nullBuffer) { + if (!nullBuffer) return 0; + return DescriptorIsOpen_nid_no_patch(descriptor) ? GUEST_EFAULT : GUEST_EBADF; +} + static int SceErrorFromErrno(int error) { int guest = error > 0 && error <= 34 ? error : error == GUEST_ENOTEMPTY ? error : GUEST_EIO; if (error == EAGAIN) guest = GUEST_EWOULDBLOCK; @@ -957,8 +979,7 @@ int APS5_VABI _open_nid_postfix(const char* path, int flags, ...) { int64_t APS5_VABI pread_nid_postfix(int d, void* buf, size_t nbytes, int64_t offset) { if (ExceedsTransferLimit(nbytes)) return PosixFailure(GUEST_EINVAL); - if (buf == nullptr && nbytes != 0) return PosixFailure(GUEST_EFAULT); - if (offset < 0) return PosixFailure(GUEST_EINVAL); + if (const int error = PositionedArgumentError(d, buf == nullptr && nbytes != 0, offset)) return PosixFailure(error); if (FailPosixPositionedGuestSocket(d)) return -1; const GuestArena::HostWrite destination(buf, nbytes); if (!destination.Open()) return PosixFailure(GUEST_EFAULT); @@ -971,8 +992,7 @@ int64_t APS5_VABI pread_nid_postfix(int d, void* buf, size_t nbytes, int64_t off int64_t APS5_VABI pwrite_nid_disambig1_nid_postfix(int d, const void* buf, size_t nbytes, int64_t offset) { if (ExceedsTransferLimit(nbytes)) return PosixFailure(GUEST_EINVAL); - if (buf == nullptr && nbytes != 0) return PosixFailure(GUEST_EFAULT); - if (offset < 0) return PosixFailure(GUEST_EINVAL); + if (const int error = PositionedArgumentError(d, buf == nullptr && nbytes != 0, offset)) return PosixFailure(error); if (FailPosixPositionedGuestSocket(d)) return -1; GuestArena::GuestArenaPrepareHostRead_nid_no_patch(buf, nbytes); char empty = 0; @@ -983,7 +1003,7 @@ int64_t APS5_VABI pwrite_nid_disambig1_nid_postfix(int d, const void* buf, size_ int64_t APS5_VABI read_nid_postfix(int d, void* buf, uint64_t nbytes) { if (ExceedsTransferLimit(nbytes)) return PosixFailure(GUEST_EINVAL); if (GuestSockets::IsOpen(d)) return GuestSockets::Read(d, buf, static_cast(nbytes)); - if (buf == nullptr && nbytes != 0) return PosixFailure(GUEST_EFAULT); + if (const int error = TransferArgumentError(d, buf == nullptr && nbytes != 0)) return PosixFailure(error); char emptyBuffer = 0; return PosixResult(sceKernelRead(d, buf == nullptr ? &emptyBuffer : buf, static_cast(nbytes))); } @@ -991,7 +1011,7 @@ int64_t APS5_VABI read_nid_postfix(int d, void* buf, uint64_t nbytes) { std::int64_t APS5_VABI _read_nid_postfix(int descriptor, void* buffer, std::size_t count) { if (ExceedsTransferLimit(count)) return PosixFailure(GUEST_EINVAL); if (GuestSockets::IsOpen(descriptor)) return GuestSockets::Read(descriptor, buffer, count); - if (buffer == nullptr && count != 0) return PosixFailure(GUEST_EFAULT); + if (const int error = TransferArgumentError(descriptor, buffer == nullptr && count != 0)) return PosixFailure(error); char emptyBuffer = 0; return PosixResult(sceKernelRead(descriptor, buffer == nullptr ? &emptyBuffer : buffer, count)); } @@ -999,7 +1019,7 @@ std::int64_t APS5_VABI _read_nid_postfix(int descriptor, void* buffer, std::size int64_t APS5_VABI write_nid_postfix(int d, const char* str, int64_t size) { if (size < 0) return PosixFailure(GUEST_EINVAL); if (GuestSockets::IsOpen(d)) return GuestSockets::Write(d, str, static_cast(size)); - if (str == nullptr && size != 0) return PosixFailure(GUEST_EFAULT); + if (const int error = TransferArgumentError(d, str == nullptr && size != 0)) return PosixFailure(error); const char emptyBuffer = 0; return PosixResult(sceKernelWrite(d, str == nullptr ? &emptyBuffer : str, static_cast(size))); } @@ -1007,7 +1027,7 @@ int64_t APS5_VABI write_nid_postfix(int d, const char* str, int64_t size) { std::int64_t APS5_VABI _write_nid_postfix(int descriptor, const void* buffer, std::size_t count) { if (ExceedsTransferLimit(count)) return PosixFailure(GUEST_EINVAL); if (GuestSockets::IsOpen(descriptor)) return GuestSockets::Write(descriptor, buffer, count); - if (buffer == nullptr && count != 0) return PosixFailure(GUEST_EFAULT); + if (const int error = TransferArgumentError(descriptor, buffer == nullptr && count != 0)) return PosixFailure(error); const char emptyBuffer = 0; return PosixResult(sceKernelWrite(descriptor, buffer == nullptr ? &emptyBuffer : buffer, count)); } @@ -1220,8 +1240,7 @@ static void PrepareWriteIovecs(const KernelIovec* iov, int iovcnt) { #ifdef _WIN32 int64_t APS5_VABI sceKernelPread(int d, void* buf, size_t nbytes, int64_t offset) { - if (buf == nullptr && nbytes != 0) return SceErrorFromErrno(GUEST_EFAULT); - if (offset < 0) return SceErrorFromErrno(GUEST_EINVAL); + if (const int error = PositionedArgumentError(d, buf == nullptr && nbytes != 0, offset)) return SceErrorFromErrno(error); if (const auto error = PositionedGuestSocketError(d); error != 0) return error; const GuestArena::HostWrite destination(buf, nbytes); if (!destination.Open()) return SceErrorFromErrno(GUEST_EFAULT); @@ -1233,8 +1252,7 @@ int64_t APS5_VABI sceKernelPread(int d, void* buf, size_t nbytes, int64_t offset } int64_t APS5_VABI sceKernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset) { - if (buf == nullptr && nbytes != 0) return SceErrorFromErrno(GUEST_EFAULT); - if (offset < 0) return SceErrorFromErrno(GUEST_EINVAL); + if (const int error = PositionedArgumentError(d, buf == nullptr && nbytes != 0, offset)) return SceErrorFromErrno(error); if (const auto error = PositionedGuestSocketError(d); error != 0) return error; GuestArena::GuestArenaPrepareHostRead_nid_no_patch(buf, nbytes); char empty = 0; @@ -1319,8 +1337,7 @@ int64_t APS5_VABI sceKernelPwritev(int d, const KernelIovec* iov, int iovcnt, in #else int64_t APS5_VABI sceKernelPread(int d, void* buf, size_t nbytes, int64_t offset) { - if (buf == nullptr && nbytes != 0) return SceErrorFromErrno(GUEST_EFAULT); - if (offset < 0) return SceErrorFromErrno(GUEST_EINVAL); + if (const int error = PositionedArgumentError(d, buf == nullptr && nbytes != 0, offset)) return SceErrorFromErrno(error); if (const auto error = PositionedGuestSocketError(d); error != 0) return error; const GuestArena::HostWrite destination(buf, nbytes); if (!destination.Open()) return SceErrorFromErrno(GUEST_EFAULT); @@ -1331,8 +1348,7 @@ int64_t APS5_VABI sceKernelPread(int d, void* buf, size_t nbytes, int64_t offset } int64_t APS5_VABI sceKernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset) { - if (buf == nullptr && nbytes != 0) return SceErrorFromErrno(GUEST_EFAULT); - if (offset < 0) return SceErrorFromErrno(GUEST_EINVAL); + if (const int error = PositionedArgumentError(d, buf == nullptr && nbytes != 0, offset)) return SceErrorFromErrno(error); if (const auto error = PositionedGuestSocketError(d); error != 0) return error; GuestArena::GuestArenaPrepareHostRead_nid_no_patch(buf, nbytes); const auto result = NativePwrite(d, buf, nbytes, offset); diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index ec64c41dfb..82cd95ebdb 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -98,6 +98,8 @@ int APS5_VABI dup2_nid_postfix(int, int); std::int64_t APS5_VABI lseek_nid_postfix(int, std::int64_t, int); std::int64_t APS5_VABI read_nid_postfix(int, void*, std::size_t); std::int64_t APS5_VABI write_nid_postfix(int, const void*, std::size_t); +std::int64_t APS5_VABI pread_nid_postfix(int, void*, std::size_t, std::int64_t); +std::int64_t APS5_VABI sceKernelWrite(int, const void*, std::size_t); int APS5_VABI sceKernelDebugOutText(int, const char*); } #ifdef _WIN32 @@ -1037,6 +1039,24 @@ int main() { Require(lseek_nid_postfix(-1, 0, 7) == -1 && *__error_nid_postfix() == 9); Require(lseek_nid_postfix(0x7fffffff, 0, -1) == -1 && *__error_nid_postfix() == 9); Require(lseek_nid_postfix(locked, 0, 7) == -1 && *__error_nid_postfix() == 22); + { + char transfer[4]{}; + int transferPipe[2] = {-1, -1}; + Require(pipe_nid_postfix(transferPipe) == 0); + Require(read_nid_postfix(-1, nullptr, 1) == -1 && *__error_nid_postfix() == 9); + Require(write_nid_postfix(-1, nullptr, 1) == -1 && *__error_nid_postfix() == 9); + Require(sceKernelRead(-1, nullptr, 1) == static_cast(0x80020009u)); + Require(sceKernelWrite(-1, nullptr, 1) == static_cast(0x80020009u)); + Require(read_nid_postfix(locked, nullptr, 1) == -1 && *__error_nid_postfix() == 14); + Require(pread_nid_postfix(-1, transfer, 1, -1) == -1 && *__error_nid_postfix() == 9); + Require(pread_nid_postfix(-1, nullptr, 1, 0) == -1 && *__error_nid_postfix() == 9); + Require(sceKernelPread(-1, transfer, 1, -1) == static_cast(0x80020009u)); + Require(pread_nid_postfix(transferPipe[0], transfer, 1, -1) == -1 && *__error_nid_postfix() == 29); + Require(pread_nid_postfix(locked, transfer, 1, -1) == -1 && *__error_nid_postfix() == 22); + Require(pread_nid_postfix(locked, nullptr, 1, -1) == -1 && *__error_nid_postfix() == 22); + Require(pread_nid_postfix(locked, nullptr, 1, 0) == -1 && *__error_nid_postfix() == 14); + Require(close_nid_postfix(transferPipe[0]) == 0 && close_nid_postfix(transferPipe[1]) == 0); + } Require(flock_nid_postfix(-1, 2 | 4) == -1 && *__error_nid_postfix() == 9); Require(flock_nid_postfix(0x7fffffff, 1) == -1 && *__error_nid_postfix() == 9); int flockPipe[2] = {-1, -1}; From 53b73b5dae17ab7005c93e766a3dc66fa2e10310 Mon Sep 17 00:00:00 2001 From: JJenkx Date: Tue, 20 Oct 2026 19:53:17 -0400 Subject: [PATCH 085/523] fix(agc): prefer imported prefixes before sparse heap mirrors --- .../Graphics/src/GuestBufferMemory.cpp | 12 +-- .../libSceAgcDriver/tests/BdaResources.cpp | 75 +++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index 7944e8b199..3281d2d62e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -4638,12 +4638,6 @@ void GuestBufferMemory::UploadFinish(bool addressable) { if (profile) refreshUs += microsecondsSince(refreshStart); continue; } - if (sparseHeapCandidate(region)) { - const auto refreshStart = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; - const bool bound = bindSparseMirror(region, importRanges()); - if (profile) refreshUs += microsecondsSince(refreshStart); - if (bound) continue; - } bool copyOnGpu = false; if (context.hostImportAlignment != 0) { auto& state = Imports(); @@ -4730,6 +4724,12 @@ void GuestBufferMemory::UploadFinish(bool addressable) { gpuCopies.push_back(®ion); continue; } + if (sparseHeapCandidate(region)) { + const auto refreshStart = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + const bool bound = bindSparseMirror(region, importRanges()); + if (profile) refreshUs += microsecondsSince(refreshStart); + if (bound) continue; + } if (region.deviceLocal) { // The staging shadow UploadPrepare made cannot take the CPU copy (no host mapping): the // region falls back to a host buffer of its own. diff --git a/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp b/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp index 6d625cf165..c52c6b4a02 100644 --- a/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp @@ -2182,6 +2182,80 @@ void importWatchTests() { #endif } +void sparseImportedHeapTests(const Context& context, const BdaTestAccess& access) { +#ifndef _WIN32 + if (!GuestWriteWatch::GuestWriteWatchAvailable_nid_postfix()) { + std::cout << "write watch unavailable: sparse imported heaps not tested\n"; + return; + } + constexpr std::size_t bytes = 65536; + void* raw = mmap(nullptr, 4 * bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Require(raw != MAP_FAILED, "cannot reserve the sparse imported heap fixture"); + const auto base = (reinterpret_cast(raw) + bytes - 1) & ~(bytes - 1); + auto* guest = reinterpret_cast(base); + Require(mprotect(guest, bytes, PROT_READ | PROT_WRITE) == 0, "cannot commit the sparse imported heap prefix"); + std::memset(guest, 0x57, bytes); + GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(guest, bytes); + auto importing = context; + importing.hostImportAlignment = bytes; + auto* mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix(); + GuestAllocations::GuestAllocationsAdd_nid_postfix(mutation, guest, bytes, true, true, true); + GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation); + const auto* imported = HostImportFor(importing, base, bytes); + Require(imported != nullptr, "the sparse heap prefix was not imported"); + const auto before = MirrorCounters(); + for (unsigned index = 0; index < 3; ++index) { + { + GuestBufferMemory described(importing); + described.AddReadable(base, 2 * bytes); + described.Upload(false); + std::uint32_t adjustment = 0; + const auto view = described.Descriptor(base, 2 * bytes, adjustment); + Require(view.buffer == imported->buffer && view.range == bytes && adjustment == 0, "the sparse descriptor did not bind its imported prefix"); + Require(described.CopiedBytes() == 0 && MirrorCounters().rebuilds == before.rebuilds, "the sparse imported descriptor rebuilt or copied its heap mirror"); + reject([&] { static_cast(described.Descriptor(base + bytes + 16, 4, adjustment)); }, "starts past its copy"); + described.WriteBack(); + } + { + GuestBufferMemory addressed(importing); + addressed.AcquireRegistered(); + addressed.Upload(true); + Require(MirrorCounters().rebuilds == before.rebuilds && MirrorCounters().heapMirrors == before.heapMirrors, "alternating descriptor and address builds rebuilt the imported heap mirror"); + addressed.WriteBack(); + } + } + { + GuestBufferMemory written(importing); + written.AddWritable(base, 2 * bytes); + written.Upload(false); + std::uint32_t adjustment = 0; + const auto view = written.Descriptor(base, 2 * bytes, adjustment); + Require(view.buffer != imported->buffer && view.range == bytes && MirrorCounters().rebuilds == before.rebuilds + 1, "a sparse writable descriptor bound a shortened import"); + access.bytes(view.buffer)[view.offset] = std::byte{0x68}; + guest[8] = 0x79; + written.WriteBack(); + Require(guest[0] == 0x68 && guest[8] == 0x79, "the sparse writable fallback lost a GPU store or rolled back a CPU store"); + } + mutation = GuestAllocations::GuestAllocationsBegin_nid_postfix(); + GuestAllocations::GuestAllocationsRemove_nid_postfix(mutation, guest); + GuestAllocations::GuestAllocationsEnd_nid_postfix(mutation); + Require(HostImportFor(importing, base, bytes) == nullptr, "the sparse heap import outlived its range"); + { + GuestBufferMemory sweep(importing); + sweep.AcquireRegistered(); + sweep.Upload(true); + sweep.WriteBack(); + } + Require(MirrorCounters().heapMirrors == before.heapMirrors, "the sparse imported heap fixture leaked its mirror"); + GuestWriteWatch::GuestWriteWatchUnregister_nid_postfix(guest, bytes); + munmap(raw, 4 * bytes); + std::cout << "Sparse imported heap tests passed\n"; +#else + static_cast(context); + static_cast(access); +#endif +} + void heapMirrorTests(const Context& context, const BdaTestAccess& access) { if (!GuestArena::GuestArenaAvailable_nid_postfix() || !GuestArena::GuestArenaWriteWatched_nid_postfix()) { std::cout << "guest arena unavailable or not write-watched: heap mirrors not tested\n"; @@ -3393,6 +3467,7 @@ void RunBdaResourceTests(const Context& context, const BdaTestAccess& access) { importReportFloorTests(context, access, true); importReportFloorTests(context, access, false); importReportRefusalMemoryTests(context, access); + sparseImportedHeapTests(context, access); heapMirrorTests(context, access); importedHeapMirrorTests(context, access); importWatchTests(); From a7b8c355d0ad980cdc942af234c4d9d5472e4696 Mon Sep 17 00:00:00 2001 From: juanpimg Date: Wed, 21 Oct 2026 02:24:33 +0200 Subject: [PATCH 086/523] fix(prx): drop the exports that #8747 declares The branch declared 222 PS5 3.20 system exports across 11 libraries. Open PR #8747 declares 83 of the same exports (libSceNpParty 25, libSceNpTrophy2 23, libSceHttp2 18, libSceAppContent 17), so drop them to avoid duplication. 139 exports remain across 7 libraries. --- core/libs/prx/libSceAppContent/Export.cpp | 86 --------------- core/libs/prx/libSceHttp2/Export.cpp | 91 ---------------- core/libs/prx/libSceNpParty/Export.cpp | 100 ----------------- core/libs/prx/libSceNpTrophy2/CMakeLists.txt | 1 - .../prx/libSceNpTrophy2/PendingExports.cpp | 101 ------------------ docs/dev/debt/libSceAppContent.md | 2 - docs/dev/debt/libSceHttp2.md | 2 - docs/dev/debt/libSceNpParty.md | 7 -- docs/dev/debt/libSceNpTrophy2.md | 4 - 9 files changed, 394 deletions(-) delete mode 100644 core/libs/prx/libSceNpTrophy2/PendingExports.cpp delete mode 100644 docs/dev/debt/libSceNpParty.md diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index 3027ba0ef9..d7d94203b0 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -190,90 +190,4 @@ int APS5_VABI sceAppContentGetAddcontDownloadProgress(uint32_t service_label, co if (!entitlement_label || !progress) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } - -int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentAddcontEnqueueDownloadSp(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentAddcontMountByEntitlemetId(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentAddcontShrink(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentAppParamGetString(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentDownload0Expand(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentDownload0Shrink(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentDownload1Expand(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentDownload1Shrink(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentGetAddcontInfoByEntitlementId(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentGetAddcontInfoListByIroTag(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentGetDownloadedStoreCountry(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentGetEntitlementKey(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentGetPftFlag(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentGetRegion(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentRequestPatchInstall(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceAppContentTemporaryDataMount(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - } diff --git a/core/libs/prx/libSceHttp2/Export.cpp b/core/libs/prx/libSceHttp2/Export.cpp index a444eed8a3..af3cde75ba 100644 --- a/core/libs/prx/libSceHttp2/Export.cpp +++ b/core/libs/prx/libSceHttp2/Export.cpp @@ -59,7 +59,6 @@ static bool HandleIsLive(int id) { return FindAnyHandle(id) != nullptr; } -struct SceHttp2CookieStats; struct Http2MemoryPoolStats { size_t pool_size; size_t max_inuse_size; @@ -382,96 +381,6 @@ int APS5_VABI sceHttp2SetResolveRetry(int id, int32_t retry) { return 0; } -int APS5_VABI sceHttp2AddCookie(int cookieBoxId, const char* url, const char* cookie, std::size_t cookieLength) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2AuthCacheFlush(int libhttpCtxId) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2CookieExport(int cookieBoxId, void* buffer, std::size_t bufferSize, std::size_t* exportSize) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2CookieImport(int cookieBoxId, void* buffer, std::size_t bufferSize) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2GetAuthEnabled() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2GetAutoRedirect() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2GetCookie(int cookieBoxId, const char* url, std::uint8_t* cookie, std::size_t* required, std::size_t prepared) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2GetCookieBox() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2GetCookieStats(int cookieBoxId, SceHttp2CookieStats* stats) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2RedirectCacheFlush(int libhttpCtxId) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2RemoveRequestHeader(int id, const char* name) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetAuthInfoCallback(int templateOrReqid, int (APS5_VABI* callback)(int, int, std::uint8_t*, std::uint8_t*, std::uint8_t*, int, std::uint8_t**, std::size_t*, int*, void*), void* userArg) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetCookieMaxNum(int cookieBoxId, std::uint32_t num) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetCookieMaxNumPerDomain(int cookieBoxId, std::uint32_t num) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetCookieMaxSize(int cookieBoxId, std::uint32_t size) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetCookieRecvCallback(int templateOrReqid, int (APS5_VABI* callback)(int, std::uint8_t*, std::uint8_t*, std::size_t, void*), void* userArg) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetCookieSendCallback(int templateOrReqid, int (APS5_VABI* callback)(int, std::uint8_t*, std::uint8_t*, void*), void* userArg) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceHttp2SetPreSendCallback() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceHttp2WebSocketCreateRequest() { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceNpParty/Export.cpp b/core/libs/prx/libSceNpParty/Export.cpp index 2058b188a0..8440dbddcf 100644 --- a/core/libs/prx/libSceNpParty/Export.cpp +++ b/core/libs/prx/libSceNpParty/Export.cpp @@ -9,105 +9,5 @@ int APS5_VABI sceNpPartyGetState(void* state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpPartyCheckCallback() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyCreate() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyCreateA() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetId() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetMemberInfo() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetMemberInfoA() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetMemberSessionInfo() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetMemberVoiceInfo() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetMembers() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetMembersA() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetStateAsUser() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetStateAsUserA() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyGetVoiceChatPriority() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyInitialize() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyJoin() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyLeave() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyRegisterHandler() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyRegisterHandlerA() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyRegisterPrivateHandler() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartySendBinaryMessage() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartySetVoiceChatPriority() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyShowInvitationList() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyShowInvitationListA() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyTerminate() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpPartyUnregisterPrivateHandler() { - NotImplemented_nid_no_patch(__func__); - return 0; -} } diff --git a/core/libs/prx/libSceNpTrophy2/CMakeLists.txt b/core/libs/prx/libSceNpTrophy2/CMakeLists.txt index f8ae9e8c5f..a7189fbc13 100644 --- a/core/libs/prx/libSceNpTrophy2/CMakeLists.txt +++ b/core/libs/prx/libSceNpTrophy2/CMakeLists.txt @@ -8,7 +8,6 @@ add_library(libSceNpTrophy2 SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/src/TrophyPackage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/TitleTrophies.cpp ) -target_sources(libSceNpTrophy2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/PendingExports.cpp) target_include_directories(libSceNpTrophy2 PRIVATE ${LIBS_INCLUDE_DIR} diff --git a/core/libs/prx/libSceNpTrophy2/PendingExports.cpp b/core/libs/prx/libSceNpTrophy2/PendingExports.cpp deleted file mode 100644 index 4d33f6f0d4..0000000000 --- a/core/libs/prx/libSceNpTrophy2/PendingExports.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include "SceTypes.hpp" -#include "prx/libc/include/General.hpp" - -extern "C" { - -int APS5_VABI sceNpTrophy2SystemAbortHandle() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemBuildGroupIconUri() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemBuildRewardIconUri() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemBuildTrophyIconUri() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemCheckCallback() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemCheckNetSyncTitles() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemCreateContext() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemCreateHandle() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemDebugLockTrophy() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemDebugUnlockTrophy() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemDestroyContext() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemGetTrophyDetails() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemGetTrophyGroupConf() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemGetTrophySetArray() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemGetTrophyTitleConf() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemGetTrophyTitleDetails() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemGetTrpIconByUri() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemIsServerAvailable() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemRegisterTitleSyncedCallback() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemRegisterTitleUpdateCallback() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemRemoveAll() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemRemoveTitleData() { - NotImplemented_nid_no_patch(__func__); - return 0; -} -int APS5_VABI sceNpTrophy2SystemRemoveUserData() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -} diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index 5a11f943ff..bf943bde0e 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -2,8 +2,6 @@ ### Unknown function info -- [sceAppContentAddcontEnqueueDownloadByEntitlemetId, sceAppContentAddcontEnqueueDownloadSp, sceAppContentAddcontMountByEntitlemetId, sceAppContentAddcontShrink, sceAppContentAppParamGetString, sceAppContentDownload0Expand, sceAppContentDownload0Shrink, sceAppContentDownload1Expand, sceAppContentDownload1Shrink](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - the list is the intersection of the export table of the console's [libSceAppContent](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppContent.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. -- [sceAppContentGetAddcontInfoByEntitlementId, sceAppContentGetAddcontInfoListByIroTag, sceAppContentGetDownloadedStoreCountry, sceAppContentGetEntitlementKey, sceAppContentGetPftFlag, sceAppContentGetRegion, sceAppContentRequestPatchInstall, sceAppContentTemporaryDataMount](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - the list is the intersection of the export table of the console's [libSceAppContent](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppContent.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. - [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 - [sceAppContentDownloadDataFormat](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - empties the `/download0` 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 diff --git a/docs/dev/debt/libSceHttp2.md b/docs/dev/debt/libSceHttp2.md index b6006c0489..5327cfe772 100644 --- a/docs/dev/debt/libSceHttp2.md +++ b/docs/dev/debt/libSceHttp2.md @@ -12,8 +12,6 @@ - [sceHttp2SetResolveRetry](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signature `(int id, int32_t retry)` from libSceHttp's `sceHttpSetResolveRetry`; the value is accepted and ignored like the other Http2 settings, since no request reaches the network - [sceHttp2WebSocketCreateRequest, sceHttp2WebSocketCloseAsync, sceHttp2WebSocketSendTextMessageAsync, sceHttp2WebSocketSendDataMessageAsync](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA16064 and not called during its start-up; unknown signatures - [libSceHttp2 cookie boxes](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - `sceHttp2CreateCookieBox` is taken to take the library context id first, as in the [prosper](https://github.com/mattias800/prosper) reimplementation; further arguments are unknown and ignored. `sceHttp2SetRequestNoContentLength` is assumed to take only the request id. Cookies are never stored, so `sceHttp2CookieFlush` has nothing to discard -- [sceHttp2AddCookie, sceHttp2AuthCacheFlush, sceHttp2CookieExport, sceHttp2CookieImport, sceHttp2GetCookie, sceHttp2GetCookieStats, sceHttp2RedirectCacheFlush, sceHttp2RemoveRequestHeader, sceHttp2SetAuthInfoCallback, sceHttp2SetCookieMaxNum, sceHttp2SetCookieMaxNumPerDomain, sceHttp2SetCookieMaxSize, sceHttp2SetCookieRecvCallback, sceHttp2SetCookieSendCallback](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signatures follow [SharpProspero's HTTP2 bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http2.cs#L173-L317), but are not measured on a console; each call throws -- [sceHttp2GetAuthEnabled, sceHttp2GetAutoRedirect, sceHttp2GetCookieBox, sceHttp2SetPreSendCallback](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceHttp2.c) and the [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. ### Functional diff --git a/docs/dev/debt/libSceNpParty.md b/docs/dev/debt/libSceNpParty.md deleted file mode 100644 index 94d38c98e9..0000000000 --- a/docs/dev/debt/libSceNpParty.md +++ /dev/null @@ -1,7 +0,0 @@ -# libSceNpParty technical debt - -### Unknown function info - -- [sceNpPartyCheckCallback, sceNpPartyCreate, sceNpPartyCreateA, sceNpPartyGetId, sceNpPartyGetMemberInfo, sceNpPartyGetMemberInfoA, sceNpPartyGetMemberSessionInfo, sceNpPartyGetMemberVoiceInfo, sceNpPartyGetMembers](../../../core/libs/prx/libSceNpParty/Export.cpp) (libSceNpParty) - the list is the intersection of the export table of the console's libSceNpParty (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpParty.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. -- [sceNpPartyGetMembersA, sceNpPartyGetStateAsUser, sceNpPartyGetStateAsUserA, sceNpPartyGetVoiceChatPriority, sceNpPartyInitialize, sceNpPartyJoin, sceNpPartyLeave, sceNpPartyRegisterHandler](../../../core/libs/prx/libSceNpParty/Export.cpp) (libSceNpParty) - the list is the intersection of the export table of the console's libSceNpParty (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpParty.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. -- [sceNpPartyRegisterHandlerA, sceNpPartyRegisterPrivateHandler, sceNpPartySendBinaryMessage, sceNpPartySetVoiceChatPriority, sceNpPartyShowInvitationList, sceNpPartyShowInvitationListA, sceNpPartyTerminate, sceNpPartyUnregisterPrivateHandler](../../../core/libs/prx/libSceNpParty/Export.cpp) (libSceNpParty) - the list is the intersection of the export table of the console's libSceNpParty (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpParty.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. diff --git a/docs/dev/debt/libSceNpTrophy2.md b/docs/dev/debt/libSceNpTrophy2.md index 1756e8ac4c..33aa1abb92 100644 --- a/docs/dev/debt/libSceNpTrophy2.md +++ b/docs/dev/debt/libSceNpTrophy2.md @@ -10,10 +10,6 @@ - [sceNpTrophy2GetRewardIcon](../../../core/libs/prx/libSceNpTrophy2/src/Context.cpp) (libSceNpTrophy2) takes `(int context, int handle, int reward_id, void* buffer, size_t* size)`, from the call sites in the bundled `Media/Plugins/PSNCore.prx` of PPSA06888, the same shape as `sceNpTrophy2GetTrophyIcon`. The title calls it twice: first with a null buffer to read the size, then with a buffer of that size allocated with `malloc`. The meaning of the third argument is unverified, and the reward icon data is unknown, so every call throws -- [sceNpTrophy2SystemAbortHandle, sceNpTrophy2SystemBuildGroupIconUri, sceNpTrophy2SystemBuildRewardIconUri, sceNpTrophy2SystemBuildTrophyIconUri, sceNpTrophy2SystemCheckCallback, sceNpTrophy2SystemCheckNetSyncTitles, sceNpTrophy2SystemCreateContext, sceNpTrophy2SystemCreateHandle](../../../core/libs/prx/libSceNpTrophy2/PendingExports.cpp) (libSceNpTrophy2) - the list is the intersection of the export table of the console's libSceNpTrophy2 (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTrophy2.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. -- [sceNpTrophy2SystemDebugLockTrophy, sceNpTrophy2SystemDebugUnlockTrophy, sceNpTrophy2SystemDestroyContext, sceNpTrophy2SystemGetTrophyDetails, sceNpTrophy2SystemGetTrophyGroupConf, sceNpTrophy2SystemGetTrophySetArray, sceNpTrophy2SystemGetTrophyTitleConf, sceNpTrophy2SystemGetTrophyTitleDetails](../../../core/libs/prx/libSceNpTrophy2/PendingExports.cpp) (libSceNpTrophy2) - the list is the intersection of the export table of the console's libSceNpTrophy2 (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTrophy2.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. -- [sceNpTrophy2SystemGetTrpIconByUri, sceNpTrophy2SystemIsServerAvailable, sceNpTrophy2SystemRegisterTitleSyncedCallback, sceNpTrophy2SystemRegisterTitleUpdateCallback, sceNpTrophy2SystemRemoveAll, sceNpTrophy2SystemRemoveTitleData, sceNpTrophy2SystemRemoveUserData](../../../core/libs/prx/libSceNpTrophy2/PendingExports.cpp) (libSceNpTrophy2) - the list is the intersection of the export table of the console's libSceNpTrophy2 (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTrophy2.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. - ### Functional - [Trophy queries](../../../core/libs/prx/libSceNpTrophy2/src/TrophyInfo.cpp) report the title's [trophy data](../../../core/libs/prx/libSceNpTrophy2/src/TrophyPackage.cpp) (`sce_sys/trophy2/trophyNN.ucp`) and the unlocks in the [trophy store](../../../core/libs/prx/libSceNpTrophy2/src/TrophyStore.cpp). Without the file, or when it is malformed, they report the old placeholders and `sceNpTrophy2GetTrophyInfoArray` lists only trophy 0. Icons are not read. Hidden trophies report their names and descriptions; whether the console blanks them before the unlock is not known. `progress_percentage` weighs gold 6, silver 2 and bronze 1 and leaves the platinum out, as KytyPS5 does (the ratio of the published PSN trophy points); the console's formula is not known. A trophy or group that is not in the file throws. A context that was not created or was destroyed reports placeholders and no unlocks and returns success; the console's errors for it are unknown. From a473096926a21131a78ab5ec0aa1d25f1c86ba44 Mon Sep 17 00:00:00 2001 From: Felipebmaitan Date: Tue, 20 Oct 2026 10:38:59 -0300 Subject: [PATCH 087/523] feat(libSceRudp): retain and query context options --- core/libs/prx/libSceRudp/Export.cpp | 75 +++++++++++++--- core/libs/tests/GuestRudpContextLifecycle.cpp | 85 ++++++++++++++++++- docs/dev/debt/libSceRudp.md | 2 +- 3 files changed, 147 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 87864f5971..68aedef515 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -20,6 +20,7 @@ constexpr int RUDP_ERROR_NOT_INITIALIZED = static_cast(0x80770001u); constexpr int RUDP_ERROR_ALREADY_INITIALIZED = static_cast(0x80770002u); constexpr int RUDP_ERROR_INVALID_CONTEXT_ID = static_cast(0x80770003u); constexpr int RUDP_ERROR_INVALID_ARGUMENT = static_cast(0x80770004u); +constexpr int RUDP_ERROR_INVALID_OPTION = static_cast(0x80770005u); constexpr int RUDP_ERROR_MEMORY = static_cast(0x80770007u); constexpr int RUDP_ERROR_CONN_RESET = static_cast(0x80770009u); constexpr int RUDP_ERROR_CONN_REFUSED = static_cast(0x8077000Au); @@ -38,12 +39,52 @@ constexpr std::size_t RUDP_STATUS_SIZE = 0xF8; constexpr std::size_t RUDP_STATUS_CURRENT_CONTEXTS = 0x64; constexpr uint16_t RUDP_MAX_SEGMENT_SIZE = 1410; +enum RudpOption { + RUDP_OPTION_MAX_PAYLOAD = 1, + RUDP_OPTION_SNDBUF = 2, + RUDP_OPTION_RCVBUF = 3, + RUDP_OPTION_NODELAY = 4, + RUDP_OPTION_DELIVERY_CRITICAL = 5, + RUDP_OPTION_ORDER_CRITICAL = 6, + RUDP_OPTION_NONBLOCK = 7, + RUDP_OPTION_STREAM = 8, + RUDP_OPTION_CONNECTION_TIMEOUT = 9, + RUDP_OPTION_CLOSE_WAIT_TIMEOUT = 10, + RUDP_OPTION_AGGREGATION_TIMEOUT = 11, + RUDP_OPTION_READ_TIMEOUT = 15, + RUDP_OPTION_WRITE_TIMEOUT = 16, + RUDP_OPTION_FLUSH_TIMEOUT = 17, + RUDP_OPTION_KEEP_ALIVE_INTERVAL = 18, + RUDP_OPTION_KEEP_ALIVE_TIMEOUT = 19, +}; + using GuestRudpEventHandler = void(APS5_VABI*)(int, int, int, void*); std::mutex g_mutex; bool g_inited = false; int g_next_ctx = 0; -std::map g_ctx; // id -> bound +struct RudpContext { + bool bound = false; + std::map options = { + {RUDP_OPTION_MAX_PAYLOAD, 1346}, + {RUDP_OPTION_SNDBUF, 65536}, + {RUDP_OPTION_RCVBUF, 65536}, + {RUDP_OPTION_NODELAY, 0}, + {RUDP_OPTION_DELIVERY_CRITICAL, 1}, + {RUDP_OPTION_ORDER_CRITICAL, 1}, + {RUDP_OPTION_NONBLOCK, 1}, + {RUDP_OPTION_STREAM, 0}, + {RUDP_OPTION_CONNECTION_TIMEOUT, 60000}, + {RUDP_OPTION_CLOSE_WAIT_TIMEOUT, 0}, + {RUDP_OPTION_AGGREGATION_TIMEOUT, 30}, + {RUDP_OPTION_READ_TIMEOUT, 0}, + {RUDP_OPTION_WRITE_TIMEOUT, 0}, + {RUDP_OPTION_FLUSH_TIMEOUT, 0}, + {RUDP_OPTION_KEEP_ALIVE_INTERVAL, 0}, + {RUDP_OPTION_KEEP_ALIVE_TIMEOUT, 0}, + }; +}; +std::map g_ctx; GuestRudpEventHandler g_handler = nullptr; void* g_handler_arg = nullptr; @@ -167,7 +208,7 @@ int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler handler, void* arg, int return RUDP_ERROR_NOT_INITIALIZED; } const int id = g_next_ctx++; - g_ctx[id] = false; + g_ctx.emplace(id, RudpContext{}); *ctx_id = id; return 0; } @@ -183,27 +224,37 @@ int APS5_VABI sceRudpBind(int ctx_id, uint16_t vport, int transport, uint16_t po if (!valid_ctx(ctx_id)) { return RUDP_ERROR_INVALID_CONTEXT_ID; } - g_ctx[ctx_id] = true; + g_ctx.at(ctx_id).bound = true; return 0; } int APS5_VABI sceRudpSetOption(int ctx_id, int option, const void* value, uint32_t len) { - (void)option; - (void)value; - (void)len; std::lock_guard lk(g_mutex); if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; } - return valid_ctx(ctx_id) ? 0 : RUDP_ERROR_INVALID_CONTEXT_ID; + const auto context = g_ctx.find(ctx_id); + if (context == g_ctx.end()) return RUDP_ERROR_INVALID_CONTEXT_ID; + if (option < RUDP_OPTION_MAX_PAYLOAD || option > RUDP_OPTION_KEEP_ALIVE_TIMEOUT) return RUDP_ERROR_INVALID_OPTION; + const auto entry = context->second.options.find(option); + if (entry == context->second.options.end()) NotImplemented_nid_no_patch(__func__); + if (value == nullptr || len != sizeof(entry->second)) return RUDP_ERROR_INVALID_ARGUMENT; + std::memcpy(&entry->second, value, sizeof(entry->second)); + return 0; } int APS5_VABI sceRudpGetOption(int ctx_id, int option, void* value, uint32_t len) { - (void)ctx_id; - (void)option; - (void)value; - (void)len; - NotImplemented_nid_no_patch(__func__); + std::lock_guard lk(g_mutex); + if (!g_inited) { + return RUDP_ERROR_NOT_INITIALIZED; + } + const auto context = g_ctx.find(ctx_id); + if (context == g_ctx.end()) return RUDP_ERROR_INVALID_CONTEXT_ID; + if (option < RUDP_OPTION_MAX_PAYLOAD || option > RUDP_OPTION_KEEP_ALIVE_TIMEOUT) return RUDP_ERROR_INVALID_OPTION; + const auto entry = context->second.options.find(option); + if (entry == context->second.options.end()) NotImplemented_nid_no_patch(__func__); + if (value == nullptr || len != sizeof(entry->second)) return RUDP_ERROR_INVALID_ARGUMENT; + std::memcpy(value, &entry->second, sizeof(entry->second)); return 0; } diff --git a/core/libs/tests/GuestRudpContextLifecycle.cpp b/core/libs/tests/GuestRudpContextLifecycle.cpp index f043d30a99..4776d065ed 100644 --- a/core/libs/tests/GuestRudpContextLifecycle.cpp +++ b/core/libs/tests/GuestRudpContextLifecycle.cpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include extern "C" { using GuestRudpEventHandler = void(APS5_VABI*)(int, int, int, void*); @@ -16,6 +19,7 @@ int APS5_VABI sceRudpSetEventHandler(GuestRudpEventHandler, void*); int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler, void*, int*); int APS5_VABI sceRudpBind(int, std::uint16_t, int, std::uint16_t); int APS5_VABI sceRudpSetOption(int, int, const void*, std::uint32_t); +int APS5_VABI sceRudpGetOption(int, int, void*, std::uint32_t); int APS5_VABI sceRudpInitiate(int, const void*, std::uint32_t, std::uint16_t); int APS5_VABI sceRudpGetContextStatus(int, void*, std::uint32_t); int APS5_VABI sceRudpWrite(int, const void*, std::uint32_t, int); @@ -27,9 +31,72 @@ static void Require(bool value) { if (!value) std::abort(); } static void APS5_VABI Handler(int, int, int, void*) {} +static void CheckOptions(int context) { + constexpr int invalidArgument = static_cast(0x80770004u); + constexpr int invalidOption = static_cast(0x80770005u); + constexpr std::array, 16> defaults{{ + {1, 1346}, {2, 65536}, {3, 65536}, {4, 0}, {5, 1}, {6, 1}, {7, 1}, {8, 0}, + {9, 60000}, {10, 0}, {11, 30}, {15, 0}, {16, 0}, {17, 0}, {18, 0}, {19, 0}, + }}; + int other = -1; + Require(sceRudpCreateContext(Handler, nullptr, &other) == 0); + std::array buffer{}; + for (const auto& [option, initial] : defaults) { + std::uint32_t result = 0; + Require(sceRudpGetOption(context, option, &result, sizeof(result)) == 0); + Require(result == initial); + for (std::uint32_t value : {0u, 1u, 0x80000000u, 0xffffffffu}) { + buffer.fill(0xa5); + std::memcpy(buffer.data() + 1, &value, sizeof(value)); + Require(sceRudpSetOption(context, option, buffer.data() + 1, sizeof(value)) == 0); + buffer.fill(0xa5); + Require(sceRudpGetOption(context, option, buffer.data() + 1, sizeof(value)) == 0); + std::memcpy(&result, buffer.data() + 1, sizeof(result)); + Require(result == value && buffer.front() == 0xa5 && buffer.back() == 0xa5); + Require(sceRudpGetOption(other, option, &result, sizeof(result)) == 0); + Require(result == initial); + } + for (std::uint32_t length : {0u, 1u, 3u, 5u, 8u, std::numeric_limits::max()}) { + Require(sceRudpSetOption(context, option, &initial, length) == invalidArgument); + buffer.fill(0xa5); + Require(sceRudpGetOption(context, option, buffer.data() + 1, length) == invalidArgument); + for (auto byte : buffer) Require(byte == 0xa5); + } + Require(sceRudpSetOption(context, option, nullptr, sizeof(result)) == invalidArgument); + Require(sceRudpGetOption(context, option, nullptr, sizeof(result)) == invalidArgument); + Require(sceRudpGetOption(context, option, &result, sizeof(result)) == 0); + Require(result == 0xffffffffu); + } + for (int option : {std::numeric_limits::min(), -1, 0, 20, std::numeric_limits::max()}) { + Require(sceRudpSetOption(context, option, nullptr, 0) == invalidOption); + buffer.fill(0xa5); + Require(sceRudpGetOption(context, option, buffer.data() + 1, 4) == invalidOption); + for (auto byte : buffer) Require(byte == 0xa5); + } + for (int option : {12, 13, 14}) { + std::uint32_t value = 0xa5a5a5a5u; + bool threw = false; + try { + sceRudpSetOption(context, option, &value, sizeof(value)); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw); + threw = false; + try { + sceRudpGetOption(context, option, &value, sizeof(value)); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw && value == 0xa5a5a5a5u); + } + Require(sceRudpTerminate(other) == 0); +} + int main() { constexpr int notInitialized = static_cast(0x80770001u); constexpr int invalidContextId = static_cast(0x80770003u); + constexpr int invalidOption = static_cast(0x80770005u); constexpr int connRefused = static_cast(0x8077000Au); constexpr int connReset = static_cast(0x80770009u); alignas(16) static unsigned char pool[0x10000]; @@ -45,6 +112,7 @@ int main() { Require(sceRudpSetOption(0, 0, nullptr, 0) == notInitialized); Require(sceRudpSetOption(12345, 0, nullptr, 0) == notInitialized); + Require(sceRudpGetOption(12345, 0, nullptr, 0) == notInitialized); Require(sceRudpInitiate(0, nullptr, 0, 0) == notInitialized); Require(sceRudpInitiate(12345, nullptr, 0, 0) == notInitialized); @@ -71,6 +139,9 @@ int main() { Require(sceRudpBind(12345, 0, 0, 0) == invalidContextId); Require(sceRudpSetOption(12345, 0, nullptr, 0) == invalidContextId); + Require(sceRudpGetOption(12345, 0, nullptr, 0) == invalidContextId); + Require(sceRudpGetOption(-1, 1, nullptr, 4) == invalidContextId); + Require(sceRudpSetOption(-1, 1, nullptr, 4) == invalidContextId); Require(sceRudpInitiate(12345, nullptr, 0, 0) == invalidContextId); @@ -89,7 +160,8 @@ int main() { Require(sceRudpBind(ctx, 0, 0, 0) == 0); - Require(sceRudpSetOption(ctx, 0, nullptr, 0) == 0); + Require(sceRudpSetOption(ctx, 0, nullptr, 0) == invalidOption); + CheckOptions(ctx); Require(sceRudpInitiate(ctx, nullptr, 0, 0) == connRefused); @@ -108,6 +180,15 @@ int main() { Require(sceRudpSetEventHandler(Handler, nullptr) == 0); Require(sceRudpTerminate(ctx) == 0); + Require(sceRudpGetOption(ctx, 1, nullptr, 4) == invalidContextId); + Require(sceRudpSetOption(ctx, 1, nullptr, 4) == invalidContextId); + Require(sceRudpEnd() == 0); + Require(sceRudpGetOption(ctx, 1, nullptr, 4) == notInitialized); + Require(sceRudpSetOption(ctx, 1, nullptr, 4) == notInitialized); + Require(sceRudpInit_nid_postfix(pool, sizeof(pool)) == 0); + Require(sceRudpGetOption(ctx, 1, nullptr, 4) == invalidContextId); + Require(sceRudpCreateContext(Handler, nullptr, &ctx) == 0); + CheckOptions(ctx); Require(sceRudpEnd() == 0); return 0; -} \ No newline at end of file +} diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index 19757221c0..08d63de91a 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -10,7 +10,7 @@ - [sceRudpGetStatus](../../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`. Measured on a PS5 (firmware 13.60): the status is 0xF8 bytes; before `sceRudpInit` every call returns `RUDP_ERROR_NOT_INITIALIZED`, and after it a null pointer or a byte count of 0 or above 0xF8 returns `RUDP_ERROR_INVALID_ARGUMENT`. The layout matches `SceRudpStatus` in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp) with `currentContexts` at 0x64 and `allocs`, `frees`, `memCurrent` and `memPeak` at 0xC8 to 0xD4. Only `currentContexts` is reported: the traffic counters stay 0 because nothing is sent, and the allocator counters stay 0 because the library does not allocate from the title's pool (the console reports 6 allocations and 0x1260 bytes right after `sceRudpInit`, and 4 more allocations and 0x3F0 bytes per context). The console also writes `currentContexts` and the allocator counters when the byte count is smaller than their offset; only the requested bytes are written here. - [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. The `uint16_t*` parameter is inferred: the measurement does not show how many bytes the console writes, PPSA03490 passes a pointer to a 16-bit value and [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.cpp) declares the PS3 `cellRudpSetMaxSegmentSize(u16 mss)`, so only 16 bits are written -- [sceRudpGetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) takes `(int ctx_id, int option, void* value, uint32_t len)`, taken from the bundled `Media/Plugins/SceRudp.prx` of PPSA06888: its two exported wrappers pass their first three arguments through unchanged and set `len` to 4, the same shape as its `sceRudpSetOption` call, which passes a 4-byte value. The option codes, the defaults and the error results are unknown, so it still throws +- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - PS5 option IDs, defaults, ranges and validation results are unverified; they follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have no known meaning and throw; last-error option 14 also throws because context operations do not maintain it. The getter's `(int ctx_id, int option, void* value, uint32_t len)` matches PPSA06888's `Media/Plugins/SceRudp.prx` wrappers, which set `len` to 4. Other options retain and return all 32 value bits per context, without range checks as in Vita3K. No connection can be established, so stored settings cannot affect network traffic - PPSA06888 imports with unknown signatures: [sceRudpGetRemoteInfo](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp), imported by its bundled `Media/Plugins/SceRudp.prx` and not called during start-up. The third argument is a pointer to a 4-byte buffer seeded from the byte its wrapper reads through the second argument, which does not show what the arguments mean. The managed `SceRudp.Context.GetRemoteInfo()` in the title's Unity metadata takes no parameters and returns a `SceRudp.AddrAndVport`; its native arguments are not visible there. Names from the NID table of [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp), which declares them without parameters too - [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. The signatures, the 8-byte `SceRudpPollEvent`, the operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once - [sceRudpAccept, sceRudpFlush, sceRudpGetLocalInfo, sceRudpListen, sceRudpPollCancel and sceRudpSetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRudp.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From a006418d72d81949df30d126f34a09460f75f7c9 Mon Sep 17 00:00:00 2001 From: Felipebmaitan Date: Tue, 20 Oct 2026 13:59:42 -0300 Subject: [PATCH 088/523] style(libSceRudp): use camelCase context arguments --- core/libs/prx/libSceRudp/Export.cpp | 20 ++++++++++---------- docs/dev/debt/libSceRudp.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 68aedef515..e7cf8e9529 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -197,10 +197,10 @@ int APS5_VABI sceRudpSetEventHandler(GuestRudpEventHandler handler, void* arg) { return 0; } -int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler handler, void* arg, int* ctx_id) { +int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler handler, void* arg, int* ctxId) { (void)handler; (void)arg; - if (ctx_id == nullptr) { + if (ctxId == nullptr) { return RUDP_ERROR_INVALID_ARGUMENT; } std::lock_guard lk(g_mutex); @@ -209,11 +209,11 @@ int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler handler, void* arg, int } const int id = g_next_ctx++; g_ctx.emplace(id, RudpContext{}); - *ctx_id = id; + *ctxId = id; return 0; } -int APS5_VABI sceRudpBind(int ctx_id, uint16_t vport, int transport, uint16_t port) { +int APS5_VABI sceRudpBind(int ctxId, uint16_t vport, int transport, uint16_t port) { (void)vport; (void)transport; (void)port; @@ -221,19 +221,19 @@ int APS5_VABI sceRudpBind(int ctx_id, uint16_t vport, int transport, uint16_t po if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; } - if (!valid_ctx(ctx_id)) { + if (!valid_ctx(ctxId)) { return RUDP_ERROR_INVALID_CONTEXT_ID; } - g_ctx.at(ctx_id).bound = true; + g_ctx.at(ctxId).bound = true; return 0; } -int APS5_VABI sceRudpSetOption(int ctx_id, int option, const void* value, uint32_t len) { +int APS5_VABI sceRudpSetOption(int ctxId, int option, const void* value, uint32_t len) { std::lock_guard lk(g_mutex); if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; } - const auto context = g_ctx.find(ctx_id); + const auto context = g_ctx.find(ctxId); if (context == g_ctx.end()) return RUDP_ERROR_INVALID_CONTEXT_ID; if (option < RUDP_OPTION_MAX_PAYLOAD || option > RUDP_OPTION_KEEP_ALIVE_TIMEOUT) return RUDP_ERROR_INVALID_OPTION; const auto entry = context->second.options.find(option); @@ -243,12 +243,12 @@ int APS5_VABI sceRudpSetOption(int ctx_id, int option, const void* value, uint32 return 0; } -int APS5_VABI sceRudpGetOption(int ctx_id, int option, void* value, uint32_t len) { +int APS5_VABI sceRudpGetOption(int ctxId, int option, void* value, uint32_t len) { std::lock_guard lk(g_mutex); if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; } - const auto context = g_ctx.find(ctx_id); + const auto context = g_ctx.find(ctxId); if (context == g_ctx.end()) return RUDP_ERROR_INVALID_CONTEXT_ID; if (option < RUDP_OPTION_MAX_PAYLOAD || option > RUDP_OPTION_KEEP_ALIVE_TIMEOUT) return RUDP_ERROR_INVALID_OPTION; const auto entry = context->second.options.find(option); diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index 08d63de91a..b269c8769f 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -10,7 +10,7 @@ - [sceRudpGetStatus](../../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`. Measured on a PS5 (firmware 13.60): the status is 0xF8 bytes; before `sceRudpInit` every call returns `RUDP_ERROR_NOT_INITIALIZED`, and after it a null pointer or a byte count of 0 or above 0xF8 returns `RUDP_ERROR_INVALID_ARGUMENT`. The layout matches `SceRudpStatus` in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp) with `currentContexts` at 0x64 and `allocs`, `frees`, `memCurrent` and `memPeak` at 0xC8 to 0xD4. Only `currentContexts` is reported: the traffic counters stay 0 because nothing is sent, and the allocator counters stay 0 because the library does not allocate from the title's pool (the console reports 6 allocations and 0x1260 bytes right after `sceRudpInit`, and 4 more allocations and 0x3F0 bytes per context). The console also writes `currentContexts` and the allocator counters when the byte count is smaller than their offset; only the requested bytes are written here. - [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. The `uint16_t*` parameter is inferred: the measurement does not show how many bytes the console writes, PPSA03490 passes a pointer to a 16-bit value and [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.cpp) declares the PS3 `cellRudpSetMaxSegmentSize(u16 mss)`, so only 16 bits are written -- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - PS5 option IDs, defaults, ranges and validation results are unverified; they follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have no known meaning and throw; last-error option 14 also throws because context operations do not maintain it. The getter's `(int ctx_id, int option, void* value, uint32_t len)` matches PPSA06888's `Media/Plugins/SceRudp.prx` wrappers, which set `len` to 4. Other options retain and return all 32 value bits per context, without range checks as in Vita3K. No connection can be established, so stored settings cannot affect network traffic +- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - PS5 option IDs, defaults, ranges and validation results are unverified; they follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have no known meaning and throw; last-error option 14 also throws because context operations do not maintain it. The getter's `(int ctxId, int option, void* value, uint32_t len)` matches PPSA06888's `Media/Plugins/SceRudp.prx` wrappers, which set `len` to 4. Other options retain and return all 32 value bits per context, without range checks as in Vita3K. No connection can be established, so stored settings cannot affect network traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. - PPSA06888 imports with unknown signatures: [sceRudpGetRemoteInfo](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp), imported by its bundled `Media/Plugins/SceRudp.prx` and not called during start-up. The third argument is a pointer to a 4-byte buffer seeded from the byte its wrapper reads through the second argument, which does not show what the arguments mean. The managed `SceRudp.Context.GetRemoteInfo()` in the title's Unity metadata takes no parameters and returns a `SceRudp.AddrAndVport`; its native arguments are not visible there. Names from the NID table of [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp), which declares them without parameters too - [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. The signatures, the 8-byte `SceRudpPollEvent`, the operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once - [sceRudpAccept, sceRudpFlush, sceRudpGetLocalInfo, sceRudpListen, sceRudpPollCancel and sceRudpSetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRudp.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From b122786155402a09ce1414a5b1401610b0ea8673 Mon Sep 17 00:00:00 2001 From: Felipebmaitan Date: Tue, 20 Oct 2026 21:41:57 -0300 Subject: [PATCH 089/523] fix(libSceRudp): accept setters for unreadable options --- core/libs/prx/libSceRudp/Export.cpp | 13 ++++++---- core/libs/tests/GuestRudpContextLifecycle.cpp | 26 ++++++++++++++----- docs/dev/debt/libSceRudp.md | 2 +- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index e7cf8e9529..56df07b0ab 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -236,10 +236,10 @@ int APS5_VABI sceRudpSetOption(int ctxId, int option, const void* value, uint32_ const auto context = g_ctx.find(ctxId); if (context == g_ctx.end()) return RUDP_ERROR_INVALID_CONTEXT_ID; if (option < RUDP_OPTION_MAX_PAYLOAD || option > RUDP_OPTION_KEEP_ALIVE_TIMEOUT) return RUDP_ERROR_INVALID_OPTION; - const auto entry = context->second.options.find(option); - if (entry == context->second.options.end()) NotImplemented_nid_no_patch(__func__); - if (value == nullptr || len != sizeof(entry->second)) return RUDP_ERROR_INVALID_ARGUMENT; - std::memcpy(&entry->second, value, sizeof(entry->second)); + uint32_t optionValue; + if (value == nullptr || len != sizeof(optionValue)) return RUDP_ERROR_INVALID_ARGUMENT; + std::memcpy(&optionValue, value, sizeof(optionValue)); + context->second.options.insert_or_assign(option, optionValue); return 0; } @@ -252,7 +252,10 @@ int APS5_VABI sceRudpGetOption(int ctxId, int option, void* value, uint32_t len) if (context == g_ctx.end()) return RUDP_ERROR_INVALID_CONTEXT_ID; if (option < RUDP_OPTION_MAX_PAYLOAD || option > RUDP_OPTION_KEEP_ALIVE_TIMEOUT) return RUDP_ERROR_INVALID_OPTION; const auto entry = context->second.options.find(option); - if (entry == context->second.options.end()) NotImplemented_nid_no_patch(__func__); + if (entry == context->second.options.end() || + (option > RUDP_OPTION_AGGREGATION_TIMEOUT && option < RUDP_OPTION_READ_TIMEOUT)) { + NotImplemented_nid_no_patch(__func__); + } if (value == nullptr || len != sizeof(entry->second)) return RUDP_ERROR_INVALID_ARGUMENT; std::memcpy(value, &entry->second, sizeof(entry->second)); return 0; diff --git a/core/libs/tests/GuestRudpContextLifecycle.cpp b/core/libs/tests/GuestRudpContextLifecycle.cpp index 4776d065ed..c3045181fb 100644 --- a/core/libs/tests/GuestRudpContextLifecycle.cpp +++ b/core/libs/tests/GuestRudpContextLifecycle.cpp @@ -76,19 +76,31 @@ static void CheckOptions(int context) { for (int option : {12, 13, 14}) { std::uint32_t value = 0xa5a5a5a5u; bool threw = false; - try { - sceRudpSetOption(context, option, &value, sizeof(value)); - } catch (const std::runtime_error&) { - threw = true; - } - Require(threw); - threw = false; try { sceRudpGetOption(context, option, &value, sizeof(value)); } catch (const std::runtime_error&) { threw = true; } Require(threw && value == 0xa5a5a5a5u); + for (std::uint32_t input : {0u, 1u, 0x80000000u, 0xffffffffu}) { + buffer.fill(0xa5); + std::memcpy(buffer.data() + 1, &input, sizeof(input)); + Require(sceRudpSetOption(context, option, buffer.data() + 1, sizeof(input)) == 0); + Require(buffer.front() == 0xa5 && buffer.back() == 0xa5); + for (int current : {context, other}) { + threw = false; + try { + sceRudpGetOption(current, option, &value, sizeof(value)); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw && value == 0xa5a5a5a5u); + } + } + for (std::uint32_t length : {0u, 1u, 3u, 5u, 8u, std::numeric_limits::max()}) { + Require(sceRudpSetOption(context, option, &value, length) == invalidArgument); + } + Require(sceRudpSetOption(context, option, nullptr, sizeof(value)) == invalidArgument); } Require(sceRudpTerminate(other) == 0); } diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index b269c8769f..2e8a365d5f 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -10,7 +10,7 @@ - [sceRudpGetStatus](../../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`. Measured on a PS5 (firmware 13.60): the status is 0xF8 bytes; before `sceRudpInit` every call returns `RUDP_ERROR_NOT_INITIALIZED`, and after it a null pointer or a byte count of 0 or above 0xF8 returns `RUDP_ERROR_INVALID_ARGUMENT`. The layout matches `SceRudpStatus` in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp) with `currentContexts` at 0x64 and `allocs`, `frees`, `memCurrent` and `memPeak` at 0xC8 to 0xD4. Only `currentContexts` is reported: the traffic counters stay 0 because nothing is sent, and the allocator counters stay 0 because the library does not allocate from the title's pool (the console reports 6 allocations and 0x1260 bytes right after `sceRudpInit`, and 4 more allocations and 0x3F0 bytes per context). The console also writes `currentContexts` and the allocator counters when the byte count is smaller than their offset; only the requested bytes are written here. - [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. The `uint16_t*` parameter is inferred: the measurement does not show how many bytes the console writes, PPSA03490 passes a pointer to a 16-bit value and [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.cpp) declares the PS3 `cellRudpSetMaxSegmentSize(u16 mss)`, so only 16 bits are written -- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - PS5 option IDs, defaults, ranges and validation results are unverified; they follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have no known meaning and throw; last-error option 14 also throws because context operations do not maintain it. The getter's `(int ctxId, int option, void* value, uint32_t len)` matches PPSA06888's `Media/Plugins/SceRudp.prx` wrappers, which set `len` to 4. Other options retain and return all 32 value bits per context, without range checks as in Vita3K. No connection can be established, so stored settings cannot affect network traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. +- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - PS5 option IDs, defaults, ranges and validation results are unverified; they follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have no known meaning; last-error option 14 is not maintained. Their setters store all 32 bits per context and return 0 for a valid four-byte buffer, preserving acceptance on `main`; their getters still throw. The getter's `(int ctxId, int option, void* value, uint32_t len)` matches PPSA06888's bundled `SceRudp.prx` wrappers, which set `len` to 4; the IDs it sets are unknown. Other options return the stored bits without range checks. No connection can be established, so stored settings cannot affect traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. - PPSA06888 imports with unknown signatures: [sceRudpGetRemoteInfo](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp), imported by its bundled `Media/Plugins/SceRudp.prx` and not called during start-up. The third argument is a pointer to a 4-byte buffer seeded from the byte its wrapper reads through the second argument, which does not show what the arguments mean. The managed `SceRudp.Context.GetRemoteInfo()` in the title's Unity metadata takes no parameters and returns a `SceRudp.AddrAndVport`; its native arguments are not visible there. Names from the NID table of [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp), which declares them without parameters too - [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. The signatures, the 8-byte `SceRudpPollEvent`, the operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once - [sceRudpAccept, sceRudpFlush, sceRudpGetLocalInfo, sceRudpListen, sceRudpPollCancel and sceRudpSetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRudp.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From 733b0fdd4b88463bd0d39aaa70510016e8d0ddc7 Mon Sep 17 00:00:00 2001 From: Felipebmaitan Date: Tue, 20 Oct 2026 22:59:36 -0300 Subject: [PATCH 090/523] docs(libSceRudp): record PPSA06888 option call sites --- docs/dev/debt/libSceRudp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index 2e8a365d5f..f8abf0989f 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -10,7 +10,7 @@ - [sceRudpGetStatus](../../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`. Measured on a PS5 (firmware 13.60): the status is 0xF8 bytes; before `sceRudpInit` every call returns `RUDP_ERROR_NOT_INITIALIZED`, and after it a null pointer or a byte count of 0 or above 0xF8 returns `RUDP_ERROR_INVALID_ARGUMENT`. The layout matches `SceRudpStatus` in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp) with `currentContexts` at 0x64 and `allocs`, `frees`, `memCurrent` and `memPeak` at 0xC8 to 0xD4. Only `currentContexts` is reported: the traffic counters stay 0 because nothing is sent, and the allocator counters stay 0 because the library does not allocate from the title's pool (the console reports 6 allocations and 0x1260 bytes right after `sceRudpInit`, and 4 more allocations and 0x3F0 bytes per context). The console also writes `currentContexts` and the allocator counters when the byte count is smaller than their offset; only the requested bytes are written here. - [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. The `uint16_t*` parameter is inferred: the measurement does not show how many bytes the console writes, PPSA03490 passes a pointer to a 16-bit value and [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.cpp) declares the PS3 `cellRudpSetMaxSegmentSize(u16 mss)`, so only 16 bits are written -- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - PS5 option IDs, defaults, ranges and validation results are unverified; they follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have no known meaning; last-error option 14 is not maintained. Their setters store all 32 bits per context and return 0 for a valid four-byte buffer, preserving acceptance on `main`; their getters still throw. The getter's `(int ctxId, int option, void* value, uint32_t len)` matches PPSA06888's bundled `SceRudp.prx` wrappers, which set `len` to 4; the IDs it sets are unknown. Other options return the stored bits without range checks. No connection can be established, so stored settings cannot affect traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. +- [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - full PS5 option contracts are unverified; defaults and validation follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have unknown meanings; last-error option 14 is not maintained. Their setters store all 32 bits per context and return 0 for valid four-byte buffers, preserving acceptance on `main`; their getters throw. Static inspection of PPSA06888 v01.001.008's bundled Unity transport shows its context-set constructor configuring IDs 2, 3, 5 and 6; runtime usage and other paths remain unverified. The plugin wrappers pass four-byte values. No context can connect, so stored settings cannot affect traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. - PPSA06888 imports with unknown signatures: [sceRudpGetRemoteInfo](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp), imported by its bundled `Media/Plugins/SceRudp.prx` and not called during start-up. The third argument is a pointer to a 4-byte buffer seeded from the byte its wrapper reads through the second argument, which does not show what the arguments mean. The managed `SceRudp.Context.GetRemoteInfo()` in the title's Unity metadata takes no parameters and returns a `SceRudp.AddrAndVport`; its native arguments are not visible there. Names from the NID table of [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp), which declares them without parameters too - [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. The signatures, the 8-byte `SceRudpPollEvent`, the operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once - [sceRudpAccept, sceRudpFlush, sceRudpGetLocalInfo, sceRudpListen, sceRudpPollCancel and sceRudpSetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRudp.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From ee81c5112deb1c7f61f538e29c7d60b310fdb9d7 Mon Sep 17 00:00:00 2001 From: klosixgaming-arch Date: Tue, 20 Oct 2026 22:25:04 -0500 Subject: [PATCH 091/523] feat(agc): draw point-list output of NGG geometry programs as mesh points A geometry program with VGT_GS_OUT_PRIM_TYPE 0 (point list) was refused as an unsupported output assembly, halting Cocoon (PPSA08766) on boot with "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported". Each primitive export of an NGG point-list geometry program outputs one point whose vertex index is stored directly in PrimitivePointIndicesEXT, so the mesh module now declares OutputPoints with PrimitivePointIndicesEXT for it when outputPrimitiveSize is 1. The point primitive export extracts the 9-bit vertex index from the packed primitive export word and stores it to the output array. A subgroup gets up to maxVertices points, with provoking vertex 0 and point raster primitive topology. Tested with Cocoon (PPSA08766) which now progresses past pipeline creation and initial frame draws into gameplay. AI-assisted: yes. Written with AI assistance and reviewed by me. --- .../Graphics/src/ShaderValidation.cpp | 7 +++-- .../libSceAgcDriver/Graphics/src/State.cpp | 9 +++--- .../prx/libSceAgcDriver/tests/Graphics.cpp | 13 +++++++-- .../tests/VertexInputValidation.cpp | 2 +- .../src/SpirvMemory/SpirvModuleSetup.cpp | 2 +- .../SpirvBackend/src/SpirvModuleEmitter.cpp | 29 ++++++++++++------- .../src/ShaderInputInfoBuilder.cpp | 2 +- docs/dev/debt/libSceAgcDriver.md | 3 +- 8 files changed, 43 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp index 40f39f6a74..f9912af9f9 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp @@ -138,9 +138,10 @@ struct Module { Require(input && (clipInput || (value == spv::BuiltInPosition && signature == "f32x4") || (value == spv::BuiltInTessCoord && stage == Stage::TessellationEvaluation && signature == "f32x3") || ((value == spv::BuiltInInvocationId || value == spv::BuiltInPrimitiveId || value == spv::BuiltInPatchVertices) && (signature == "i32" || signature == "u32"))), "unsupported tessellation built-in"); return; } + if (!input && value == spv::BuiltInPrimitivePointIndicesEXT) primitiveIndices = 1u; if (!input && value == spv::BuiltInPrimitiveLineIndicesEXT) primitiveIndices = 2u; if (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT) primitiveIndices = 3u; - Require((input && ((value == spv::BuiltInWorkgroupId || value == spv::BuiltInLocalInvocationId || value == spv::BuiltInGlobalInvocationId || value == spv::BuiltInNumWorkgroups) && signature == "u32x3")) || (input && value == spv::BuiltInLocalInvocationIndex && signature == "u32") || (!input && value == spv::BuiltInPrimitiveLineIndicesEXT && signature == "u32x2") || (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT && signature == "u32x3") || (!input && value == spv::BuiltInCullPrimitiveEXT && signature == "bool") || (!input && value == spv::BuiltInLayer && signature == "u32"), "unsupported mesh built-in"); + Require((input && ((value == spv::BuiltInWorkgroupId || value == spv::BuiltInLocalInvocationId || value == spv::BuiltInGlobalInvocationId || value == spv::BuiltInNumWorkgroups) && signature == "u32x3")) || (input && value == spv::BuiltInLocalInvocationIndex && signature == "u32") || (!input && value == spv::BuiltInPrimitivePointIndicesEXT && signature == "u32") || (!input && value == spv::BuiltInPrimitiveLineIndicesEXT && signature == "u32x2") || (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT && signature == "u32x3") || (!input && value == spv::BuiltInCullPrimitiveEXT && signature == "bool") || (!input && value == spv::BuiltInLayer && signature == "u32"), "unsupported mesh built-in"); return; } const auto signature = Signature(type); @@ -458,7 +459,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi mode(spv::ExecutionModeLocalSize, {paired ? config.threadsPerGroup / 2u : config.threadsPerGroup, 1, 1}); mode(spv::ExecutionModeOutputVertices, {config.maxVertices}); mode(spv::ExecutionModeOutputPrimitivesEXT, {config.maxPrimitives}); - mode(config.outputPrimitiveSize == 2u ? spv::ExecutionModeOutputLinesEXT : spv::ExecutionModeOutputTrianglesEXT, {}); + mode(config.outputPrimitiveSize == 1u ? spv::ExecutionModeOutputPoints : config.outputPrimitiveSize == 2u ? spv::ExecutionModeOutputLinesEXT : spv::ExecutionModeOutputTrianglesEXT, {}); Require(module.modes.size() == 4, "unsupported mesh execution mode"); } else if (barycentricPatches) { if (control) mode(spv::ExecutionModeOutputVertices, {3u}); @@ -513,7 +514,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi const auto length = module.constants.find(outer[3]); Require(length != module.constants.end() && length->second != 0, "invalid per-vertex interface array length"); if (mesh) { - const bool primitive = decoration.perPrimitive || module.perPrimitiveMembers.contains({outer[2], 0u}) || (decoration.builtin && (*decoration.builtin == spv::BuiltInPrimitiveLineIndicesEXT || *decoration.builtin == spv::BuiltInPrimitiveTriangleIndicesEXT || *decoration.builtin == spv::BuiltInCullPrimitiveEXT)); + const bool primitive = decoration.perPrimitive || module.perPrimitiveMembers.contains({outer[2], 0u}) || (decoration.builtin && (*decoration.builtin == spv::BuiltInPrimitivePointIndicesEXT || *decoration.builtin == spv::BuiltInPrimitiveLineIndicesEXT || *decoration.builtin == spv::BuiltInPrimitiveTriangleIndicesEXT || *decoration.builtin == spv::BuiltInCullPrimitiveEXT)); Require(length->second == (primitive ? state.stages.mesh->maxPrimitives : state.stages.mesh->maxVertices), "mesh interface array disagrees with output limits"); } else if (geometry) { Require(length->second == 3u, "geometry interface array must hold the three vertices of a triangle"); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index a9f9e6026b..5e2e831be9 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -1014,8 +1014,8 @@ ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) { const auto maxVertices = read(queue.context, 0x1ff); const auto verticesPerPrimitive = vertexWorkgroup ? 3u : read(queue.context, 0x2ce); const auto outputType = read(queue.context, 0x29b); - validate(outputType == 2 || (outputType == 1 && !vertexWorkgroup), "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported"); - const auto outputSize = outputType == 1 ? 2u : 3u; + validate(outputType == 2 || ((outputType == 0 || outputType == 1) && !vertexWorkgroup), "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a point list or line strip from a geometry program, is unsupported"); + const auto outputSize = outputType == 0 ? 1u : outputType == 1 ? 2u : 3u; validate((primitive == 1 || primitive == 2 || primitive == 4 || primitive == 5 || primitive == 6) && verticesPerPrimitive >= outputSize, "unsupported geometry input or output assembly"); const auto inputSize = primitive == 1 ? 1u : primitive == 2 ? 2u : 3u; validate(vertices >= inputSize && maxVertices != 0 && (!vertexWorkgroup || maxVertices >= inputSize) && maxVertices <= 256 && verticesPerPrimitive <= 256, "invalid geometry subgroup output"); @@ -1028,7 +1028,7 @@ ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) { validate(esgsItemSize != 0 && esgsItemSize * vertices <= 0xffffu, "invalid VGT_ESGS_RING_ITEMSIZE"); const auto amplification = fastLaunch ? read(queue.context, 0x2d3) & 0x1ffu : verticesPerPrimitive - outputSize + 1u; validate(amplification != 0, "fast launch subgroup amplifies to no primitives"); - const auto maxPrimitives = outputSize == 2u && !fastLaunch ? std::max(primitives * amplification, maxVertices) : primitives * amplification; + const auto maxPrimitives = outputSize <= 2u && !fastLaunch ? std::max(primitives * amplification, maxVertices) : primitives * amplification; const auto threads = std::max({(groupPrimitives - 1u) * inputStep + inputSize, primitives, maxVertices, maxPrimitives}); const auto raster = find(queue.context, 0x205); const auto provokingVertex = raster != queue.context.end() && (raster->second & (1u << 19u)) != 0 ? outputSize - 1u : 0u; @@ -1260,7 +1260,7 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) { const auto raster = read(cx, 0x205); // Bits 5-10 give the front/back polygon type (2 = filled triangles), which POLY_MODE (bit 3) turns on // explicitly; KEEP_TOGETHER_ENABLE (bit 24) only affects primitive distribution across the chip. - const bool pointsOrLines = result.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP || (result.stages.mesh.has_value() && result.stages.mesh->outputPrimitiveSize == 2u); + const bool pointsOrLines = result.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP || (result.stages.mesh.has_value() && result.stages.mesh->outputPrimitiveSize <= 2u); const auto lineOffset = (raster & 0x1800u) != 0 && !pointsOrLines ? 0x2000u : 0u; const auto rasterMode = raster & ~0x7u & ~(1u << 24u) & ~0x1800u & ~lineOffset & ~(1u << 19u) & ~(1u << 16u); if (rasterMode != 0 && rasterMode != 0x240u && rasterMode != 0x248u) throw std::runtime_error("AGC graphics: " + zeroMessage(0x205, raster, "polygon mode, depth bias or nonstandard rasterization")); @@ -1545,6 +1545,7 @@ std::array QuadSampleMasks(const State& state) { ShaderRecompiler::RasterPrimitive RasterPrimitiveOf(const State& state) { if (state.rectList) return ShaderRecompiler::RasterPrimitive::Rectangle; + if (state.stages.mesh.has_value() && state.stages.mesh->outputPrimitiveSize == 1u) return ShaderRecompiler::RasterPrimitive::Point; if (state.stages.mesh.has_value() && state.stages.mesh->outputPrimitiveSize == 2u) return ShaderRecompiler::RasterPrimitive::Line; if (state.stages.mesh.has_value() || state.stages.tessellation.has_value()) return ShaderRecompiler::RasterPrimitive::Triangle; switch (state.topology) { diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 6c37520b87..cb642f67c6 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -1070,14 +1070,19 @@ void ShaderStageTests() { Require(AgcDriver::Graphics::DecodeState(lines).stages.mesh->provokingVertex == 2 && AgcDriver::Graphics::DecodeShaderStages(lines).mesh->provokingVertex == 2, "a last-vertex geometry draw does not take its flat inputs and layer from the third vertex of each output triangle"); lines.context[0x29b] = 1; Require(AgcDriver::Graphics::DecodeState(lines).stages.mesh->provokingVertex == 1, "a last-vertex geometry draw does not take its flat inputs and layer from the second vertex of each output line"); + lines.context[0x29b] = 0; + state = AgcDriver::Graphics::DecodeState(lines); + mesh = *state.stages.mesh; + Require(mesh.outputPrimitiveSize == 1 && mesh.maxPrimitives == 255 && AgcDriver::Graphics::RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Point, "a point-list subgroup did not decode as points"); + Require(mesh.provokingVertex == 0, "a point draw has provoking vertex 0"); lines.context[0x205] = raster; lines.context[0x29b] = 1; lines.context[0x2ce] = 1; expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "unsupported geometry input or output assembly"); lines.context[0x2ce] = 5; - for (const auto output : {0u, 3u, 0x80000001u, 0x101u}) { + for (const auto output : {3u, 0x80000001u, 0x101u}) { lines.context[0x29b] = output; - expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported"); + expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a point list or line strip from a geometry program, is unsupported"); } lines.userConfig[0x242] = 4; lines.context[0x205] = 0x2a40; @@ -1231,7 +1236,9 @@ void NggVertexWorkgroupTests() { } queue.context[0x2d5] = 0x12010; queue.context[0x29b] = 1; - expectFailure([&] { AgcDriver::Graphics::DecodeState(queue, true); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported"); + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue, true); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a point list or line strip from a geometry program, is unsupported"); + queue.context[0x29b] = 0; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue, true); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a point list or line strip from a geometry program, is unsupported"); } constexpr std::array IdentityExports{0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u}; diff --git a/core/libs/prx/libSceAgcDriver/tests/VertexInputValidation.cpp b/core/libs/prx/libSceAgcDriver/tests/VertexInputValidation.cpp index bd6a291b22..1411867478 100644 --- a/core/libs/prx/libSceAgcDriver/tests/VertexInputValidation.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/VertexInputValidation.cpp @@ -71,7 +71,7 @@ int main() { std::uint32_t provokingVertex; bool accepted; }; - constexpr std::array meshExpectations{MeshExpectation{0u, 0u, false}, MeshExpectation{1u, 0u, false}, MeshExpectation{2u, 0u, true}, MeshExpectation{2u, 1u, true}, MeshExpectation{2u, 2u, false}, MeshExpectation{3u, 2u, true}, MeshExpectation{3u, 3u, false}, MeshExpectation{4u, 0u, false}, MeshExpectation{4u, 3u, false}}; + constexpr std::array meshExpectations{MeshExpectation{0u, 0u, false}, MeshExpectation{1u, 0u, true}, MeshExpectation{1u, 1u, false}, MeshExpectation{2u, 0u, true}, MeshExpectation{2u, 1u, true}, MeshExpectation{2u, 2u, false}, MeshExpectation{3u, 2u, true}, MeshExpectation{3u, 3u, false}, MeshExpectation{4u, 0u, false}, MeshExpectation{4u, 3u, false}}; const auto meshWith = [&](const MeshExpectation& expectation) { auto varied = mesh; varied.outputPrimitiveSize = expectation.outputPrimitiveSize; diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp index 5fad9ba460..ddde52c8e9 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp @@ -240,7 +240,7 @@ void DefineModule(SpirvEmitterState& state) { state.meshGuestFunc = state.module.AllocateId(); state.module.EmitCapability(spv::CapabilityMeshShadingEXT); state.module.EmitExtension("SPV_EXT_mesh_shader"); - state.module.AddExecutionMode(state.mainFunc, mesh.outputPrimitiveSize == 2u ? spv::ExecutionModeOutputLinesEXT : spv::ExecutionModeOutputTrianglesEXT); + state.module.AddExecutionMode(state.mainFunc, mesh.outputPrimitiveSize == 1u ? spv::ExecutionModeOutputPoints : mesh.outputPrimitiveSize == 2u ? spv::ExecutionModeOutputLinesEXT : spv::ExecutionModeOutputTrianglesEXT); state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeOutputVertices, mesh.maxVertices); state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeOutputPrimitivesEXT, mesh.maxPrimitives); } diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp index 8449663a77..fdaaa5b46c 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp @@ -997,11 +997,11 @@ void DefineMeshOutputs(SpirvEmitterState& state) { } state.meshAllocation = MeshArray(state, spv::StorageClassWorkgroup, TypeU32(state), 2u); state.meshPrimitiveData = MeshArray(state, spv::StorageClassPrivate, TypeU32(state), state.laneCount); - state.meshPrimitives = MeshArray(state, spv::StorageClassOutput, TypeU32Vector(state, mesh.outputPrimitiveSize), mesh.maxPrimitives); + state.meshPrimitives = MeshArray(state, spv::StorageClassOutput, mesh.outputPrimitiveSize == 1u ? TypeU32(state) : TypeU32Vector(state, mesh.outputPrimitiveSize), mesh.maxPrimitives); state.meshCull = MeshArray(state, spv::StorageClassOutput, TypeBool(state), mesh.maxPrimitives); state.interfaceVariables.push_back(state.meshPrimitives); state.interfaceVariables.push_back(state.meshCull); - state.module.AddAnnotation(spv::OpDecorate, state.meshPrimitives, spv::DecorationBuiltIn, mesh.outputPrimitiveSize == 2u ? spv::BuiltInPrimitiveLineIndicesEXT : spv::BuiltInPrimitiveTriangleIndicesEXT); + state.module.AddAnnotation(spv::OpDecorate, state.meshPrimitives, spv::DecorationBuiltIn, mesh.outputPrimitiveSize == 1u ? spv::BuiltInPrimitivePointIndicesEXT : mesh.outputPrimitiveSize == 2u ? spv::BuiltInPrimitiveLineIndicesEXT : spv::BuiltInPrimitiveTriangleIndicesEXT); state.module.AddAnnotation(spv::OpDecorate, state.meshCull, spv::DecorationBuiltIn, spv::BuiltInCullPrimitiveEXT); state.module.AddAnnotation(spv::OpDecorate, state.meshCull, spv::DecorationPerPrimitiveEXT); if (state.numWorkgroupsVariable == 0u) { @@ -1114,16 +1114,25 @@ void EmitMeshEntryPoint(SpirvEmitterState& state) { EmitIfCondition(state, isPrimitive, [&]() { const auto packed = MeshLoad(state, state.meshPrimitiveData, spv::StorageClassPrivate, TypeU32(state), ConstantU32(state, half)); std::array vertex {}; - for (std::uint32_t component = 0; component < mesh.outputPrimitiveSize; component++) { + if (mesh.outputPrimitiveSize == 1u) { const auto field = state.module.AllocateId(); - state.module.AddFunction(spv::OpBitFieldUExtract, TypeU32(state), field, packed, ConstantU32(state, component * 10u), ConstantU32(state, 9u)); - vertex.at(component) = state.module.AllocateId(); - state.module.AddFunction(spv::OpExtInst, TypeU32(state), vertex.at(component), GlslStd450(state), GLSLstd450UMin, field, lastVertex); + state.module.AddFunction(spv::OpBitFieldUExtract, TypeU32(state), field, packed, ConstantU32(state, 0u), ConstantU32(state, 9u)); + vertex.at(0) = state.module.AllocateId(); + state.module.AddFunction(spv::OpExtInst, TypeU32(state), vertex.at(0), GlslStd450(state), GLSLstd450UMin, field, lastVertex); + const auto indicesPointer = MeshElement(state, state.meshPrimitives, spv::StorageClassOutput, TypeU32(state), index); + state.module.AddFunction(spv::OpStore, indicesPointer, vertex.at(0)); + } else { + for (std::uint32_t component = 0; component < mesh.outputPrimitiveSize; component++) { + const auto field = state.module.AllocateId(); + state.module.AddFunction(spv::OpBitFieldUExtract, TypeU32(state), field, packed, ConstantU32(state, component * 10u), ConstantU32(state, 9u)); + vertex.at(component) = state.module.AllocateId(); + state.module.AddFunction(spv::OpExtInst, TypeU32(state), vertex.at(component), GlslStd450(state), GLSLstd450UMin, field, lastVertex); + } + const auto indices = state.module.AllocateId(); + state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, mesh.outputPrimitiveSize), indices, std::span(vertex.data(), mesh.outputPrimitiveSize)); + const auto indicesPointer = MeshElement(state, state.meshPrimitives, spv::StorageClassOutput, TypeU32Vector(state, mesh.outputPrimitiveSize), index); + state.module.AddFunction(spv::OpStore, indicesPointer, indices); } - const auto indices = state.module.AllocateId(); - state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, mesh.outputPrimitiveSize), indices, std::span(vertex.data(), mesh.outputPrimitiveSize)); - const auto indicesPointer = MeshElement(state, state.meshPrimitives, spv::StorageClassOutput, TypeU32Vector(state, mesh.outputPrimitiveSize), index); - state.module.AddFunction(spv::OpStore, indicesPointer, indices); const auto nullBit = EmitBinaryU32(state, spv::OpBitwiseAnd, packed, ConstantU32(state, 0x80000000u)); const auto culled = state.module.AllocateId(); state.module.AddFunction(spv::OpINotEqual, TypeBool(state), culled, nullBit, ConstantU32(state, 0u)); diff --git a/core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp b/core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp index 62fc9b9f65..62c198d4a0 100644 --- a/core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp +++ b/core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp @@ -70,7 +70,7 @@ void _validateTessellation(const TessellationConfiguration* tessellation) { void _validateMesh(const MeshConfiguration* mesh) { if (mesh == nullptr) throw std::runtime_error("ShaderInputInfoBuilder: a mesh-stage program has no mesh configuration"); - if (mesh->threadsPerGroup == 0u || mesh->maxVertices == 0u || mesh->maxPrimitives == 0u || (mesh->outputPrimitiveSize != 2u && mesh->outputPrimitiveSize != 3u) || mesh->provokingVertex >= mesh->outputPrimitiveSize) throw std::runtime_error("ShaderInputInfoBuilder: invalid mesh configuration"); + if (mesh->threadsPerGroup == 0u || mesh->maxVertices == 0u || mesh->maxPrimitives == 0u || (mesh->outputPrimitiveSize != 1u && mesh->outputPrimitiveSize != 2u && mesh->outputPrimitiveSize != 3u) || mesh->provokingVertex >= mesh->outputPrimitiveSize) throw std::runtime_error("ShaderInputInfoBuilder: invalid mesh configuration"); } } diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index f174aa110e..a7767ab748 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -22,7 +22,8 @@ - [Indices far past every vertex fetch](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) - when a draw's highest index is at least 64Ki vertices past the largest per-vertex record count, every index past that count is moved to it in a copy of the indices, so the zero-padded vertex copies are not sized to the original index (PPSA04159 draws a list of `0xffffffff` with primitive restart off, which asked for 160 GiB). Each attribute fetched by vertex index reads zeros there as at the original index (out-of-bounds select 0 or 1); a draw with another out-of-bounds select keeps its indices. The vertex index the shader sees changes for the moved indices, which a shader that reads it directly would notice - [Depth clear draws](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - a draw with `DB_RENDER_CONTROL.DEPTH_CLEAR_ENABLE` is taken to write `DB_DEPTH_CLEAR` to every pixel it covers whatever the depth test, depth bounds and depth bias say: it is drawn with an always-passing depth write and the viewport depth range pinned to the clear value. The draw is rasterized as submitted, so its coverage is the draw's own. Whether the DB still applies the depth test or HiZ to such a draw is not measured - [GS_FAST_LAUNCH geometry](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH = 1` with a point list and `GE_CNTL` groups of one vertex and one primitive decodes as one point per subgroup, with the primitive limit taken from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR` and the GS/ES VGPR counts not required. Follows how RADV drives mesh shaders on gfx10.3 (`ac_nir_lower_ngg.c`, the workgroup index comes from the vertex id of the first thread), not measured on the console. Fast launch 2, other group sizes or input primitives and indexed fast-launch draws still throw. PPSA03671's fast-launch draws decode, but the title stops earlier in a wave64 vertex program, so they have not reached the GPU yet. -- [Line-strip geometry output](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - a geometry program with `VGT_GS_OUT_PRIM_TYPE = 1` (line strip) runs as a mesh shader with `OutputLinesEXT`: each primitive export is one line whose two vertex indices are packed like the three of a triangle, as Mesa's NGG lowering exports line strips (`ac_nir_lower_ngg_gs.c`), and each input primitive gets `VGT_GS_MAX_VERT_OUT - 1` lines where a triangle strip gets `- 2` triangles. A subgroup gets at least `GE_MAX_OUTPUT_PER_SUBGROUP` line slots, because Mesa's lowering exports one primitive, possibly null, per emitted vertex; whether the console's compiler does the same is not known, and triangle strips keep `- 2` per input. A fast-launch subgroup takes its line count from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR` as for triangles. Line output counts as lines for the polygon offset decode, as on the vertex path. The lines are drawn 1 pixel wide with Vulkan's default line rasterization, like lines on the vertex path: `PA_SU_LINE_CNTL` is not read, and neither its width nor the console's line rules were measured. Points, rectangle lists, per-stream types and line strips from a vertex program on the mesh path still throw. +- [Point-list geometry output](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - a geometry program with `VGT_GS_OUT_PRIM_TYPE = 0` (point list) runs as a mesh shader with `OutputPoints` and scalar `PrimitivePointIndicesEXT`. Each primitive export emits one point whose vertex index is extracted from bits [8:0] of the packed word. A subgroup gets up to `maxVertices` points, with provoking vertex 0 and point raster primitive topology. Rectangle lists, per-stream types and point or line strips from a vertex program on the mesh path still throw. +- [Line-strip geometry output](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - a geometry program with `VGT_GS_OUT_PRIM_TYPE = 1` (line strip) runs as a mesh shader with `OutputLinesEXT`: each primitive export is one line whose two vertex indices are packed like the three of a triangle, as Mesa's NGG lowering exports line strips (`ac_nir_lower_ngg_gs.c`), and each input primitive gets `VGT_GS_MAX_VERT_OUT - 1` lines where a triangle strip gets `- 2` triangles. A subgroup gets at least `GE_MAX_OUTPUT_PER_SUBGROUP` line slots; whether the console does the same is unmeasured. A fast-launch subgroup takes its line count from `GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR`. Line output counts as lines for polygon offset decode. The lines are drawn 1 pixel wide with Vulkan default rasterization; `PA_SU_LINE_CNTL` is not read, and console line rules are not measured. - [Alpha-to-coverage](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - only `DB_ALPHA_TO_MASK = 0xaa01` is accepted: the enable bit with the four quad offsets at 2 and no `OFFSET_ROUND`, the undithered setting [radeonsi](https://gitlab.freedesktop.org/mesa/mesa/-/blob/bffd91947efc1932da99aca69fc540d6d4201b0c/src/gallium/drivers/radeonsi/si_state.c#L371-L381) programs. Draws are single-sampled, so the pixel shader [writes](../../../core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp) `SampleMask` 0 when its MRT0 alpha is below one half or NaN and all ones otherwise, and runs with late fragment tests. That rule was measured on a Radeon 660M (gfx1035, RADV with `RADV_DEBUG=noatocdithering`, which programs the same register) for 0, -0, 0.25, the two floats below one half, one half, the two above, 0.75, 1, 2, -1, both infinities and both NaNs, and is pinned in [AlphaToCoverage.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp); it is not measured on gfx1013. Vulkan's `alphaToCoverageEnable` is not used: the NVIDIA driver 615.78.08 dithers single-sample coverage (an alpha of 0.25 writes half the pixels of an RTX 3050 Laptop target). Other offsets, `OFFSET_ROUND`, a sample-mask export, and an MRT0 that is missing, swizzled, packed or exported without a floating-point alpha throw. A draw without a color target on MRT0 keeps alpha-to-coverage from its MRT0 export, as [radeonsi exports MRT0 as `32_AR` for alpha-to-coverage without a color buffer](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-24.0.0/src/gallium/drivers/radeonsi/si_state_shaders.cpp#L2425-L2431). A draw that enables alpha-to-coverage with no MRT0 export still throws: what the console covers then is not measured. Default dithered `DB_ALPHA_TO_MASK = 0x18701` additionally uses a per-quad sample-mask table [measured on gfx1036](https://github.com/boykopovar/AnyPS5/pull/4451#issuecomment-6124848921) for eight standard samples with FP16_ABGR or 32_ABGR exports; other sample counts, custom locations, EQAA combinations and dithered UNORM16/SNORM16 exports throw. The dithered rule is not measured on gfx1013. - [A one-sample target under a multisampled rasterizer without MSAA_ENABLE](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` > 0 with `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear, one-sample color targets and no depth target, or a one-sample depth target with no color target or one-sample color targets, is drawn as a one-sample draw at the pixel center. Without the bit every sample is taken to be at the pixel center (see the entry for a multisampled target rasterized without MSAA_ENABLE below), and a one-sample target is taken to keep the coverage of sample 0, so a one-sample depth target is tested and written once per pixel with the depth at the pixel center. That is the rule [`VK_AMD_mixed_attachment_samples`](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/fragops.adoc#L2510-L2516) gives a color target with fewer samples than the rasterizer, but Vulkan reaches it only beside a depth or stencil target with the rasterizer's sample count ([VUID-VkGraphicsPipelineCreateInfo-subpass-01505](https://github.com/KhronosGroup/Vulkan-Docs/blob/193ebe195f47ffc5004683d1659e71c725658c4d/chapters/pipelines.adoc#L3033-L3041)), so here it is an analogy, and for a one-sample depth target an extension of it. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp#L127-L128) also rasterizes with the largest sample count of the bound depth and color targets ([L416-L417](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L416-L417), [L477-L480](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L477-L480)), and reads `PA_SC_AA_CONFIG` only for a `SAMPLE_COVERAGE` input ([L174-L177](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L174-L177)). This is not measured on hardware. `PS_ITER_SAMPLE`, an `ANCILLARY` or `SAMPLE_COVERAGE` pixel input and a sample-mask export throw in a pixel program that runs, because what the console gives them under a multisampled configuration is not known; a depth-only draw that skips its pixel program is drawn with the null program, and its `PS_ITER_SAMPLE` and pixel input bits are ignored. Points, lines and alpha-to-coverage throw as for a multisampled target. With a one-sample depth target, `DB_EQAA` Z interpolation (`INTERPOLATE_COMP_Z`, `INTERPOLATE_SRC_Z`) or overrasterization (`OVERRASTERIZATION_AMOUNT`, `ENABLE_POSTZ_OVERRASTERIZATION`) also throws. METAL GEAR SOLID: Peace Walker (PPSA18216) draws a one-sample color target under a 4x configuration this way; the first draw of its opening movie binds a one-sample depth target and no color target under the same configuration. - [MSAA_ENABLE on a single-sample target](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (AGC driver) - `PA_SC_MODE_CNTL_0.MSAA_ENABLE` with `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES = 0` is drawn like the same state without the bit when sample 0 of the four `PA_SC_AA_SAMPLE_LOCS_PIXEL_*_0` registers is at the pixel center and the draw rasterizes filled triangles or rectangles (`VGT_PRIMITIVE_TYPE` 7 or 17 as well as the triangle types) with alpha-to-coverage and conservative rasterization off. [RADV](https://gitlab.freedesktop.org/mesa/mesa/-/blob/745709a000482a4a24c91faef5c5a40bd653a863/src/amd/vulkan/radv_cmd_buffer.c#L13606-13609) programs this state for one-sample custom sample locations on GFX10 and later, where the bit makes the rasterizer use the programmed location of sample 0 ([061bc615](https://gitlab.freedesktop.org/mesa/mesa/-/commit/061bc6151a11597f720535f00e79a5752e24aff6)); that the bit changes nothing else for such a draw is inferred from that and is not measured on hardware. EA Sports UFC 5 (PPSA03541) reaches a rect-list draw in this state. Other locations of sample 0, absent location registers, points, lines, alpha-to-coverage and conservative rasterization throw From 8bddc39245ea7c33d7a7046751eb29ca5dd8978f Mon Sep 17 00:00:00 2001 From: hypermist Date: Wed, 21 Oct 2026 16:31:00 +1300 Subject: [PATCH 092/523] feat(libc): forward the C11 thread and TSS calls to libkernel _Call_once, _Tss_create, _Tss_delete, _Tss_get, _Tss_set, _Thrd_equal, _Thrd_current and _Thrd_exit call scePthreadOnce, scePthreadKeyCreate, scePthreadKeyDelete, scePthreadGetspecific, scePthreadSetspecific, scePthreadEqual, scePthreadSelf and scePthreadExit and return their results unchanged, as the console's libSceLibcInternal does. _Mtx_current_owns returns 1 there. libkernel registers the calls with libc at load, next to the pthread_create registration. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- core/libs/prx/libc/include/General.hpp | 12 +++ core/libs/prx/libc/src/Threads.cpp | 49 ++++++++++++ .../prx/libkernel/Pthread/Posix/Thread.cpp | 17 +++++ core/libs/tests/GuestLibcThreads.cpp | 75 +++++++++++++++++++ 4 files changed, 153 insertions(+) diff --git a/core/libs/prx/libc/include/General.hpp b/core/libs/prx/libc/include/General.hpp index 6dade90518..beaa7fd585 100644 --- a/core/libs/prx/libc/include/General.hpp +++ b/core/libs/prx/libc/include/General.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "general/LogMacros.hpp" #include "general/VabiMacros.hpp" @@ -57,6 +58,17 @@ using PthreadDetachFn_nid_no_patch = int (APS5_VABI*)(void*); extern "C" void RegisterPthreadCreate_nid_no_patch(PthreadCreateFn_nid_no_patch fn); extern "C" void RegisterPthreadJoin_nid_no_patch(PthreadJoinFn_nid_no_patch fn); extern "C" void RegisterPthreadDetach_nid_no_patch(PthreadDetachFn_nid_no_patch fn); +struct ThreadForwards_nid_no_patch { + int (APS5_VABI* once)(std::int32_t*, void (APS5_VABI*)(void)); + int (APS5_VABI* keyCreate)(int*, void (APS5_VABI*)(void*)); + int (APS5_VABI* keyDelete)(int); + void* (APS5_VABI* getSpecific)(int); + int (APS5_VABI* setSpecific)(int, void*); + int (APS5_VABI* equal)(void*, void*); + void* (APS5_VABI* self)(); + void (APS5_VABI* exit)(void*); +}; +extern "C" void RegisterThreadForwards_nid_no_patch(const ThreadForwards_nid_no_patch* forwards); #define APS5_INVALID_ARG_EX throw std::invalid_argument(std::string(__func__) + ": invalid argument") diff --git a/core/libs/prx/libc/src/Threads.cpp b/core/libs/prx/libc/src/Threads.cpp index 3373def131..a00ba39463 100644 --- a/core/libs/prx/libc/src/Threads.cpp +++ b/core/libs/prx/libc/src/Threads.cpp @@ -497,5 +497,54 @@ int APS5_VABI _Thrd_id_nid_postfix(void) { ThrowCppError_nid_no_patch(code); } +static std::atomic registeredThreadForwards{nullptr}; + +void RegisterThreadForwards_nid_no_patch(const ThreadForwards_nid_no_patch* forwards) { + registeredThreadForwards.store(forwards, std::memory_order_release); +} + +static const ThreadForwards_nid_no_patch& ThreadForwards(const char* function) { + const auto* forwards = registeredThreadForwards.load(std::memory_order_acquire); + if (!forwards) throw std::runtime_error(std::string(function) + ": libkernel threads are not registered"); + return *forwards; +} + +void APS5_VABI _Call_once_nid_postfix(std::int32_t* flag, void (APS5_VABI* function)(void)) { + ThreadForwards(__func__).once(flag, function); +} + +int APS5_VABI _Tss_create_nid_postfix(int* key, void (APS5_VABI* destructor)(void*)) { + return ThreadForwards(__func__).keyCreate(key, destructor); +} + +int APS5_VABI _Tss_delete_nid_postfix(int key) { + return ThreadForwards(__func__).keyDelete(key); +} + +void* APS5_VABI _Tss_get_nid_postfix(int key) { + return ThreadForwards(__func__).getSpecific(key); +} + +int APS5_VABI _Tss_set_nid_postfix(int key, void* value) { + return ThreadForwards(__func__).setSpecific(key, value); +} + +int APS5_VABI _Thrd_equal_nid_postfix(void* first, void* second) { + return ThreadForwards(__func__).equal(first, second); +} + +void* APS5_VABI _Thrd_current_nid_postfix(void) { + return ThreadForwards(__func__).self(); +} + +[[noreturn]] void APS5_VABI _Thrd_exit_nid_postfix(int code) { + ThreadForwards(__func__).exit(reinterpret_cast(static_cast(static_cast(code)))); + std::abort(); +} + +int APS5_VABI _Mtx_current_owns_nid_postfix(void**) { + return 1; +} + } diff --git a/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp b/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp index dc71c1cebc..12589bc185 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Thread.cpp @@ -21,6 +21,12 @@ int APS5_VABI scePthreadJoin(Pthread thread, void** retval); int APS5_VABI scePthreadRename(Pthread thread, const char* name); int APS5_VABI scePthreadGetname(Pthread thread, char* name); Pthread APS5_VABI scePthreadSelf(); +int APS5_VABI scePthreadEqual(Pthread thread1, Pthread thread2); +int APS5_VABI scePthreadOnce(int32_t* once, void (APS5_VABI* init)(void)); +int APS5_VABI scePthreadKeyCreate(PthreadKey* key, pthread_key_destructor_func_t destructor); +int APS5_VABI scePthreadKeyDelete(PthreadKey key); +void* APS5_VABI scePthreadGetspecific(PthreadKey key); +int APS5_VABI scePthreadSetspecific(PthreadKey key, void* value); int APS5_VABI scePthreadCancel(Pthread thread); int APS5_VABI scePthreadSetcancelstate(int state, int* old_state); int APS5_VABI scePthreadSetcanceltype(int type, int* old_type); @@ -240,6 +246,17 @@ struct RegisterPthreadOnLoad { RegisterPthreadCreate_nid_no_patch(reinterpret_cast(pthread_create_nid_postfix)); RegisterPthreadJoin_nid_no_patch(reinterpret_cast(pthread_join_nid_postfix)); RegisterPthreadDetach_nid_no_patch(reinterpret_cast(pthread_detach_nid_postfix)); + static const ThreadForwards_nid_no_patch forwards{ + reinterpret_cast(scePthreadOnce), + reinterpret_cast(scePthreadKeyCreate), + scePthreadKeyDelete, + scePthreadGetspecific, + scePthreadSetspecific, + reinterpret_cast(scePthreadEqual), + reinterpret_cast(scePthreadSelf), + scePthreadExit, + }; + RegisterThreadForwards_nid_no_patch(&forwards); } }; const RegisterPthreadOnLoad gRegisterPthread; diff --git a/core/libs/tests/GuestLibcThreads.cpp b/core/libs/tests/GuestLibcThreads.cpp index ac1015b41f..102b21ada0 100644 --- a/core/libs/tests/GuestLibcThreads.cpp +++ b/core/libs/tests/GuestLibcThreads.cpp @@ -34,6 +34,26 @@ int APS5_VABI _Thrd_detach_nid_postfix(void* thread); void RegisterPthreadCreate_nid_no_patch(int (APS5_VABI* fn)(void**, const void*, void* (APS5_VABI*)(void*), void*)); void RegisterPthreadJoin_nid_no_patch(int (APS5_VABI* fn)(void*, void**)); void RegisterPthreadDetach_nid_no_patch(int (APS5_VABI* fn)(void*)); + +struct ThreadForwards { + int (APS5_VABI* once)(std::int32_t*, void (APS5_VABI*)(void)); + int (APS5_VABI* keyCreate)(int*, void (APS5_VABI*)(void*)); + int (APS5_VABI* keyDelete)(int); + void* (APS5_VABI* getSpecific)(int); + int (APS5_VABI* setSpecific)(int, void*); + int (APS5_VABI* equal)(void*, void*); + void* (APS5_VABI* self)(); + void (APS5_VABI* exit)(void*); +}; +void RegisterThreadForwards_nid_no_patch(const ThreadForwards* forwards); +void APS5_VABI _Call_once_nid_postfix(std::int32_t* flag, void (APS5_VABI* function)(void)); +int APS5_VABI _Tss_create_nid_postfix(int* key, void (APS5_VABI* destructor)(void*)); +int APS5_VABI _Tss_delete_nid_postfix(int key); +void* APS5_VABI _Tss_get_nid_postfix(int key); +int APS5_VABI _Tss_set_nid_postfix(int key, void* value); +int APS5_VABI _Thrd_equal_nid_postfix(void* first, void* second); +void* APS5_VABI _Thrd_current_nid_postfix(void); +int APS5_VABI _Mtx_current_owns_nid_postfix(void** handle); } static void Require(bool value) { if (!value) std::abort(); } @@ -299,7 +319,62 @@ static void PadDetach() { RegisterPthreadDetach_nid_no_patch(nullptr); } +static void KernelKeys() { + int key = -1; + Require(_Tss_create_nid_postfix(&key, nullptr) == success && key >= 0); + int value = 0; + Require(_Tss_get_nid_postfix(key) == nullptr); + Require(_Tss_set_nid_postfix(key, &value) == success && _Tss_get_nid_postfix(key) == &value); + Require(_Tss_delete_nid_postfix(key) == success); +} + +struct ForwardLog { + std::int32_t* flag; + void (APS5_VABI* function)(void); + int key; + void* value; + void* first; + void* second; +}; +static ForwardLog forwardLog; + +static int APS5_VABI FakeOnce(std::int32_t* flag, void (APS5_VABI* function)(void)) { + forwardLog.flag = flag; + forwardLog.function = function; + return 7; +} +static int APS5_VABI FakeKeyCreate(int* key, void (APS5_VABI*)(void*)) { *key = 41; return 0x80020016; } +static int APS5_VABI FakeKeyDelete(int key) { forwardLog.key = key; return 0x80020002; } +static void* APS5_VABI FakeGetSpecific(int key) { forwardLog.key = key; return &forwardLog; } +static int APS5_VABI FakeSetSpecific(int key, void* value) { forwardLog.key = key; forwardLog.value = value; return 0; } +static int APS5_VABI FakeEqual(void* first, void* second) { forwardLog.first = first; forwardLog.second = second; return 1; } +static void* APS5_VABI FakeSelf() { return &forwardLog.key; } +static void APS5_VABI FakeExit(void*) { std::abort(); } +static void APS5_VABI OnceTarget() {} + +static void ForwardedCalls() { + static const ThreadForwards fakes{FakeOnce, FakeKeyCreate, FakeKeyDelete, FakeGetSpecific, FakeSetSpecific, FakeEqual, FakeSelf, FakeExit}; + RegisterThreadForwards_nid_no_patch(&fakes); + std::int32_t flag = 0; + _Call_once_nid_postfix(&flag, OnceTarget); + Require(forwardLog.flag == &flag && forwardLog.function == OnceTarget); + int key = 0; + Require(_Tss_create_nid_postfix(&key, nullptr) == static_cast(0x80020016) && key == 41); + Require(_Tss_delete_nid_postfix(5) == static_cast(0x80020002) && forwardLog.key == 5); + Require(_Tss_get_nid_postfix(6) == &forwardLog && forwardLog.key == 6); + Require(_Tss_set_nid_postfix(8, &flag) == success && forwardLog.key == 8 && forwardLog.value == &flag); + Require(_Thrd_equal_nid_postfix(&flag, &key) == 1 && forwardLog.first == &flag && forwardLog.second == &key); + Require(_Thrd_current_nid_postfix() == &forwardLog.key); + Require(_Mtx_current_owns_nid_postfix(nullptr) == 1); + + RegisterThreadForwards_nid_no_patch(nullptr); + RequireThrows([] { _Thrd_current_nid_postfix(); }); + RequireThrows([] { _Tss_get_nid_postfix(0); }); +} + int main() { + KernelKeys(); + ForwardedCalls(); MutexBasics(); RecursiveMutex(); MutualExclusion(); From 7b07c20497015fac54bfa01735ba1eb7b7e8fecf Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 21 Oct 2026 03:41:57 +0000 Subject: [PATCH 093/523] fix(relinker): resolve kernel variant imports via canonical kernel --- .../elfpatcher/src/windows/WindowsEntryStubBuilder.cpp | 5 +++++ .../relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp index 175a42f755..dbcca232a2 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp @@ -172,6 +172,11 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con const bool hostModule = !import.Library.empty() && std::any_of(order.begin(), order.end(), [&](auto index) { return index >= guestModules.size(); }); if (hostModule) { + if (library == "libkernel_sys.prx" || library == "libkernel_web.prx") { + for (std::size_t index = guestModules.size(); index < libraries.size(); ++index) { + if (libraries[index] == "libkernel.prx") order.push_back(CheckedRva(index)); + } + } for (std::size_t index = guestModules.size(); index < libraries.size(); ++index) { if (libraries[index] == "libc.prx" && library != "libc.prx") order.push_back(CheckedRva(index)); } diff --git a/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp index e7153da217..4f41f50b82 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp @@ -55,7 +55,7 @@ std::vector WindowsImportBuilder::ReadLibraries(const Domain::SysVD // Keep the requested module first, then make its shared runtime visible. if ((unique.contains("libSceLibcInternal.prx") || unique.contains("libkernel.prx")) && !unique.contains("libc.prx")) result.push_back("libc.prx"); - if (unique.contains("libkernel_web.prx") && !unique.contains("libkernel.prx")) + if ((unique.contains("libkernel_sys.prx") || unique.contains("libkernel_web.prx")) && !unique.contains("libkernel.prx")) result.push_back("libkernel.prx"); return result; } From fef2755c76cb1f567c82dfeaaf646b88e241c3a6 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 21 Oct 2026 03:41:57 +0000 Subject: [PATCH 094/523] test(relinker): cover scoped kernel variant imports --- .../elfpatcher/tests/EntryStubTests.cpp | 29 +++++++++++++++++++ .../WindowsDependencyDiagnosticsTests.cpp | 2 ++ 2 files changed, 31 insertions(+) diff --git a/core/relinker/elfpatcher/tests/EntryStubTests.cpp b/core/relinker/elfpatcher/tests/EntryStubTests.cpp index 5b3b8a78b1..5802231e59 100644 --- a/core/relinker/elfpatcher/tests/EntryStubTests.cpp +++ b/core/relinker/elfpatcher/tests/EntryStubTests.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -116,6 +118,31 @@ void windowsStubRejectsAnOverlongAllocatorPath() { throw std::runtime_error("an allocator path past the startup buffer was accepted"); } +void windowsKernelVariantImportsSearchCanonicalKernelAfterTheRequestedVariant() { + using namespace Elfpatcher::Windows; + const auto nativeImports = WindowsImportBuilder().Build(0x1000); + const std::vector libraries{"libkernel_sys.prx", "libc.prx", "libkernel.prx"}; + const std::vector imports{{.Name = "getpagesize", .TargetRva = 0x2000, .Addend = 0, .Library = "libkernel_sys.prx"}}; + const auto stub = WindowsEntryStubBuilder().Build(0x40000, 0x1000, nativeImports, libraries, imports, "$ORIGIN/libs", false, false); + const std::vector expected{0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0}; + require(std::search(stub.Data.Data.begin(), stub.Data.Data.end(), expected.begin(), expected.end()) != stub.Data.Data.end(), + "a scoped libkernel_sys import does not search the requested variant before canonical libkernel and libc"); +} + +void windowsKernelVariantDependenciesLoadCanonicalKernel() { + using namespace Elfpatcher::Windows; + for (const auto& variant : {std::string("libkernel_sys.prx"), std::string("libkernel_web.prx")}) { + Domain::SysVDynamicSection dynamic; + dynamic.DynStrData.insert(dynamic.DynStrData.end(), variant.begin(), variant.end()); + dynamic.DynStrData.push_back(0); + dynamic.DynamicSegmentData.resize(16); + Io::WriteU64(dynamic.DynamicSegmentData, 0, 1); + Io::WriteU64(dynamic.DynamicSegmentData, 8, 0); + require(WindowsImportBuilder().ReadLibraries(dynamic) == std::vector{variant, "libkernel.prx"}, + variant + ": canonical libkernel is not loaded after the requested module"); + } +} + } int main() { @@ -126,6 +153,8 @@ int main() { windowsStubLoadsTheAllocatorOnRequest("C:/Games/Title/libs", u"C:\\Games\\Title\\libs\\", {0x48, 0x8d, 0x0d}); windowsStubLoadsTheAllocatorOnRequest("$ORIGIN/libs", u"libs\\", {0x48, 0x8d, 0x35}); windowsStubRejectsAnOverlongAllocatorPath(); + windowsKernelVariantImportsSearchCanonicalKernelAfterTheRequestedVariant(); + windowsKernelVariantDependenciesLoadCanonicalKernel(); } catch (const std::exception& error) { std::cerr << error.what() << '\n'; return 1; diff --git a/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp b/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp index fa6c1d4085..7a0d29e1bf 100644 --- a/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp +++ b/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp @@ -55,6 +55,8 @@ void checkRuntimeDependencies() { {"libc.prx", "libSceLibcInternal.prx"}); check({"libSceVideoOut.prx", "libkernel.prx", "libSceVideoOut.prx"}, {"libSceVideoOut.prx", "libkernel.prx", "libc.prx"}); + check({"libkernel_sys.prx"}, {"libkernel_sys.prx", "libkernel.prx"}); + check({"libkernel_web.prx"}, {"libkernel_web.prx", "libkernel.prx"}); check({"libSceVideoOut.prx", "libSceVideoOut.prx"}, {"libSceVideoOut.prx"}); } From 0c7d25c3d1574652f30965fb09e8a0df93a9afdc Mon Sep 17 00:00:00 2001 From: harisibrar1 <207127467+harisibrar1@users.noreply.github.com> Date: Wed, 21 Oct 2026 00:50:41 -0400 Subject: [PATCH 095/523] ci(conventions): check pull requests with the current base branch rules The workflow checked out pull_request.base.sha, the base recorded when the pull request was opened, and ran that revision's check_conventions.py. A rule merged after a pull request was opened never ran on it: #7005 added APS5_NO_WGI without an ENVIRONMENT.md row and passed with the checker of 07e741481, which had no env-reference rule. Check out the current tip of the base branch and pass it as --base; the checker already diffs from the merge base, so only the pull request's own changes are checked. A name documented on the base branch after the branch point now counts as documented unless the pull request removes its row. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/conventions.yml | 7 ++-- CONTRIBUTING.md | 2 +- tools/check_conventions.py | 6 ++- .../test_check_conventions_env_reference.py | 42 ++++++++++++++++--- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/.github/workflows/conventions.yml b/.github/workflows/conventions.yml index 2a14c1c2a3..d09210cfd0 100644 --- a/.github/workflows/conventions.yml +++ b/.github/workflows/conventions.yml @@ -18,15 +18,14 @@ jobs: steps: - uses: actions/checkout@v7 with: - ref: ${{ github.event.pull_request.base.sha }} + ref: ${{ github.event.pull_request.base.ref }} fetch-depth: 0 persist-credentials: false - name: Fetch pull request objects without checking out its code env: HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: git fetch --no-tags origin "$HEAD_SHA" - - name: Check conventions with the trusted base script + - name: Check conventions with the current base branch script env: - BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} - run: python3 -I tools/check_conventions.py --base "$BASE_SHA" --head "$HEAD_SHA" + run: python3 -I tools/check_conventions.py --base HEAD --head "$HEAD_SHA" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27ccf98173..2e218f13f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,7 +41,7 @@ This manual run also builds and tests the relinker on Linux, Windows and macOS w With `--field full=false`, the manual run skips the full builds and runs only the relinker matrix on Linux, Windows and macOS, if the pull request changes a file the relinker-only build uses, such as the Python tools and their tests in `tools/`. -The Conventions check runs automatically for every contributor, including first-time contributors. It uses the base branch checker and reads pull request Git objects without executing pull request code. The Conventions check runs on every pull request and fails when a rule on this page is broken. It accepts code comments only when the pull request also changes [technical debt](docs/dev/TechnicalDebt.md), the index or a file in `docs/dev/debt/`, and only UTF-8 text files. Run it locally before pushing: +The Conventions check runs automatically for every contributor, including first-time contributors. It uses the checker from the current tip of the base branch, so rules added after a pull request was opened apply to its next run, and reads pull request Git objects without executing pull request code. The Conventions check runs on every pull request and fails when a rule on this page is broken. It accepts code comments only when the pull request also changes [technical debt](docs/dev/TechnicalDebt.md), the index or a file in `docs/dev/debt/`, and only UTF-8 text files. Run it locally before pushing: ``` python3 tools/check_conventions.py --base origin/main diff --git a/tools/check_conventions.py b/tools/check_conventions.py index 9c3eea8e60..586c1d1e1e 100644 --- a/tools/check_conventions.py +++ b/tools/check_conventions.py @@ -93,6 +93,7 @@ def git(*args, text=True): class Check: def __init__(self, base, head): self.head = head + self.target = base self.base = git("merge-base", base, head).strip() self.findings = [] self.added = defaultdict(list) @@ -361,7 +362,10 @@ class Check: reads = set() for line in result.stdout.splitlines(): reads.update(match.group("name") for _, match in env_reference.line_reads(line)) - documented = set(env_reference.documented_pattern(env_reference.PREFIXES).findall(self.show(doc))) if doc in self.tree else set() + pattern = env_reference.documented_pattern(env_reference.PREFIXES) + documented = set(pattern.findall(self.show(doc))) if doc in self.tree else set() + if git("ls-tree", "--name-only", self.target, "--", doc).strip() == doc: + documented |= set(pattern.findall(git("show", f"{self.target}:{doc}"))) - dropped for name, (source, line) in sorted(added.items()): if name not in documented: self.report("env-reference", source, line, f"{name} is read here but has no row in {doc}") diff --git a/tools/tests/test_check_conventions_env_reference.py b/tools/tests/test_check_conventions_env_reference.py index 0ccd9bf93c..b217e80247 100644 --- a/tools/tests/test_check_conventions_env_reference.py +++ b/tools/tests/test_check_conventions_env_reference.py @@ -14,7 +14,7 @@ SAMPLE = "core/libs/prx/libSceExample/Sample.cpp" DOC_TEXT = "| `APS5_DOCUMENTED` | presence |\n| `APS5_GONE` | presence |\n" -def repository(root, base, head): +def repository(root, base, head, target=None): def git(*args): subprocess.run(["git", "-c", f"safe.directory={root}", "-c", "user.name=Conventions Test", "-c", "user.email=test@example.com", "-c", "commit.gpgsign=false", *args], @@ -34,16 +34,23 @@ def repository(root, base, head): git("add", ".") git("commit", "-qm", "docs: add base") git("tag", "base") + if target is not None: + git("branch", "pull") + apply(target) + git("add", "-A") + git("commit", "-qm", "docs: advance the target") + git("tag", "target") + git("checkout", "-q", "pull") apply(head) git("add", "-A") git("commit", "-qm", "feat: read a new name") -def findings_of(root, base, head): +def findings_of(root, base, head, ref="base"): previous = os.getcwd() os.chdir(root) try: - check = Check("base", "HEAD") + check = Check(ref, "HEAD") check.env_reference() return check.findings finally: @@ -51,12 +58,12 @@ def findings_of(root, base, head): class EnvReferenceDiffTests(unittest.TestCase): - def run_case(self, base, head): + def run_case(self, base, head, target=None): directory = tempfile.TemporaryDirectory() self.addCleanup(directory.cleanup) root = Path(directory.name) - repository(root, base, head) - return findings_of(root, base, head) + repository(root, base, head, target) + return findings_of(root, base, head, "base" if target is None else "target") def test_only_a_name_the_pull_request_adds_is_reported_at_its_line(self): base = { @@ -143,6 +150,29 @@ class EnvReferenceDiffTests(unittest.TestCase): self.assertEqual(self.run_case(base, head), [("env-reference", DOC, 0, "APS5_GONE has a row but the code no longer reads it")]) + def test_a_row_the_target_added_after_the_branch_point_documents_the_read(self): + base = {SAMPLE: "int unrelated;\n"} + target = {DOC: "| `APS5_NEW_READ` | presence |\n"} + head = {SAMPLE: 'static bool a = std::getenv("APS5_NEW_READ") != nullptr;\n'} + self.assertEqual(self.run_case(base, head, target), []) + + def test_a_read_the_target_does_not_document_is_still_reported(self): + base = {SAMPLE: "int unrelated;\n"} + target = {DOC: DOC_TEXT} + head = {SAMPLE: 'static bool a = std::getenv("APS5_NEW_READ") != nullptr;\n'} + self.assertEqual(self.run_case(base, head, target), [("env-reference", SAMPLE, 1, + f"APS5_NEW_READ is read here but has no row in {DOC}")]) + + def test_a_row_the_pull_request_removes_is_reported_although_the_target_keeps_it(self): + base = { + DOC: DOC_TEXT, + SAMPLE: 'static bool a = std::getenv("APS5_DOCUMENTED") != nullptr;\n', + } + target = {"core/libs/prx/libSceExample/Other.cpp": "int other;\n"} + head = {DOC: "| `APS5_GONE` | presence |\n"} + self.assertEqual(self.run_case(base, head, target), [("env-reference", DOC, 0, + "APS5_DOCUMENTED is still read by the code but this pull request removes its row")]) + if __name__ == "__main__": unittest.main() From 988f98f40c67341d64c83a1ad9b01acd60f510b6 Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Wed, 21 Oct 2026 05:17:34 +0000 Subject: [PATCH 096/523] docs(debt): cut stale libScePsml_debug entries the MFSR queries supersede Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- docs/dev/debt/libScePsml_debug.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index e63e91a198..a4cd5f3070 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -2,7 +2,7 @@ ### Unknown function info -- [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - signatures from the calls in PPSA08330 (0x5724b6-0x5727e8, 0xc8697) and PPSA03016 (0x43dcf4d-0x43dd69a): a `{block bytes, alignment, block count}` requirement, a 32-byte shared-resources parameter with the type at 0 and the `{address, bytes}` block list at 8 and 16, a context parameter with the block list at 8 and 16 and the shared-resources handle at 0x38, and 8-byte handles written through the first argument and passed back by value. The other context parameter fields (a 16-bit size pair at 0x18, two descriptor pointers at 0x20 and 0x28) are not read. The requirement of four 2 MiB blocks, 2 MiB aligned, is what PPSA08330 v06.006 allocates regardless of the returned count; Sony's values are unknown ([KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/libPsml.cpp) derives 52 blocks for type 0; its layout differs from these calls: it writes a 0x30-byte object through the first argument, where PPSA08330 passes one of two adjacent 8-byte handle globals, and reads the shared resources at context parameter offset 8, where both titles pass the block list). Only type 0 is accepted. The error codes for null pointers, short block lists and foreign handles are unknown, so those calls throw +- [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - signatures from the calls in PPSA08330 (0x5724b6-0x5727e8, 0xc8697) and PPSA03016 (0x43dcf4d-0x43dd69a): a `{block bytes, alignment, block count}` requirement, a 32-byte shared-resources parameter with the type at 0 and the `{address, bytes}` block list at 8 and 16, a context parameter with the block list at 8 and 16 and the shared-resources handle at 0x38, and 8-byte handles written through the first argument and passed back by value. The descriptor pointer at 0x28 is not read. The requirement of four 2 MiB blocks, 2 MiB aligned, is what PPSA08330 v06.006 allocates regardless of the returned count; Sony's values are unknown ([KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/libPsml.cpp) derives 52 blocks for type 0; its layout differs from these calls: it writes a 0x30-byte object through the first argument, where PPSA08330 passes one of two adjacent 8-byte handle globals, and reads the shared resources at context parameter offset 8, where both titles pass the block list). Only type 0 is accepted. The error codes for null pointers, short block lists and foreign handles are unknown, so those calls throw - [scePsmlMfsr2CreateContext, scePsmlMfsr2CreateSharedResources, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords, scePsmlMfsr2GetSharedResourcesInitRequirement, scePsmlMfsr2Init, scePsmlMfsr2ReleaseContext, scePsmlMfsr2ReleaseSharedResources](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters - [scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_debug) - argument layouts unknown; PPSA26344 imports but does not call the first two and at `0x87EFA` passes `(rdi, rsi=&stack, rdx)` as three pointers to `GetDispatchMfsrPacket1100`; before `scePsmlMfsrInit` they return 0x8A810001 (not initialized) and after it they throw. `scePsmlMfsrCreateContext1300` and `scePsmlMfsrGetDispatchMfsrPacket900`, `1000` and `1300` take the same shapes (context slot and parameter; context, command buffer and parameter) and behave the same way. PPSA08330 and PPSA03016 pass `GetDispatchMfsrPacket900`/`1000` the context handle, a `{cursor, size in dwords}` command buffer and the dispatch parameters, and advance their cursor by the size `scePsmlMfsrGetDispatchMfsrPacketSizeInDwords` returned. Whether the 1100 and 1300 versions take these arguments, and the console's error, are unverified - [scePsmlMfsrGetDispatchMfsrPacket1000](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA25344. libScePsml and libScePsml_debug are built from the same source @@ -12,4 +12,4 @@ ### Functional - [scePsmlMfsrGetDispatchMfsrPacketSizeInDwords, scePsmlMfsrGetMipmapBias, scePsmlMfsrSelectConfig, scePsmlMfsrRequestCapture, scePsmlMfsrIsCaptureInProgress](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - semantics from the APR-EMU `fakelib/libScePsml.sprx`. Guards: `0x8A810001` uninitialized, `0x8A810005` null/foreign handle (fakelib checks an engine id `0xA9C4`/`0x9231`; our map check is the equivalent), `0x8A810009` null output. `GetDispatchMfsrPacketSizeInDwords` writes `0x600`; `GetMipmapBias(context, bias, dimension)` writes `log2f(dimension / context->inputWidth) - 1.0f`, width recorded at `CreateContext800M3_2` from the parameter pair at 0x18 or `descriptor0 + 0x20` for callers that pass a descriptor pointer there (indirection inferred; zero width returns `0x8A810005`). `SelectConfig`, `RequestCapture`, `IsCaptureInProgress` validate then return 0. The `GetDispatchMfsrPacket*` calls still throw: the 0x158-byte template's packet routine is not understood -- [scePsmlMfsrInit](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - succeeds and only sets the initialized state; the shared resources and contexts are bookkeeping objects that use none of the guest blocks. Whether Sony's library fails on hardware without PSSR is unknown. PPSA08330 calls the setup unconditionally and only uses a context when its cached `sceKernelIsTrinityMode` result is set (0x3c643d, 0x567f97; otherwise it stores a -0.25 mip bias itself at 0x567fe2), which AnyPS5 reports as 0, so the dispatch, mip bias and capture calls still throw +- [scePsmlMfsrInit](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - succeeds and only sets the initialized state; the shared resources and contexts are bookkeeping objects that use none of the guest blocks. Whether Sony's library fails on hardware without PSSR is unknown. PPSA08330 calls the setup unconditionally and only uses a context when its cached `sceKernelIsTrinityMode` result is set (0x3c643d, 0x567f97; otherwise it stores a -0.25 mip bias itself at 0x567fe2), which AnyPS5 reports as 0 From 0d0b29cf446f035b6adf34d4d21400e8e8633d0f Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:47:05 +0000 Subject: [PATCH 097/523] test(relinker): require libc for kernel variant fallback --- core/relinker/elfpatcher/tests/EntryStubTests.cpp | 4 ++-- .../elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/relinker/elfpatcher/tests/EntryStubTests.cpp b/core/relinker/elfpatcher/tests/EntryStubTests.cpp index 5802231e59..2dc67f2ec2 100644 --- a/core/relinker/elfpatcher/tests/EntryStubTests.cpp +++ b/core/relinker/elfpatcher/tests/EntryStubTests.cpp @@ -138,8 +138,8 @@ void windowsKernelVariantDependenciesLoadCanonicalKernel() { dynamic.DynamicSegmentData.resize(16); Io::WriteU64(dynamic.DynamicSegmentData, 0, 1); Io::WriteU64(dynamic.DynamicSegmentData, 8, 0); - require(WindowsImportBuilder().ReadLibraries(dynamic) == std::vector{variant, "libkernel.prx"}, - variant + ": canonical libkernel is not loaded after the requested module"); + require(WindowsImportBuilder().ReadLibraries(dynamic) == std::vector{variant, "libkernel.prx", "libc.prx"}, + variant + ": canonical kernel fallback and its libc provider are not loaded after the requested module"); } } diff --git a/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp b/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp index 7a0d29e1bf..15197aa868 100644 --- a/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp +++ b/core/relinker/elfpatcher/tests/WindowsDependencyDiagnosticsTests.cpp @@ -55,8 +55,8 @@ void checkRuntimeDependencies() { {"libc.prx", "libSceLibcInternal.prx"}); check({"libSceVideoOut.prx", "libkernel.prx", "libSceVideoOut.prx"}, {"libSceVideoOut.prx", "libkernel.prx", "libc.prx"}); - check({"libkernel_sys.prx"}, {"libkernel_sys.prx", "libkernel.prx"}); - check({"libkernel_web.prx"}, {"libkernel_web.prx", "libkernel.prx"}); + check({"libkernel_sys.prx"}, {"libkernel_sys.prx", "libkernel.prx", "libc.prx"}); + check({"libkernel_web.prx"}, {"libkernel_web.prx", "libkernel.prx", "libc.prx"}); check({"libSceVideoOut.prx", "libSceVideoOut.prx"}, {"libSceVideoOut.prx"}); } From 7fdad075c3d25cd03120ff584a28f4d834d17c51 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:47:33 +0000 Subject: [PATCH 098/523] fix(relinker): include libc for kernel variant imports --- .../elfpatcher/src/windows/WindowsImportBuilder.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp index 4f41f50b82..f2d45060be 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsImportBuilder.cpp @@ -53,10 +53,15 @@ std::vector WindowsImportBuilder::ReadLibraries(const Domain::SysVD // AnyPS5 implements the C runtime in libc.prx. Windows GetProcAddress // does not search a module's dependencies as ELF symbol lookup does. // Keep the requested module first, then make its shared runtime visible. - if ((unique.contains("libSceLibcInternal.prx") || unique.contains("libkernel.prx")) && !unique.contains("libc.prx")) - result.push_back("libc.prx"); - if ((unique.contains("libkernel_sys.prx") || unique.contains("libkernel_web.prx")) && !unique.contains("libkernel.prx")) - result.push_back("libkernel.prx"); + const auto addIfMissing = [&](const char* library) { + if (unique.insert(library).second) result.emplace_back(library); + }; + if (unique.contains("libSceLibcInternal.prx") || unique.contains("libkernel.prx")) + addIfMissing("libc.prx"); + if (unique.contains("libkernel_sys.prx") || unique.contains("libkernel_web.prx")) { + addIfMissing("libkernel.prx"); + addIfMissing("libc.prx"); + } return result; } From aa56b002285182763ac422483bd384d309568ee5 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Wed, 21 Oct 2026 08:06:00 +0200 Subject: [PATCH 099/523] test(libSceNet): allow more timed sends before the blocking-send test expects EAGAIN The test fills the send path with up to 16 timed sends and expects one of them to return EAGAIN. Where the loopback receive buffer can grow to 32 MiB (tcp_rmem max 33554432), 16 sends of at most 4 MiB do not fill it and the three net_dontwait_blocking_send tests fail with "peer never exhausted the send buffers". Raise the bound to 64; the loop still ends at the first EAGAIN. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/tests/NetBlockingSend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/tests/NetBlockingSend.cpp b/core/libs/tests/NetBlockingSend.cpp index fb64d834ba..6e3b3fdebb 100644 --- a/core/libs/tests/NetBlockingSend.cpp +++ b/core/libs/tests/NetBlockingSend.cpp @@ -113,7 +113,7 @@ int main(int argc, char** argv) { Require(partial > 0 && partial < static_cast(bytes.size()), "timeout must return queued bytes"); Require(std::chrono::steady_clock::now() - started >= std::chrono::milliseconds(80), "partial send returned before its timeout"); bool emptyTimeout = false; - for (int attempt = 0; attempt < 16; ++attempt) { + for (int attempt = 0; attempt < 64; ++attempt) { const auto result = Send(api, connection.sender, bytes); if (result < 0) { Require(*sceNetErrnoLoc() == NET_EAGAIN, "timeout without progress must return EAGAIN"); From de573c6d1ef287fe20a5e90f728712fef8e3bb0c Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Wed, 21 Oct 2026 06:14:13 +0000 Subject: [PATCH 100/523] test(net): bound Linux blocking-send receiver buffer --- core/libs/tests/NetBlockingSend.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/libs/tests/NetBlockingSend.cpp b/core/libs/tests/NetBlockingSend.cpp index fb64d834ba..86ccdde3f5 100644 --- a/core/libs/tests/NetBlockingSend.cpp +++ b/core/libs/tests/NetBlockingSend.cpp @@ -107,6 +107,9 @@ int main(int argc, char** argv) { } { Connection connection; +#ifndef _WIN32 + SetOption(connection.receiver, NET_SO_RCVBUF, 4096); +#endif SetOption(connection.sender, 0x1105, 100000); auto started = std::chrono::steady_clock::now(); const auto partial = Send(api, connection.sender, bytes); From fd178d902130feffa682dafae1f96622c2af272f Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Wed, 21 Oct 2026 09:32:28 +0300 Subject: [PATCH 101/523] docs(debt): note where the Hmd field of view query leaves shadPS4's order --- docs/dev/debt/libSceHmd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 2a7c96d0ff..ee1012aafa 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -6,7 +6,7 @@ ### Unknown function info -- [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count +- [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count - [sceHmdInternalGetPUVersion](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalEventInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetVirtualDisplaySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From ede012406323abf22b9e9c38985d7421f60ac812 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Wed, 21 Oct 2026 09:36:43 +0300 Subject: [PATCH 102/523] fix(libSceHttp): refuse the option calls before init and sceHttpSetEpoll for a handle that is not an epoll The last sceHttpTerm now ends every template, connection and request's options, so a stale id is refused after the next sceHttpInit. --- core/libs/prx/libSceHttp/Export.cpp | 6 +++++ core/libs/prx/libSceHttp/src/Options.cpp | 13 ++++++++++ core/libs/prx/libSceHttp/src/Options.hpp | 1 + core/libs/tests/GuestHttpOptions.cpp | 31 ++++++++++++++++++++++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 2781b783c0..a0052fb084 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -303,6 +303,7 @@ int APS5_VABI sceHttpSetCookieEnabled(int id, int enable) { } int APS5_VABI sceHttpSetAutoRedirect(int id, int enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; return SetHttpOption(id, &HttpOptions::autoRedirect, enable != 0) ? 0 : ERROR_INVALID_ID; } @@ -313,10 +314,14 @@ int APS5_VABI sceHttpSetConnectTimeOut(int id, uint32_t usec) { } int APS5_VABI sceHttpSetEpoll(int id, HttpEpollHandle eh, void* user_arg) { + std::lock_guard lock(g_stateMutex); + if (g_contexts.empty()) return ERROR_BEFORE_INIT; + if (!FindEpoll(eh)) return ERROR_INVALID_ID; return SetHttpEpollBinding(id, eh, user_arg) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpSetNonblock(int id, int enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; return SetHttpOption(id, &HttpOptions::nonblock, enable != 0) ? 0 : ERROR_INVALID_ID; } @@ -395,6 +400,7 @@ int APS5_VABI sceHttpTerm_nid_postfix(int http_ctx_id) { } int APS5_VABI sceHttpUnsetEpoll(int id) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; return SetHttpEpollBinding(id, nullptr, nullptr) ? 0 : ERROR_INVALID_ID; } diff --git a/core/libs/prx/libSceHttp/src/Options.cpp b/core/libs/prx/libSceHttp/src/Options.cpp index 6889908fa4..710b33311a 100644 --- a/core/libs/prx/libSceHttp/src/Options.cpp +++ b/core/libs/prx/libSceHttp/src/Options.cpp @@ -23,6 +23,12 @@ void AddHttpContext(int contextId) { void RemoveHttpContext(int contextId) { std::lock_guard lock(g_optionsMutex); g_contextAcceptEncodingGzip.erase(contextId); + if (g_contextAcceptEncodingGzip.empty()) g_objectOptions.clear(); +} + +bool HttpInitialised() { + std::lock_guard lock(g_optionsMutex); + return !g_contextAcceptEncodingGzip.empty(); } void AddHttpTemplate(int templateId, int contextId) { @@ -78,6 +84,7 @@ bool SetHttpEpollBinding(int id, HttpEpollHandle epoll, void* argument) { extern "C" { int APS5_VABI sceHttpGetAutoRedirect(int id, int* enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; if (!enable) return ERROR_INVALID_VALUE; const auto options = FindHttpOptions(id); if (!options) return ERROR_INVALID_ID; @@ -86,6 +93,7 @@ int APS5_VABI sceHttpGetAutoRedirect(int id, int* enable) { } int APS5_VABI sceHttpGetNonblock(int id, int* enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; if (!enable) return ERROR_INVALID_VALUE; const auto options = FindHttpOptions(id); if (!options) return ERROR_INVALID_ID; @@ -98,6 +106,7 @@ int APS5_VABI sceHttpTryGetNonblock(int id, int* enable) { } int APS5_VABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; if (!enable) return ERROR_INVALID_VALUE; const auto options = FindHttpOptions(id); if (!options) return ERROR_INVALID_ID; @@ -106,10 +115,12 @@ int APS5_VABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* enable) { } int APS5_VABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; return SetHttpOption(id, &HttpOptions::acceptEncodingGzip, enable != 0) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpGetEpoll(int id, HttpEpollHandle* epoll, void** argument) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; if (!epoll) return ERROR_INVALID_VALUE; const auto options = FindHttpOptions(id); if (!options) return ERROR_INVALID_ID; @@ -119,11 +130,13 @@ int APS5_VABI sceHttpGetEpoll(int id, HttpEpollHandle* epoll, void** argument) { } int APS5_VABI sceHttpCacheRedirectedConnectionEnabled(int id, int enable) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; (void)enable; return FindHttpOptions(id) ? 0 : ERROR_INVALID_ID; } int APS5_VABI sceHttpsEnableOptionPrivate(int id, std::uint32_t sslFlags) { + if (!HttpInitialised()) return ERROR_BEFORE_INIT; if ((sslFlags & ~PrivateSslFlags) != 0) return ERROR_INVALID_VALUE; return FindHttpOptions(id) ? 0 : ERROR_INVALID_ID; } diff --git a/core/libs/prx/libSceHttp/src/Options.hpp b/core/libs/prx/libSceHttp/src/Options.hpp index 998fbd022a..beca67b395 100644 --- a/core/libs/prx/libSceHttp/src/Options.hpp +++ b/core/libs/prx/libSceHttp/src/Options.hpp @@ -14,6 +14,7 @@ struct HttpOptions { void AddHttpContext(int contextId); void RemoveHttpContext(int contextId); +bool HttpInitialised(); void AddHttpTemplate(int templateId, int contextId); void AddHttpChild(int id, int parentId); void RemoveHttpObject(int id); diff --git a/core/libs/tests/GuestHttpOptions.cpp b/core/libs/tests/GuestHttpOptions.cpp index 775e0701fa..bfa0a25748 100644 --- a/core/libs/tests/GuestHttpOptions.cpp +++ b/core/libs/tests/GuestHttpOptions.cpp @@ -36,6 +36,7 @@ int APS5_VABI sceHttpAbortRequestForce(int); namespace { +constexpr int BeforeInit = static_cast(0x80431001u); constexpr int InvalidId = static_cast(0x80431100u); constexpr int InvalidValue = static_cast(0x804311FEu); constexpr int UnknownId = 0x7fff0000; @@ -127,8 +128,33 @@ void CheckEpoll(int context) { Require(sceHttpUnsetEpoll(request) == 0 && sceHttpGetEpoll(request, &bound, &argument) == 0 && bound == nullptr && argument == nullptr, "sceHttpUnsetEpoll left the binding"); Require(sceHttpGetEpoll(request, nullptr, &argument) == InvalidValue, "a null epoll output was not refused"); Require(sceHttpGetEpoll(UnknownId, &bound, &argument) == InvalidId && sceHttpSetEpoll(UnknownId, epoll, nullptr) == InvalidId && sceHttpUnsetEpoll(UnknownId) == InvalidId, "an unknown id was not refused"); - Require(sceHttpDeleteRequest(request) == 0 && sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); + const auto unknownEpoll = reinterpret_cast(std::uintptr_t{0x1234}); + Require(sceHttpSetEpoll(request, nullptr, &marker) == InvalidId && sceHttpSetEpoll(request, unknownEpoll, &marker) == InvalidId, "sceHttpSetEpoll took a handle that is not an epoll"); Require(sceHttpDestroyEpoll(context, epoll) == 0, "sceHttpDestroyEpoll failed"); + Require(sceHttpSetEpoll(request, epoll, &marker) == InvalidId, "sceHttpSetEpoll took a destroyed epoll"); + Require(sceHttpGetEpoll(request, &bound, &argument) == 0 && bound == nullptr, "a refused sceHttpSetEpoll changed the binding"); + Require(sceHttpDeleteRequest(request) == 0 && sceHttpDeleteConnection(connection) == 0 && sceHttpDeleteTemplate(pattern) == 0, "delete failed"); +} + +void CheckTermEndsOptions() { + const int context = sceHttpInit_nid_postfix(1, 1, 0x10000); + const int pattern = sceHttpCreateTemplate(context, "anyps5-test", 2, 0); + const int connection = sceHttpCreateConnectionWithURL(pattern, "http://example.com/", 1); + Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); + int value = -1; + HttpEpollHandle bound = nullptr; + Require(sceHttpGetAutoRedirect(pattern, &value) == BeforeInit && sceHttpSetAutoRedirect(pattern, 0) == BeforeInit, "the redirect option outlived the last sceHttpTerm"); + Require(sceHttpGetNonblock(connection, &value) == BeforeInit && sceHttpTryGetNonblock(connection, &value) == BeforeInit, "the nonblocking getters work after the last sceHttpTerm"); + Require(sceHttpSetNonblock(connection, 1) == BeforeInit && sceHttpTrySetNonblock(connection, 1) == BeforeInit, "the nonblocking setters work after the last sceHttpTerm"); + Require(sceHttpGetAcceptEncodingGZIPEnabled(pattern, &value) == BeforeInit && sceHttpSetAcceptEncodingGZIPEnabled(pattern, 1) == BeforeInit, "the gzip option outlived the last sceHttpTerm"); + Require(sceHttpGetEpoll(connection, &bound, nullptr) == BeforeInit && sceHttpUnsetEpoll(connection) == BeforeInit, "the epoll binding outlived the last sceHttpTerm"); + Require(sceHttpCacheRedirectedConnectionEnabled(connection, 1) == BeforeInit, "sceHttpCacheRedirectedConnectionEnabled works after the last sceHttpTerm"); + Require(sceHttpsEnableOptionPrivate(connection, 0x4000u) == BeforeInit && sceHttpsDisableOptionPrivate(connection, 0x4000u) == BeforeInit, "the private SSL options work after the last sceHttpTerm"); + Require(sceHttpGetNonblock(connection, nullptr) == BeforeInit, "a null output is checked before initialisation"); + Require(value == -1 && bound == nullptr, "a refused getter wrote its output"); + const int next = sceHttpInit_nid_postfix(1, 1, 0x10000); + Require(sceHttpSetAutoRedirect(pattern, 0) == InvalidId && sceHttpGetNonblock(connection, &value) == InvalidId, "an object outlived the last sceHttpTerm into a new sceHttpInit"); + Require(sceHttpTerm_nid_postfix(next) == 0, "sceHttpTerm failed"); } void CheckUnobservedOptions(int context) { @@ -159,7 +185,8 @@ int main() { CheckEpoll(context); CheckUnobservedOptions(context); Require(sceHttpTerm_nid_postfix(context) == 0, "sceHttpTerm failed"); - Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 1) == static_cast(0x80431001u), "a gzip default was taken before sceHttpInit"); + Require(sceHttpSetDefaultAcceptEncodingGZIPEnabled(context, 1) == BeforeInit, "a gzip default was taken before sceHttpInit"); + CheckTermEndsOptions(); std::puts("HTTP option tests passed"); return 0; } From 0406a1b54e7f224649f46a8aaa71a7f7f9c10ec3 Mon Sep 17 00:00:00 2001 From: Yossi Yadgar Date: Wed, 21 Oct 2026 09:36:43 +0300 Subject: [PATCH 103/523] docs(debt): drop the unknown-signature lines for the HTTP option calls --- docs/dev/debt/libSceHttp.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index afb4cdf0cc..24bd494246 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -7,7 +7,7 @@ ### Unknown function info -- [HTTP object options](../../../core/libs/prx/libSceHttp/src/Options.cpp) (libSceHttp) - a template starts with redirects followed, blocking calls and no `Accept-Encoding: gzip`, a connection or request copies its parent's options when it is created, and `sceHttpGetAutoRedirect`, `sceHttpGetNonblock`, `sceHttpGetAcceptEncodingGZIPEnabled` and `sceHttpGetEpoll` read them back; these defaults and the copying follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/network/http.cpp) (PS4) and are not measured on a PS5. `sceHttpSetDefaultAcceptEncodingGZIPEnabled` sets the gzip value for templates created later in that context, where shadPS4 keeps one value for the library, and the option setters now return `ERROR_INVALID_ID` for an id that is not a live template, connection or request. The options change no request, as none reaches the network +- [HTTP object options](../../../core/libs/prx/libSceHttp/src/Options.cpp) (libSceHttp) - a template starts with redirects followed, blocking calls and no `Accept-Encoding: gzip`, and a connection or request copies its parent's options when it is created. The signatures of the option calls, these defaults, the copying and the order of the checks (`BEFORE_INIT` first; the last `sceHttpTerm` ends every object's options) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/network/http.cpp) (PS4) and are not measured on a PS5. `sceHttpSetDefaultAcceptEncodingGZIPEnabled` sets the gzip value for templates created later in that context, where shadPS4 keeps one value for the library, and `sceHttpUnsetEpoll` takes a template or connection id, where shadPS4 looks only at requests. The options change no request, as none reaches the network - [sceHttpUriBuild](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) retains `//` for a non-opaque URI independently of the selected fields, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a8a70da595da2a96e0410309472ba4a7eb558144/src/core/libraries/network/http.cpp#L3551-L3558). Black Myth: Wukong builds its `Scheme` header with option 1 and expects `http://` or `https://`. Other partial field combinations follow the PS4 reference and remain unverified on PS5 hardware. - [sceHttpSetCookieEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) - the two-argument signature and 0/1 enable values follow [VitaSDK](https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h) and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp); PPSA01521 passes 0 to disable cookies. Disabling succeeds because the offline HTTP backend has no cookie engine; enabling throws. Invalid enable values use the same error as `sceHttpSetInflateGZIPEnabled`. Like the existing offline HTTP setters, the handle is not validated; requests still fail with the network error. Console behavior is unverified - [sceHttpParseResponseHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); parsing and error codes follow [shadPS4's reverse-engineered PS4 library](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp), not confirmed on a PS5 title or hardware. Folded values retain their line breaks and whitespace; an empty first line starts the value scan after its line feed. A consumed byte count above `INT_MAX` throws instead of wrapping. @@ -32,17 +32,11 @@ - [sceHttpDbgShowMemoryPoolStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgGetRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpAbortRequestForce](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpAddRequestHeaderRaw](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpRemoveRequestHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetAuthEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetAutoRedirect](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpTrySetNonblock](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetNonblock](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpTryGetNonblock](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetEpoll](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookie](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookieEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpAddCookie](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature @@ -54,14 +48,9 @@ - [sceHttpSetCookieMaxNum](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetCookieMaxNumPerDomain](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookieStats](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpsEnableOptionPrivate](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpsDisableOptionPrivate](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetPriorityOption](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetPolicyOption](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpRequestGetAllHeaders](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetAcceptEncodingGZIPEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetAcceptEncodingGZIPEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetDefaultAcceptEncodingGZIPEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetProxy](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetHttp09Enabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature @@ -69,7 +58,6 @@ - [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpAddQuery](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetDelayBuildRequestEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpCacheRedirectedConnectionEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature ### Functional From da5c7a895d0c6bd4d7b5246b6156b0c2c5577e55 Mon Sep 17 00:00:00 2001 From: thuanquang Date: Mon, 19 Oct 2026 23:41:56 +0700 Subject: [PATCH 104/523] fix(agc): give AMD's proprietary driver robustBufferAccess2 again enabling it broke agc_driver_layered_volume_draw, agc_driver_layered_volume_vertex and agc_driver_subvector_loop on an RX 6700 XT. With #7623 and #7828 on main, those tests pass with the feature on, in either library-mode order. This reverts 48b394d1e and keeps the record of #7623's empty-mask test in the s_subvector_loop debt entry. Co-Authored-By: Claude --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 9 ----- .../Execution/include/RobustBufferAccess.hpp | 14 -------- .../Execution/src/VulkanDevice.cpp | 10 +----- .../tests/RobustBufferAccess.cpp | 33 ------------------- docs/dev/debt/shader.md | 4 +-- 5 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 core/libs/prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp delete mode 100644 core/libs/prx/libSceAgcDriver/tests/RobustBufferAccess.cpp diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 2782023e6a..c94391c9b4 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -712,15 +712,6 @@ if(BUILD_TESTING) add_test(NAME agc_pipeline_link_optimization COMMAND agc_pipeline_link_optimization_tests) endif() -add_test_executable(agc_robust_buffer_access_tests EXCLUDE_FROM_ALL tests/RobustBufferAccess.cpp) -target_include_directories(agc_robust_buffer_access_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}) -set_target_properties(agc_robust_buffer_access_tests PROPERTIES CXX_EXTENSIONS OFF) -configure_windows_unwind(agc_robust_buffer_access_tests) -if(BUILD_TESTING) - set_target_properties(agc_robust_buffer_access_tests PROPERTIES EXCLUDE_FROM_ALL OFF) - add_test(NAME agc_robust_buffer_access COMMAND agc_robust_buffer_access_tests) -endif() - add_test_executable(agc_draw_inputs_in_place_driver_tests EXCLUDE_FROM_ALL tests/DrawInputsInPlace.cpp) target_include_directories(agc_draw_inputs_in_place_driver_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}) set_target_properties(agc_draw_inputs_in_place_driver_tests PROPERTIES CXX_EXTENSIONS OFF) diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp deleted file mode 100644 index a180b23104..0000000000 --- a/core/libs/prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_ROBUSTBUFFERACCESS_HPP -#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_ROBUSTBUFFERACCESS_HPP - -#include - -namespace AgcDriver { - -inline bool RobustBufferAccess2Usable(VkDriverId driver) { - return driver != VK_DRIVER_ID_AMD_PROPRIETARY; -} - -} - -#endif diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 226c792301..0ed7b7bf1f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -18,7 +18,6 @@ #include "prx/libSceAgcDriver/Execution/include/Float32FlushControl.hpp" #include "prx/libSceAgcDriver/Execution/include/PipelineLinkOptimization.hpp" #include "prx/libSceAgcDriver/Execution/include/DrawInputsInPlace.hpp" -#include "prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp" #include "prx/libSceAgcDriver/Execution/include/SubgroupBarrier.hpp" #include "prx/libSceAgcDriver/Execution/include/SubgroupClock.hpp" #include "prx/libSceAgcDriver/Execution/include/ShaderDeviceProfile.hpp" @@ -1915,14 +1914,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) : VkPhysicalDeviceFeatures2 queried{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &robustness2}; state->InstanceFunction("vkGetPhysicalDeviceFeatures2")(selected, &queried); } - VkDriverId robustnessDriver = VK_DRIVER_ID_MAX_ENUM; - if (hasExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) { - VkPhysicalDeviceDriverProperties driver{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES}; - VkPhysicalDeviceProperties2 driverProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &driver}; - state->InstanceFunction("vkGetPhysicalDeviceProperties2")(selected, &driverProperties); - robustnessDriver = driver.driverID; - } - state->robustBufferAccess2 = robustness2.robustBufferAccess2 == VK_TRUE && enabled.robustBufferAccess == VK_TRUE && RobustBufferAccess2Usable(robustnessDriver); + state->robustBufferAccess2 = robustness2.robustBufferAccess2 == VK_TRUE && enabled.robustBufferAccess == VK_TRUE; if (robustness2.nullDescriptor == VK_TRUE || state->robustBufferAccess2) { const bool nullDescriptor = robustness2.nullDescriptor == VK_TRUE; robustness2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT}; diff --git a/core/libs/prx/libSceAgcDriver/tests/RobustBufferAccess.cpp b/core/libs/prx/libSceAgcDriver/tests/RobustBufferAccess.cpp deleted file mode 100644 index 6b0331109f..0000000000 --- a/core/libs/prx/libSceAgcDriver/tests/RobustBufferAccess.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp" -#include - -namespace { - -struct Case { - VkDriverId driver; - bool usable; -}; - -constexpr Case Cases[]{ - {VK_DRIVER_ID_AMD_PROPRIETARY, false}, - {VK_DRIVER_ID_AMD_OPEN_SOURCE, true}, - {VK_DRIVER_ID_MESA_RADV, true}, - {VK_DRIVER_ID_NVIDIA_PROPRIETARY, true}, - {VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, true}, - {VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, true}, - {VK_DRIVER_ID_MESA_LLVMPIPE, true}, - {VK_DRIVER_ID_MOLTENVK, true}, -}; - -} - -int main() { - int failures = 0; - for (const auto& item : Cases) { - if (AgcDriver::RobustBufferAccess2Usable(item.driver) != item.usable) { - std::fprintf(stderr, "driver %d: expected robustBufferAccess2 to be %s\n", static_cast(item.driver), item.usable ? "usable" : "unusable"); - ++failures; - } - } - return failures == 0 ? 0 : 1; -} diff --git a/docs/dev/debt/shader.md b/docs/dev/debt/shader.md index 5c42726ddc..4e1b62f1de 100644 --- a/docs/dev/debt/shader.md +++ b/docs/dev/debt/shader.md @@ -17,7 +17,7 @@ - [Color comparison descriptor forms](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp): emulated color comparison supports UNORM, SNORM and float formats for 2D, 2D-array and cube views. 8-bit sRGB and UNORM (`Format8Srgb`, `Format8_8Srgb`, `Format8_8_8_8Srgb`, `Format8UNorm`, `Format8_8UNorm`, `Format8_8_8_8UNorm`) return 0 for every compare function, measured with `image_sample_c_lz` on gfx1035 over references -0.5..1.5 and texel codes 0..255. Anisotropic filters are refused, except on those 8-bit formats, where every texel comparison is 0 (not measured with an anisotropic S#). Bilinear cube comparisons of float, snorm and the other unorm formats without `DISABLE_CUBE_WRAP` are refused; on that hardware a 4x4 R32 float cube blends across faces (46 of 90 edge rows differ from a per-face clamp; face 0, ref 0.1, u=3.9 v=2.5 is 0.6016 and u=1.5 v=0.1 is 0.3984), and with `DISABLE_CUBE_WRAP` the per-face clamp matches. An 8-bit sRGB or UNORM bilinear cube without that bit returns 0, since every texel comparison is already 0 for every compare function and the selected face cannot change the result. Cube-edge sampling behavior and console coverage on PS5's gfx1013 remain unverified. - [s_setkill](../../../core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp) (core/shader/recompiler) - decoded as `s_endpgm` for any immediate: measured on RDNA2 hardware (gfx1035), a store before it is visible and nothing after it runs ([#771](https://github.com/boykopovar/AnyPS5/pull/771)); whether the KILL status bit it sets is observable differently from `s_endpgm` is unverified - [s_sethalt](../../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp) (core/shader/recompiler) - with SIMM16[0] clear it is a no-op: the RDNA 2 ISA guide describes that value as resuming a halted wave, and a wave that executes it is not halted; measured on a gfx1035 (RDNA 2) wave: `s_sethalt 0` followed by a VALU op completes and gives the normal result. Only SIMM16[0] is read; with it set the instruction throws -- [s_subvector_loop_begin and s_subvector_loop_end](../../../core/shader/recompiler/Translation/src/ControlFlowInstructions.cpp) (core/shader/recompiler) - follow AMD's RDNA 2 instruction set reference and a gfx1036 (wave64) measurement, pinned in [SubvectorLoop.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/SubvectorLoop.cpp); `S0` in the end instruction's `elif(S0 == 0)` is read as its SGPR operand. A wave32 shader and a destination other than an SGPR throw because neither is measured. Measured on PC RDNA2 (Radeon 610M), not on a PS5. The [specialization pass](../../../core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp) keeps a constant-condition branch whose untaken target is a loop header, since folding it removes the loop's back edge, which SPIR-V requires exactly once per loop header (SPIRV-Tools rejects the module and the NVIDIA driver 615.78.08 crashes compiling it); a loop whose back-edge block becomes unreachable in another way is not handled +- [s_subvector_loop_begin and s_subvector_loop_end](../../../core/shader/recompiler/Translation/src/ControlFlowInstructions.cpp) (core/shader/recompiler) - follow AMD's RDNA 2 instruction set reference and a gfx1036 (wave64) measurement, pinned in [SubvectorLoop.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/SubvectorLoop.cpp); `S0` in the end instruction's `elif(S0 == 0)` is read as its SGPR operand. A wave32 shader and a destination other than an SGPR throw because neither is measured. The begin instruction tests for an empty mask as `(exec_lo | exec_hi) == 0`: AMD's Windows driver (32.0.21045.5002 and 32.0.21042.62) compiled `exec_lo == 0 && exec_hi == 0` as a test of the whole 64-bit mask when guest loads use selects, as they do with `robustBufferAccess2` (#7376, #7623). Measured on PC RDNA2 (Radeon 610M), not on a PS5. The [specialization pass](../../../core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp) keeps a constant-condition branch whose untaken target is a loop header, since folding it removes the loop's back edge, which SPIR-V requires exactly once per loop header (SPIRV-Tools rejects the module and the NVIDIA driver 615.78.08 crashes compiling it); a loop whose back-edge block becomes unreachable in another way is not handled - [Vertex entry merged wave info](../../../core/shader/recompiler/Translation/src/InstructionTranslator.cpp) (recompiler) - `s3` of every vertex-path shader is set to `waveSize | waveSize << 8` (vertex and primitive count of a full wave, wave index 0), or to `32 | 32 << 8` for a wave64 program on a 32-lane host subgroup, which runs it at one lane per invocation and holds no lanes 32-63. On a device whose `subgroupSupportedStages` lacks the vertex stage (lavapipe, MoltenVK), vertex and local programs run as one-lane waves: `s3` is `1 | 1 << 8`, the lane id is 0, a ballot is the invocation's own bit, a shuffle or broadcast returns the invocation's value, an elect is true and a subgroup reduction is the value itself, so no `GroupNonUniform` capability is declared. Layout taken from the prologue Mesa's ACO emits for NGG vertex shaders (`s_bfe_u64 exec` from `s3`), not measured on hardware. The driver gives every graphics program user data from `s8`, so the condition does not tell NGG from legacy vertex shaders: they just never read `s3`. A draw whose last wave is partial still reports a full wave, and so does a host wave with idle invocations (an RTX 5070 ran every vertex warp of a non-indexed triangle list with 30 of its 32 invocations). - [s_barrier in vertex-path programs](../../../core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp) (recompiler) - emits no instruction when the program has no LDS or GDS access and no memory write or atomic, so no wave of the subgroup can observe another, or when the barrier runs before every memory access of the program (it is on the entry's straight-line path with no load, store, atomic, image or LDS access ahead of it), so it orders nothing; the `GS_ALLOC_REQ` such a barrier orders on NGG passthrough programs is already dropped on the vertex path. The Vertex execution model has no workgroup barrier. Other programs with LDS, GDS or memory writes throw. Not measured on hardware - [Vertex clip and cull distances](../../../core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp) (libSceAgcDriver) - `PA_CL_VS_OUT_CNTL` enable bit `s` of `CLIP_DIST_ENA`/`CULL_DIST_ENA` is taken to select component `s` of the `pos1`/`pos2` exports, with the Vulkan array index counted by the enabled bits below it, and `VS_OUT_CCDIST0_VEC_ENA` as bit 22; this follows the recompiler's existing position-export mapping, not hardware or documentation. The guest clip distances share one `gl_ClipDistance` array with the all-zero clip position distance, which takes the index after them. A vertex program whose own code has no `pos1`..`pos3` export is compiled and prepared without these bits (`PositionExportLayout`), so registration and draws agree on its artifact; an export in a subroutine it calls is not seen by that scan @@ -171,6 +171,6 @@ - [Flag structurization](../../../core/shader/recompiler/ControlFlow/src/Structurizer.cpp) (shader recompiler) - a graph that every structurizer attempt refuses, irreducible and non-terminating graphs included, is rebuilt with one reach flag per block and one again flag per loop (`structurizeWithFlags`): each block keeps one copy and runs behind a guard on its flag, and each strongly connected component becomes a loop whose single latch repeats it while a back edge set its flag. The output has about 8 times the input's blocks and one route variable per block, and ends in an empty Return that never runs. Only blocks that start the top-level order unconditionally run unguarded, so resource planning also sees the static paths that skip a guarded block and can plan extra descriptor candidates for them. The attempts still run first. Shared merge splitting refuses an attempt once the graph has grown past 16 times its block count at the start of splitting (at least 64 blocks); succeeding attempts grew at most 10.6 times on random graphs of up to 128 blocks, and an attempt that would succeed only beyond the budget leaves the graph to a later attempt or to this construction. Splitting that adds one block per round stays within that budget until its 4 rounds per block run out, so the attempts can still take more than 10 s (9 of 2,000 random graphs of up to 128 blocks, 63 s at most). `APS5_FORCE_STRUCTURIZER_FALLBACK=1` builds every graph this way, for tests - [Scalar buffer descriptors](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) use only the base address, stride and record count for resources accessed exclusively by scalar loads; their fourth word is normalized to zero, following AMD RDNA2 ISA section 7.2.2. Vector and mixed accesses retain descriptor validation. Native Vulkan readbacks cover ignored fourth-word fields on Windows/NVIDIA; their console behavior has not been measured. - [Spanned wave64 compute](../../../core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvSubgroup.cpp) (shader recompiler) - `APS5_SPANNED_WAVE=1`, off by default, runs a single-wave wave64 compute program at one lane per invocation on a 32-lane host subgroup and exchanges ballots, shuffles and reductions between the two host subgroups through workgroup memory and barriers. The driver turns it on only where compute subgroups can be pinned at 32 full lanes (`subgroupSizeControl` and `computeFullSubgroups`), passes it as `SpirvTarget::spannedWaves` (part of the cache key and the serialized request), and creates the pipelines of spanned programs with `REQUIRE_FULL_SUBGROUPS`, since the lane number `SubgroupId * 32 + SubgroupLocalInvocationId` needs two full subgroups. Those barriers are correct only where every invocation of the workgroup reaches them, which holds for the guest's wave-uniform control flow. The emulated 64-bit LDS atomics ([LockedLdsUpdate](../../../core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvMemoryInstructions.cpp)) run only on the lanes the exec mask keeps, so their lock loop ballots within its own host subgroup, as its elect already does. Any other cross-lane exchange emitted inside per-lane control flow would hang a spanned wave in the same way, and the emitter does not reject one: there is no uniformity check. The only check so far is manual: disassemble the module (`spirv-dis`), list the selection and loop constructs that enclose each `OpControlBarrier`, and read the condition of each enclosing selection. In Ghost of Yōtei's program 0x80003af600 (PPSA26344), 108 of 111 barriers sat only in loops on wave-uniform conditions and 3 inside per-invocation conditionals, all in the lock loop. Other programs and titles have not been scanned. An earlier, cheaper ballot (one barrier instead of three) also lost the device for a reason that is not understood, which may be another barrier placement problem. Measured on an RTX 4090 (Windows): an offline replay of that program and two Yōtei runs that previously lost the device (NVIDIA TDR) now run past it, and [LdsMaskWaterfall.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/LdsMaskWaterfall.cpp) runs both modes and covers a half-inactive wave. Stray (PPSA02101) on Intel ANV and a Demon's Souls (PPSA01342) request replay showed no fault, before this fix -- [Guest memory load guards](../../../core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvMemoryInstructions.cpp) (shader recompiler) - a guest load whose lane is inactive or whose address is out of bounds is still performed and its result replaced by 0 with `OpSelect`, instead of a selection per guard, when the load can be pointed at a location nothing writes: scratch reads its own element 0; storage buffers (direct buffers, GDS) read element 0xfffffffe, which `robustBufferAccess2` turns into a zero load with no memory access, so the extra read cannot race with a write the guarded program would not have raced with ([Vulkan memory model](https://docs.vulkan.org/spec/latest/appendices/memorymodel.html#data-race)). Without `robustBufferAccess2` ([VulkanDevice.cpp](../../../core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp) enables it when the device has it) those loads keep their selections; LDS loads, stores, atomics, formatted and physical-address (BDA) accesses always do. LDS keeps the branch because NVIDIA's compiler predicates a branched LDS load at no cost, while the select form costs about 2.5 instructions per dword and splits a `ds_read_b128` into four loads (RTX 4060 measurements by @karanvora2599 in #4048); Intel's static counts favour the select only for wide reads, and run time was not measured on either. The aligned high word of a misaligned-capable buffer read re-reads the low word's own element. A selection guard costs four blocks per lane half and the host compiler's time grows with the block count: PPSA11142's merged PSP vertex program (3176 ISA words, wave64) emitted 16,983 blocks and 1,710 conditional branches, 5,907 and 868 with the selects. `agc_guarded_memory_loads` traces the emitted loads in both device modes. AMD's proprietary driver does not get `robustBufferAccess2` ([RobustBufferAccess.hpp](../../../core/libs/prx/libSceAgcDriver/Execution/include/RobustBufferAccess.hpp)), so its loads keep their selections. On an RX 6700 XT with driver 32.0.21045.5002, enabling the feature broke `agc_driver_layered_volume_draw` and `agc_driver_layered_volume_vertex`: the vertex draw left the volume untouched even with the selections kept. `agc_driver_subvector_loop` failed too, with the selected loads: the driver compiled the begin instruction's `exec_lo == 0` as a test of the whole 64-bit mask, so the high half of a wave64 skipped its loop. The begin instruction now tests for an empty mask as `(exec_lo | exec_hi) == 0` rather than `exec_lo == 0 && exec_hi == 0`, and that driver compiles it correctly. The cause of the layered-volume failures was not found. +- [Guest memory load guards](../../../core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvMemoryInstructions.cpp) (shader recompiler) - a guest load whose lane is inactive or whose address is out of bounds is still performed and its result replaced by 0 with `OpSelect`, instead of a selection per guard, when the load can be pointed at a location nothing writes: scratch reads its own element 0; storage buffers (direct buffers, GDS) read element 0xfffffffe, which `robustBufferAccess2` turns into a zero load with no memory access, so the extra read cannot race with a write the guarded program would not have raced with ([Vulkan memory model](https://docs.vulkan.org/spec/latest/appendices/memorymodel.html#data-race)). Without `robustBufferAccess2` ([VulkanDevice.cpp](../../../core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp) enables it when the device has it) those loads keep their selections; LDS loads, stores, atomics, formatted and physical-address (BDA) accesses always do. LDS keeps the branch because NVIDIA's compiler predicates a branched LDS load at no cost, while the select form costs about 2.5 instructions per dword and splits a `ds_read_b128` into four loads (RTX 4060 measurements by @karanvora2599 in #4048); Intel's static counts favour the select only for wide reads, and run time was not measured on either. The aligned high word of a misaligned-capable buffer read re-reads the low word's own element. A selection guard costs four blocks per lane half and the host compiler's time grows with the block count: PPSA11142's merged PSP vertex program (3176 ISA words, wave64) emitted 16,983 blocks and 1,710 conditional branches, 5,907 and 868 with the selects. `agc_guarded_memory_loads` traces the emitted loads in both device modes. - [Merged hull programs](../../../core/shader/recompiler/Optimization/src/TessellationLowering.cpp) (registered binary type 3) run whole in the hull stage, one invocation per output control point. On the console hull lane i keeps local lane i's registers, which with fewer input than output control points stop matching the patch's control points from i = inputs on; here invocation i holds control point min(i, inputs - 1) of its own patch. A wave-mask branch (`s_cbranch_exec[n]z`, `s_cbranch_vcc[n]z`) tests the invocation's own lane, so it can skip a block, scalar instructions included, that the console runs for the wave; `v_readfirstlane_b32` returns the invocation's own value, not the first active lane's, and another lane's register throws. The register layout (`s3`, `v0`-`v5`, as for the separate halves) is not measured on hardware. s[0:1] hold the hull user-data address (SH 0x102/0x103) when a draw sets it and zero otherwise, so a program reading it unset faults at the null address. - [Wave LDS ordering on AMD's proprietary driver](../../../core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp) (shader recompiler) - there ([SubgroupBarrier.hpp](../../../core/libs/prx/libSceAgcDriver/Execution/include/SubgroupBarrier.hpp)) one guest wave's LDS accesses are ordered by a subgroup-scope `OpMemoryBarrier` with no control barrier, relying on a 32-lane subgroup being one hardware wave whose lanes run in lockstep and issue LDS instructions in order, which Vulkan does not guarantee; for LDS kept in device memory the same is assumed of global loads and stores, unmeasured. Driver 32.0.21042.62 (Radeon 0x13c0, RDNA 2) compiled the subgroup-scope `OpControlBarrier` to `s_barrier`, which waves in a branch paired with other waves' barriers (PS5LM Plus). Measured on that driver only ([LdsTreeReduction.cpp](../../../core/libs/prx/libSceAgcDriver/tests/execution/LdsTreeReduction.cpp)); a Radeon 860M on 32.0.13058.8001 reportedly did not show the fault. From ee1bd9fbcbdd525c3c26b429fa8ede8b5ff8e2d9 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Wed, 21 Oct 2026 08:49:56 +0200 Subject: [PATCH 105/523] docs(debt): say that snwprintf_s does not invoke the exported constraint handler Co-Authored-By: Claude Sonnet 5.5 --- docs/dev/debt/libc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 6f00311858..d4f3f6fd18 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -37,7 +37,7 @@ - [Integer compiler-rt builtins](../../../core/libs/prx/libc/src/IntegerBuiltins.cpp) (libc) - the console traps in `__divsi3`, `__modsi3` and the 64- and 128-bit divisions on a zero divisor (`div`) and in `__negv*`, `__absv*` and `__mulv*` on overflow (an abort helper, then `ud2`); these throw instead. As in the `libc.prx` bundled with PPSA08427 (disassembly), `__addv*` and `__subv*` wrap without an overflow check, and `__udivsi3`, `__umodsi3` and `__udivmodsi4` divide in software, giving a quotient of 0 and the dividend as remainder for a zero divisor; `__clzdi2` and `__ctzdi2` of 0 return 32 and `__clzti2` and `__ctzti2` of 0 return 64 - [qsort](../../../core/libs/prx/libc/src/FileAndHeap.cpp) (libc) follows FreeBSD 11 [qsort.c](https://github.com/freebsd/freebsd-src/blob/5ed7eb0d97ba4436218e810f61bd059acba984c2/lib/libc/stdlib/qsort.c): Bentley and McIlroy's quicksort, with insertion sort below 7 elements and after a partition pass that swaps nothing, so elements that compare equal end in FreeBSD's order and the comparator is called in the same sequence. That the console's libc uses this FreeBSD code is unverified - [memcpy_s, memmove_s, strcpy_s, strncpy_s, strcat_s, strncat_s, wmemcpy_s, wmemmove_s, wcsncpy_s, wcscat_s, wcsncat_s](../../../core/libs/prx/libc/src/Strings.cpp) (libc) use the C11 Annex K destination size constraints, with RSIZE_MAX taken as (SIZE_MAX >> 1) / sizeof(char16_t) for wide operations and SIZE_MAX >> 1 for byte operations. An oversized destination size returns ERANGE (34) without touching the destination; the console's RSIZE_MAX and exact error value are unverified. Constraint violations return an error without invoking the registered constraint handler, as before. -- [snwprintf_s](../../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follows C11 K.3.9.1.3 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`. A runtime-constraint violation only returns a negative value: `set_constraint_handler_s` is not exported and the console's default handler is unknown. An invalid multibyte `%s` argument is copied byte by byte instead of being reported as an encoding error, as in `vswprintf` +- [snwprintf_s](../../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follows C11 K.3.9.1.3 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`. A runtime-constraint violation only returns a negative value: the handler installed with `set_constraint_handler_s` is not invoked, and the console's default handler is unknown. An invalid multibyte `%s` argument is copied byte by byte instead of being reported as an encoding error, as in `vswprintf` - [swprintf_s and vswprintf_s](../../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follow C11 K.3.9.1.4 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`: the complete output, including the terminating null, must fit in the buffer, otherwise it is a runtime-constraint violation that sets `buffer[0]` to zero and returns a negative value, and the buffer is not partially written. Argument and encoding checks match `snwprintf_s` - [strerror_s and strerrorlen_s](../../../core/libs/prx/libc/src/WideConversion.cpp) (libc) follow [C11 K.3.7.4.2 and K.3.7.4.3](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf), using the existing FreeBSD error messages and unknown-error formatting. Truncation writes an ellipsis when capacity exceeds 3; constraint failures leave the destination untouched and invoke the registered guest-ABI handler. The console's `RSIZE_MAX` (`SIZE_MAX >> 1` here), nonzero returns (`EINVAL` 22 for constraints, `ERANGE` 34 for truncation), handler arguments and default abort behavior are unverified. Messages remain in the C locale, and guest errno is preserved. - [getw and putw](../../../core/libs/prx/libc/src/BinaryWordIo.cpp) (libc) follow FreeBSD 11.4 [getw.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/getw.c) and [putw.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/putw.c): one 32-bit little-endian word, `getw` returns -1 for EOF or an error, and `putw` returns 0 on success or -1 on error while holding the stream lock. They use the existing host-backed streams, so buffering and error timing follow the host CRT. Null streams throw and closed streams return -1 with `EBADF`, as existing byte I/O does. Console behavior is unverified. From 44dfebb524c3397e4ad1019f0056e45323e5de70 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Wed, 21 Oct 2026 10:06:06 +0300 Subject: [PATCH 106/523] fix(libSceNpManager): take the request id and online id in sceNpSetGamePresenceOnline shadPS4 declares sceNpSetGamePresenceOnline(req_id, online_id, presence) and returns INVALID_ARGUMENT for request id 0 or a null online id. The export took (user id, timeout), so a caller's arguments landed in the wrong registers. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceNpManager/Export.cpp | 6 +++--- core/libs/tests/GuestNpManager.cpp | 6 ++++-- docs/dev/debt/libSceNpManager.md | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceNpManager/Export.cpp b/core/libs/prx/libSceNpManager/Export.cpp index bc722e640a..f0fe106801 100644 --- a/core/libs/prx/libSceNpManager/Export.cpp +++ b/core/libs/prx/libSceNpManager/Export.cpp @@ -166,9 +166,9 @@ int APS5_VABI sceNpGetAccountId(const NpOnlineId* online_id, uint64_t* account_i return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpSetGamePresenceOnline(int user_id, uint32_t timeout) { - (void)user_id; - (void)timeout; +int APS5_VABI sceNpSetGamePresenceOnline(int req_id, const NpOnlineId* online_id, uint32_t presence) { + (void)presence; + if (req_id == 0 || !online_id) return SCE_NP_ERROR_INVALID_ARGUMENT; return SCE_NP_ERROR_SIGNED_OUT; } diff --git a/core/libs/tests/GuestNpManager.cpp b/core/libs/tests/GuestNpManager.cpp index 655caac773..96b146845e 100644 --- a/core/libs/tests/GuestNpManager.cpp +++ b/core/libs/tests/GuestNpManager.cpp @@ -20,7 +20,7 @@ int APS5_VABI sceNpGetUserIdByOnlineId(const NpOnlineId* online_id, int* user_id int APS5_VABI sceNpCheckPlus(int req_id, const void* param, void* result); int APS5_VABI sceNpNotifyPlusFeature(const void* param); int APS5_VABI sceNpGetAccountId(const NpOnlineId* online_id, std::uint64_t* account_id); -int APS5_VABI sceNpSetGamePresenceOnline(int user_id, std::uint32_t timeout); +int APS5_VABI sceNpSetGamePresenceOnline(int req_id, const NpOnlineId* online_id, std::uint32_t presence); int APS5_VABI sceNpRegisterPlusEventCallback(void* callback, void* userdata); int APS5_VABI sceNpUnregisterPlusEventCallback(void); } @@ -245,7 +245,9 @@ void CheckOnlineIdQueries() { Require(sceNpGetAccountId(&onlineId, &accountId) == SignedOut, "sceNpGetAccountId must report NP as signed out"); Require(sceNpGetAccountId(nullptr, &accountId) == InvalidArgument, "sceNpGetAccountId must reject a null online id"); Require(sceNpGetAccountId(&onlineId, nullptr) == InvalidArgument, "sceNpGetAccountId must reject a null output"); - Require(sceNpSetGamePresenceOnline(0x10000000, 0) == SignedOut, "sceNpSetGamePresenceOnline must report NP as signed out"); + Require(sceNpSetGamePresenceOnline(1, &onlineId, 0) == SignedOut, "sceNpSetGamePresenceOnline must report NP as signed out"); + Require(sceNpSetGamePresenceOnline(0, &onlineId, 0) == InvalidArgument, "sceNpSetGamePresenceOnline must reject request id 0"); + Require(sceNpSetGamePresenceOnline(1, nullptr, 0) == InvalidArgument, "sceNpSetGamePresenceOnline must reject a null online id"); Require(sceNpRegisterPlusEventCallback(output, nullptr) == 0 && sceNpUnregisterPlusEventCallback() == 0, "the plus event callback must register and unregister"); Require(sceNpUnregisterPlusEventCallback() == 0, "unregistering the plus event callback again must succeed like the premium one"); Require(std::memcmp(output, untouched, sizeof(output)) == 0 && age == 0x5a && userId == 0x5a5a && accountId == 0x5a5a, "a failed query wrote its output"); diff --git a/docs/dev/debt/libSceNpManager.md b/docs/dev/debt/libSceNpManager.md index fcf80d6cb1..41f897369e 100644 --- a/docs/dev/debt/libSceNpManager.md +++ b/docs/dev/debt/libSceNpManager.md @@ -18,7 +18,7 @@ - [sceNpGetAccountCountry, sceNpGetAccountDateOfBirth, sceNpGetAccountLanguage, sceNpGetParentalControlInfo](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms taking an online id, not measured on a console. Signatures and checks follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp): a null pointer is `INVALID_ARGUMENT`; no user is signed in, so no online id matches one and the rest return `USER_NOT_FOUND` without writing, like `sceNpCheckNpAvailability`. Request ids are not checked - [sceNpGetUserIdByOnlineId](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(online id, user id output)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L703): a null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output, as `sceNpGetUserIdByAccountId` does - [sceNpCheckPlus](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(request id, parameter, result)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L246): request id 0 or a null parameter or result returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and every other call returns `SCE_NP_ERROR_SIGNED_OUT` without writing the result, as `sceNpCheckPremium` does. The parameter's size and feature fields, which shadPS4 checks, are not read: the errors the console returns for them are not known -- [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(user id, timeout)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp). A null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`; otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured +- [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(request id, online id, presence)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L1092-L1096). A null online id or output, or request id 0 for `sceNpSetGamePresenceOnline`, returns `SCE_NP_ERROR_INVALID_ARGUMENT`; otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured - [NP reachability callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable. - [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests - [NP state callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the registration rules are [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L857-L901)'s: `sceNpRegisterStateCallbackA` keeps up to 8 callbacks and returns the slot plus 1, a null callback returns `INVALID_ARGUMENT`, a callback already registered `CALLBACK_ALREADY_REGISTERED` and a ninth one `CALLBACK_MAX` (0x8055001D, [np_error.h](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_error.h#L38)); `sceNpUnregisterStateCallbackA` returns `INVALID_ARGUMENT` for an id outside 1..8 and `CALLBACK_NOT_REGISTERED` for an empty slot; the legacy [sceNpRegisterStateCallback](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L981-L1005) keeps one callback with the same errors. Each registration gets `SIGNED_OUT` for the initial user (0x10000000) once, from the next `sceNpCheckCallback` on the calling thread. For the A form this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3959-L3974), whose [sceNpRegisterStateCallbackA](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4021-L4034) marks the registration pending. KytyPS5 never calls the legacy callback ([sceNpRegisterStateCallback](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4009-L4019) stores nothing); it is called as `(user, SIGNED_OUT, nullptr, userdata)`, as in shadPS4, which passes an NpId only for `SIGNED_IN` ([L823](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L823), [L938-L940](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L938-L940)). shadPS4 instead queues `SIGNED_OUT` from the user service login event and delivers it from `sceNpCheckCallback` ([DispatchPendingNpStateCallbacks](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L903-L962)); the reachability callback is not given an initial `UNAVAILABLE`, unlike that function ([L922-L961](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L922-L961)). A callback being delivered when another thread unregisters it is still called once. When and how often the console calls the state callbacks, and the errors, are not measured From 74bd5d9fa5fccb79719a8bc8f18016a96e93d585 Mon Sep 17 00:00:00 2001 From: Sebastian <115291+slider@users.noreply.github.com> Date: Tue, 20 Oct 2026 12:10:06 +0000 Subject: [PATCH 107/523] feat(libkernel): add mutex ownership, relative lock and spin/yield accessors Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnkpF2oEMofseVz9Z7WEAC --- .../prx/libkernel/Pthread/Posix/Mutex.cpp | 27 +++- .../prx/libkernel/Pthread/include/Mutex.hpp | 5 + .../prx/libkernel/Pthread/include/Pthread.hpp | 9 ++ core/libs/prx/libkernel/Pthread/src/Mutex.cpp | 47 ++++++- core/libs/tests/GuestPthreadMutexErrors.cpp | 120 ++++++++++++++++++ docs/dev/debt/libkernel.md | 1 + 6 files changed, 206 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp b/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp index 3a5d38dc3f..346b447bb8 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp @@ -29,7 +29,7 @@ void _initializeStatic(PthreadMutex* mutex, const char* funcName) { PthreadMutex current = slot.load(std::memory_order_acquire); if (!_isStaticInitializer(current)) return; auto* created = new PthreadMutexPrivate(); - created->_type = reinterpret_cast(current) == POSIX_ADAPTIVE_MUTEX_INITIALIZER ? MutexType::Adaptive : MutexType::ErrorCheck; + created->SetType(reinterpret_cast(current) == POSIX_ADAPTIVE_MUTEX_INITIALIZER ? MutexType::Adaptive : MutexType::ErrorCheck); if (!slot.compare_exchange_strong(current, created, std::memory_order_acq_rel, std::memory_order_acquire)) delete created; } @@ -79,6 +79,31 @@ int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex) { return PosixThread::ToErrno(scePthreadMutexUnlock(mutex)); } +int APS5_VABI pthread_mutex_reltimedlock_np_nid_postfix(PthreadMutex* mutex, KernelUseconds usec) { + _initializeStatic(mutex, __func__); + return PosixThread::ToErrno(scePthreadMutexTimedlock(mutex, usec)); +} + +int APS5_VABI pthread_mutex_isowned_np_nid_postfix(PthreadMutex* mutex) { + return MutexOperations::IsOwned(mutex) ? 1 : 0; +} + +int APS5_VABI pthread_mutex_getspinloops_np_nid_postfix(PthreadMutex* mutex, int* count) { + return PosixThread::ToErrno(scePthreadMutexGetspinloops(mutex, count)); +} + +int APS5_VABI pthread_mutex_setspinloops_np_nid_postfix(PthreadMutex* mutex, int count) { + return PosixThread::ToErrno(scePthreadMutexSetspinloops(mutex, count)); +} + +int APS5_VABI pthread_mutex_getyieldloops_np_nid_postfix(PthreadMutex* mutex, int* count) { + return PosixThread::ToErrno(scePthreadMutexGetyieldloops(mutex, count)); +} + +int APS5_VABI pthread_mutex_setyieldloops_np_nid_postfix(PthreadMutex* mutex, int count) { + return PosixThread::ToErrno(scePthreadMutexSetyieldloops(mutex, count)); +} + int APS5_VABI pthread_mutexattr_destroy_nid_postfix(PthreadMutexattr* attr) { return PosixThread::ToErrno(scePthreadMutexattrDestroy(attr)); } diff --git a/core/libs/prx/libkernel/Pthread/include/Mutex.hpp b/core/libs/prx/libkernel/Pthread/include/Mutex.hpp index ca734c603d..a994a1b5c3 100644 --- a/core/libs/prx/libkernel/Pthread/include/Mutex.hpp +++ b/core/libs/prx/libkernel/Pthread/include/Mutex.hpp @@ -6,6 +6,7 @@ class MutexOperations { public: static int Timedlock(PthreadMutex* mutex, const KernelTimespec* abstime); + static bool IsOwned(const PthreadMutex* mutex); }; extern "C" { @@ -20,6 +21,10 @@ int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex); int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex); int APS5_VABI scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec); int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex); +int APS5_VABI scePthreadMutexGetspinloops(PthreadMutex* mutex, int* count); +int APS5_VABI scePthreadMutexSetspinloops(PthreadMutex* mutex, int count); +int APS5_VABI scePthreadMutexGetyieldloops(PthreadMutex* mutex, int* count); +int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count); } diff --git a/core/libs/prx/libkernel/Pthread/include/Pthread.hpp b/core/libs/prx/libkernel/Pthread/include/Pthread.hpp index 03024d9d7f..0cef7ba498 100644 --- a/core/libs/prx/libkernel/Pthread/include/Pthread.hpp +++ b/core/libs/prx/libkernel/Pthread/include/Pthread.hpp @@ -37,14 +37,23 @@ struct PthreadMutexattrPrivate { int pshared = 0; }; +constexpr int ADAPTIVE_MUTEX_SPINS = 2000; + struct PthreadMutexPrivate { std::recursive_timed_mutex _rmtx; std::timed_mutex _mtx; MutexType _type; std::atomic _owner; int _count; + int _spinloops = 0; + int _yieldloops = 0; PthreadMutexPrivate() : _type(MutexType::ErrorCheck), _owner(std::thread::id{}), _count(0) {} + + void SetType(MutexType type) { + _type = type; + _spinloops = type == MutexType::Adaptive ? ADAPTIVE_MUTEX_SPINS : 0; + } }; struct PthreadRwlockattrPrivate { diff --git a/core/libs/prx/libkernel/Pthread/src/Mutex.cpp b/core/libs/prx/libkernel/Pthread/src/Mutex.cpp index 876bd9ee20..dccc979442 100644 --- a/core/libs/prx/libkernel/Pthread/src/Mutex.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Mutex.cpp @@ -46,7 +46,7 @@ PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) { throw std::runtime_error("Mutex is not initialized"); auto* created = new PthreadMutexPrivate(); if (current == adaptiveInitializer()) - created->_type = MutexType::Adaptive; + created->SetType(MutexType::Adaptive); slot.store(created, std::memory_order_release); return created; } @@ -80,6 +80,15 @@ int acquireMutex(PthreadMutex mutex, TAcquire acquire, int unavailable, bool try } +bool MutexOperations::IsOwned(const PthreadMutex* mutex) { + if (!mutex) + throw std::invalid_argument("Mutex pointer is null"); + const auto current = std::atomic_ref(*const_cast(mutex)).load(std::memory_order_acquire); + if (!isInitializedMutex(current)) + return false; + return current->_owner.load(std::memory_order_acquire) == std::this_thread::get_id(); +} + int MutexOperations::Timedlock(PthreadMutex* mutex, const KernelTimespec* abstime) { if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) throw std::invalid_argument("Invalid absolute mutex timeout"); @@ -152,7 +161,7 @@ int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* a throw std::invalid_argument("Mutex pointer is null"); auto replacement = std::make_unique(); if (attr && *attr) - replacement->_type = (*attr)->type; + replacement->SetType((*attr)->type); std::lock_guard lock(initializationMutex); std::atomic_ref(*mutex).store(replacement.release(), std::memory_order_release); return 0; @@ -224,4 +233,38 @@ int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex) { return acquireMutex(resolveMutex(mutex, true), [](auto& native) { return native.try_lock(); }, sceBusy, true); } +int APS5_VABI scePthreadMutexGetspinloops(PthreadMutex* mutex, int* count) { + auto* current = resolveMutex(mutex, true); + if (!current) + return SCE_KERNEL_ERROR_EINVAL; + if (!count) + throw std::invalid_argument("Spin loop count pointer is null"); + *count = current->_spinloops; + return 0; +} + +int APS5_VABI scePthreadMutexSetspinloops(PthreadMutex* mutex, int count) { + auto* current = resolveMutex(mutex, true); + if (!current) + return SCE_KERNEL_ERROR_EINVAL; + current->_spinloops = count; + return 0; +} + +int APS5_VABI scePthreadMutexGetyieldloops(PthreadMutex* mutex, int* count) { + auto* current = resolveMutex(mutex, true); + if (!current) + return SCE_KERNEL_ERROR_EINVAL; + if (!count) + throw std::invalid_argument("Yield loop count pointer is null"); + *count = current->_yieldloops; + return 0; +} + +int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count) { + if (auto* current = resolveMutex(mutex, true)) + current->_yieldloops = count; + return 0; +} + } diff --git a/core/libs/tests/GuestPthreadMutexErrors.cpp b/core/libs/tests/GuestPthreadMutexErrors.cpp index 064469d42d..da1083b121 100644 --- a/core/libs/tests/GuestPthreadMutexErrors.cpp +++ b/core/libs/tests/GuestPthreadMutexErrors.cpp @@ -2,6 +2,7 @@ #include #include #include +#include extern "C" { int APS5_VABI scePthreadMutexattrInit(PthreadMutexattr* attr); @@ -12,14 +13,131 @@ int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* a int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex); int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex); int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex); +int APS5_VABI scePthreadMutexGetspinloops(PthreadMutex* mutex, int* count); +int APS5_VABI scePthreadMutexSetspinloops(PthreadMutex* mutex, int count); +int APS5_VABI scePthreadMutexGetyieldloops(PthreadMutex* mutex, int* count); +int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count); +int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name); +int APS5_VABI scePthreadJoin(Pthread thread, void** retval); +int APS5_VABI pthread_mutex_lock_nid_postfix(PthreadMutex* mutex); +int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex); +int APS5_VABI pthread_mutex_destroy_nid_postfix(PthreadMutex* mutex); +int APS5_VABI pthread_mutex_reltimedlock_np_nid_postfix(PthreadMutex* mutex, KernelUseconds usec); +int APS5_VABI pthread_mutex_isowned_np_nid_postfix(PthreadMutex* mutex); +int APS5_VABI pthread_mutex_getspinloops_np_nid_postfix(PthreadMutex* mutex, int* count); +int APS5_VABI pthread_mutex_setspinloops_np_nid_postfix(PthreadMutex* mutex, int count); +int APS5_VABI pthread_mutex_getyieldloops_np_nid_postfix(PthreadMutex* mutex, int* count); +int APS5_VABI pthread_mutex_setyieldloops_np_nid_postfix(PthreadMutex* mutex, int count); } static constexpr int SCE_OK = 0; static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast(0x80020016u); static constexpr int SCE_KERNEL_ERROR_EBUSY = static_cast(0x80020010u); +static constexpr int POSIX_EDEADLK = 11; +static constexpr int POSIX_EINVAL = 22; +static constexpr int POSIX_ETIMEDOUT = 60; +static const PthreadMutex AdaptiveInitializer = reinterpret_cast(std::uintptr_t{1}); +static const PthreadMutex DestroyedMutex = reinterpret_cast(std::uintptr_t{2}); + static void Require(bool value) { if (!value) std::abort(); } +template +static bool Throws(Call call) { + try { + call(); + } catch (const std::exception&) { + return true; + } + return false; +} + +struct OtherThread { + PthreadMutex* mutex; + int isowned = -1; + int reltimedlock = -1; +}; + +static void* APS5_VABI CheckFromOtherThread(void* arg) { + auto& other = *static_cast(arg); + other.isowned = pthread_mutex_isowned_np_nid_postfix(other.mutex); + other.reltimedlock = pthread_mutex_reltimedlock_np_nid_postfix(other.mutex, 1000); + return nullptr; +} + +static void CheckOwnershipAndRelativeLock() { + PthreadMutex mutex = nullptr; + Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 0 && mutex == nullptr); + PthreadMutex adaptive = AdaptiveInitializer; + Require(pthread_mutex_isowned_np_nid_postfix(&adaptive) == 0 && adaptive == AdaptiveInitializer); + Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 1000) == 0 && mutex != nullptr); + Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 1); + Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 1000) == POSIX_EDEADLK); + OtherThread other{&mutex}; + Pthread thread = nullptr; + Require(scePthreadCreate(&thread, nullptr, CheckFromOtherThread, &other, nullptr) == SCE_OK); + Require(scePthreadJoin(thread, nullptr) == SCE_OK); + Require(other.isowned == 0 && other.reltimedlock == POSIX_ETIMEDOUT); + Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 0); + Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 0) == 0); + Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0); + Require(mutex == DestroyedMutex); + Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 0); + Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 1000) == POSIX_EINVAL); + Require(Throws([] { pthread_mutex_isowned_np_nid_postfix(nullptr); })); +} + +static void CheckLoops(PthreadMutexattr* attr) { + PthreadMutex mutex = nullptr; + int count = -1; + Require(pthread_mutex_getspinloops_np_nid_postfix(&mutex, &count) == 0 && count == 0 && mutex != nullptr); + Require(pthread_mutex_getyieldloops_np_nid_postfix(&mutex, &count) == 0 && count == 0); + Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0); + + PthreadMutex adaptive = AdaptiveInitializer; + Require(scePthreadMutexGetspinloops(&adaptive, &count) == SCE_OK && count == 2000 && adaptive != AdaptiveInitializer); + Require(scePthreadMutexGetyieldloops(&adaptive, &count) == SCE_OK && count == 0); + Require(scePthreadMutexDestroy(&adaptive) == SCE_OK); + + Require(scePthreadMutexattrSettype(attr, 4) == SCE_OK); + Require(scePthreadMutexInit(&mutex, attr, "adaptive") == SCE_OK); + Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_OK && count == 2000); + Require(scePthreadMutexSetspinloops(&mutex, 7) == SCE_OK); + Require(pthread_mutex_getspinloops_np_nid_postfix(&mutex, &count) == 0 && count == 7); + Require(pthread_mutex_setspinloops_np_nid_postfix(&mutex, -3) == 0); + Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_OK && count == -3); + Require(scePthreadMutexSetyieldloops(&mutex, 9) == SCE_OK); + Require(pthread_mutex_getyieldloops_np_nid_postfix(&mutex, &count) == 0 && count == 9); + Require(pthread_mutex_setyieldloops_np_nid_postfix(&mutex, 11) == 0); + Require(scePthreadMutexGetyieldloops(&mutex, &count) == SCE_OK && count == 11); + Require(pthread_mutex_lock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_lock_nid_postfix(&mutex) == POSIX_EDEADLK); + Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0); + Require(Throws([&] { scePthreadMutexGetspinloops(&mutex, nullptr); })); + Require(Throws([&] { scePthreadMutexGetyieldloops(&mutex, nullptr); })); + Require(scePthreadMutexDestroy(&mutex) == SCE_OK); + + count = -1; + Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_KERNEL_ERROR_EINVAL && count == -1); + Require(scePthreadMutexSetspinloops(&mutex, 1) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexGetyieldloops(&mutex, &count) == SCE_KERNEL_ERROR_EINVAL && count == -1); + Require(pthread_mutex_getspinloops_np_nid_postfix(&mutex, &count) == POSIX_EINVAL); + Require(pthread_mutex_setspinloops_np_nid_postfix(&mutex, 1) == POSIX_EINVAL); + Require(pthread_mutex_getyieldloops_np_nid_postfix(&mutex, &count) == POSIX_EINVAL && count == -1); + Require(scePthreadMutexSetyieldloops(&mutex, 1) == SCE_OK); + Require(pthread_mutex_setyieldloops_np_nid_postfix(&mutex, 1) == 0); + Require(mutex == DestroyedMutex); + + for (const int type : {1, 2, 3}) { + Require(scePthreadMutexattrSettype(attr, type) == SCE_OK); + Require(scePthreadMutexInit(&mutex, attr, nullptr) == SCE_OK); + Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_OK && count == 0); + Require(scePthreadMutexDestroy(&mutex) == SCE_OK); + } +} + int main() { PthreadMutexattr attr = nullptr; Require(scePthreadMutexattrInit(&attr) == SCE_OK); @@ -46,5 +164,7 @@ int main() { Require(scePthreadMutexInit(&mutex, &attr, "attr") == SCE_OK); Require(scePthreadMutexDestroy(&mutex) == SCE_OK); + CheckLoops(&attr); + CheckOwnershipAndRelativeLock(); Require(scePthreadMutexattrDestroy(&attr) == SCE_OK); } diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 6384b00c9f..9db863874c 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -219,6 +219,7 @@ - [inet_ntop, inet_pton](../../../core/libs/prx/libkernel/Socket/src/AddressConversion.cpp) (libkernel) - an `AF_INET6` result of `inet_ntop` is followed by zero bytes up to `size`. In a payload process on a PS5 (firmware 12.70), the text forms, the order of the checks and this padding were measured through both exports. The padding was sampled at sizes from 3 to 65536, above 64 only at 65, 128, 256, 1024, 4096 and 65536; the sizes that were not sampled, sizes above 65536 and a title process are assumed to behave the same - [_fstatfs](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) fills the FreeBSD 11 `struct statfs` of the PS5 payload SDK with the host volume that holds the file: block size, transfer size, total, free and available blocks, maximum name length and the read-only, `nosuid` and `noexec` flags (on Windows only read-only, and `f_files` and `f_ffree` are 0, as NTFS has no fixed number of files). The filesystem type number and name, mount paths, owner, filesystem id and I/O counters are left 0 or empty, since they would describe a host mount, not a console one. Sockets and pipes return `EINVAL`, as FreeBSD's `getvnode` does; a Windows descriptor that is neither a file nor a pipe throws. Stremio Plus (PPSA74126) reads the free space before a download and only logs the other fields - [pthread_spin_lock](../../../core/libs/prx/libkernel/Pthread/Posix/Spinlock.cpp) (libkernel) - a lock taken again by the thread that holds it throws here. On a PS5 (firmware 12.70) that call had not returned after 2 s, as FreeBSD 11.4 libthr [`thr_pspinlock.c`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_pspinlock.c) spins forever +- [pthread_mutex_getspinloops_np, pthread_mutex_setspinloops_np, pthread_mutex_getyieldloops_np, pthread_mutex_setyieldloops_np and their scePthreadMutex counterparts](../../../core/libs/prx/libkernel/Pthread/src/Mutex.cpp) (libkernel) store and report the counts, but host mutexes do not spin or yield by them. The defaults follow FreeBSD 11.4's [`mutex_init_body`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_mutex.c#L236-L272) without `LIBPTHREAD_SPINLOOPS` and `LIBPTHREAD_YIELDLOOPS`: 2000 spins for an adaptive mutex, 0 otherwise; the console's defaults are unverified. Setting the yield count of a destroyed mutex returns 0 as FreeBSD's [`__pthread_mutex_setyieldloops_np`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_mutex.c#L1125-L1135) does; the console's result is unverified - [pthread_rwlockattr_getpshared, pthread_rwlockattr_setpshared](../../../core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp) (libkernel) - `pthread_rwlockattr_destroy` clears the attribute here, so a later get, or a set of 0, throws. On a PS5 (firmware 12.70) destroy leaves the attribute set; a get afterwards returned 0 with a stale value and a set of 0 returned 0 - [sceKernelGetEventError](../../../core/libs/prx/libkernel/Equeue/src/EventAccessors.cpp) (libkernel) returns the event's `data` as an `int` when `EV_ERROR` (0x4000) is set in `flags` and 0 otherwise, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/947627d10d4ef4b68ba3c0189aab6c368e70a8d2/src/kernel/eventQueue.cpp#L663) does and as FreeBSD 11's [kevent](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/kern_event.c#L1005) reports a failed change (`flags = EV_ERROR`, errno in `data`). [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_queue.pas#L643) instead returns `fflags` plus `0x80020000` when `fflags` is nonzero. Whether the console reads `data` or `fflags` and returns the raw errno or an SCE error code is unverified. No event delivered by the equeue here carries `EV_ERROR`, so the call returns 0 for every event `sceKernelWaitEqueue` reports - [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console From 31fc06b120e9f26902251d5dcb6042cddf1848ab Mon Sep 17 00:00:00 2001 From: Sebastian <115291+slider@users.noreply.github.com> Date: Wed, 21 Oct 2026 08:00:28 +0000 Subject: [PATCH 108/523] docs(debt): record the pthread_mutex_reltimedlock_np timeout width Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnkpF2oEMofseVz9Z7WEAC --- docs/dev/debt/libkernel.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 9db863874c..35aee71692 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -223,6 +223,7 @@ - [pthread_rwlockattr_getpshared, pthread_rwlockattr_setpshared](../../../core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp) (libkernel) - `pthread_rwlockattr_destroy` clears the attribute here, so a later get, or a set of 0, throws. On a PS5 (firmware 12.70) destroy leaves the attribute set; a get afterwards returned 0 with a stale value and a set of 0 returned 0 - [sceKernelGetEventError](../../../core/libs/prx/libkernel/Equeue/src/EventAccessors.cpp) (libkernel) returns the event's `data` as an `int` when `EV_ERROR` (0x4000) is set in `flags` and 0 otherwise, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/947627d10d4ef4b68ba3c0189aab6c368e70a8d2/src/kernel/eventQueue.cpp#L663) does and as FreeBSD 11's [kevent](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/sys/kern/kern_event.c#L1005) reports a failed change (`flags = EV_ERROR`, errno in `data`). [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_queue.pas#L643) instead returns `fflags` plus `0x80020000` when `fflags` is nonzero. Whether the console reads `data` or `fflags` and returns the raw errno or an SCE error code is unverified. No event delivered by the equeue here carries `EV_ERROR`, so the call returns 0 for every event `sceKernelWaitEqueue` reports - [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console +- [pthread_mutex_reltimedlock_np](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) takes `(mutex, usec)`, a relative timeout in microseconds, and is `scePthreadMutexTimedlock` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadMutexTimedlock` ([`mutex.cpp`](https://github.com/shadps4-emu/shadPS4/blob/7c960eae136b956cf4c1d72aacd2426b650330df/src/core/libraries/kernel/threads/mutex.cpp#L284-L287)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `pthread_cond_reltimedwait_np`, so a count of 2^32 microseconds or more would be truncated if the console reads 64 bits. Unverified on the console - [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation. - [pthread_atfork](../../../core/libs/prx/libkernel/Pthread/Posix/Atfork.cpp) (libkernel) appends the three handlers to a process-wide list and returns 0, or `ENOMEM` when the entry cannot be allocated, as FreeBSD 11.4's [`_pthread_atfork`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_fork.c#L78-L100) does; null handlers are accepted. The handlers never run: `fork` throws and `vfork` is absent, so no child process is created. Handlers of a module that is unloaded stay in the list, where FreeBSD's `__pthread_cxa_finalize` removes them. PPSA97358 has a source-level call from the constructor `rcu_init` ([rcu.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/util/rcu.c#L488-L495)), but current-main runtime reachability is unverified; XPSemu currently stops first at the unresolved `mlockall` import (#4386) - [sceKernelGetPageTableStats](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) reports 8192 entries on each side and counts them as a PS5 (firmware 13.60) counted them for a payload process: one cpu entry for each 2 MiB granule that holds a committed mapping, whatever its protection (none included), and one gpu entry for each 1 GiB region that holds a GPU-accessible mapping, plus one for each 2 MiB granule of such a mapping that is not a whole granule of direct memory at a 2 MiB-aligned physical address. A granule or region shared by several mappings counts once, and reservations do not count. On the console the first GPU mapping of a process also takes three gpu entries and one cpu entry that stay after it is unmapped, which is not modelled, and only the ranges whose protection is recorded here are counted. A null pointer returns `SCE_KERNEL_ERROR_EFAULT`, where the console faults. Resonance: A Plague Tale Legacy (PPSA30601) calls it at start-up (#4067) From 191c8a8d2b9d1f104d9f7203e752775f88cd7cea Mon Sep 17 00:00:00 2001 From: codingreapermaps <233954859+codingreapermaps@users.noreply.github.com> Date: Tue, 13 Oct 2026 22:24:05 +0100 Subject: [PATCH 109/523] feat(agc): run DB depth copies into a single-sample R32 float target A draw with DB_RENDER_CONTROL.DEPTH_COPY copies one sample of the Z_32_FLOAT depth surface into color target 0. It now runs as a compute pass that fetches that sample and stores it into the R32 float target, when the draw covers the whole surface without depth or stencil tests. With COPY_CENTROID the sample is the first one the draw covers, which is sample 0 under the full sample mask the driver requires, as radeonsi programs its per-sample DB to CB copies. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 14 ++++ .../Execution/include/VulkanDevice.hpp | 1 + .../src/Driver/Draw/DrawPrecheck.cpp | 10 +++ .../Execution/src/VulkanDevice.cpp | 4 + .../Graphics/include/DepthSurface.hpp | 1 + .../libSceAgcDriver/Graphics/include/Draw.hpp | 1 + .../Graphics/include/State.hpp | 8 ++ .../Graphics/include/TextureDetiler.hpp | 6 ++ .../Graphics/shaders/DepthSample.comp | 18 +++++ .../Graphics/src/DepthSurface.cpp | 37 +++++++++ .../prx/libSceAgcDriver/Graphics/src/Draw.cpp | 6 ++ .../libSceAgcDriver/Graphics/src/State.cpp | 68 ++++++++++++----- .../Graphics/src/TextureDetiler.cpp | 76 +++++++++++++++++++ .../prx/libSceAgcDriver/tests/Graphics.cpp | 44 +++++++++++ .../tests/execution/DepthStorage.cpp | 56 ++++++++++++++ docs/dev/debt/libSceAgcDriver.md | 1 + 16 files changed, 333 insertions(+), 18 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index f6f2e95e22..e784a11285 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -130,6 +130,19 @@ add_custom_command( VERBATIM ) +set(agcDepthSampleShaderSource ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/shaders/DepthSample.comp) +set(agcDepthSampleShaderSpv ${agcTextureDetileGeneratedDir}/DepthSample.spv) +set(agcDepthSampleShaderHeader ${agcTextureDetileGeneratedDir}/DepthSample_spv.h) +add_custom_command( + OUTPUT "${agcDepthSampleShaderHeader}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTextureDetileGeneratedDir}" + COMMAND "$" -V --target-env vulkan1.1 -o "${agcDepthSampleShaderSpv}" "${agcDepthSampleShaderSource}" + COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthSampleShaderSpv} -DOUTPUT=${agcDepthSampleShaderHeader} + -DSYMBOL=DEPTH_SAMPLE_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake" + DEPENDS "${agcDepthSampleShaderSource}" glslang-standalone + VERBATIM +) + set(agcSampleCounterShaderSource ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/shaders/SampleCounter.comp) set(agcSampleCounterShaderSpv ${agcTextureDetileGeneratedDir}/SampleCounter.spv) set(agcSampleCounterShaderHeader ${agcTextureDetileGeneratedDir}/SampleCounter_spv.h) @@ -303,6 +316,7 @@ set(agcDriverSources ${agcTextureDetileShaderHeader} ${agcReadbackHeaders} ${agcCmaskClearShaderHeader} + ${agcDepthSampleShaderHeader} ${agcMeshArgumentsShaderHeader} ${agcTextureDetileImageShaderHeader} ${agcTextureRepackShaderHeader} diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp index 5012ff4ab4..ecbff29189 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp +++ b/core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp @@ -321,6 +321,7 @@ public: bool DrawInputsInPlace() const; std::optional KnownDrawRejection(const Graphics::State& graphics, std::span shaders) const; void ColorMetadataPass(const Graphics::ColorMetadataPass& pass); + void DepthCopyPass(const Graphics::DepthCopyPass& pass); // A draw-cache hit recorded from its recipe (Graphics::DrawWithRecipe), under the mutex after // the packet's labels; Rebuild when the recipe's device is not this one or DrawWithRecipe // missed (nothing recorded: the caller runs Draw with the hit's stages and re-attaches). diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp index a6787296d4..157789875f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp @@ -25,6 +25,16 @@ std::optional Driver::precheckDraw(const QueueState& queue, const S localDevice->ColorMetadataPass(*pass); return DrawVerdict::Drawn; } + if (const auto copy = Graphics::DecodeDepthCopyPass(queue)) { + require(!drawParameters.indirect, "indirect DB depth copies are unsupported"); + GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Draw); + std::lock_guard gpuLock(GuestMemory::GpuMutex()); + if (device == nullptr) device = std::make_shared(); + const std::shared_ptr localDevice = device; + recordLabelsForPacket(localDevice.get(), submission.queue); + localDevice->DepthCopyPass(*copy); + return DrawVerdict::Drawn; + } if (Graphics::DepthResummarizeDrawsNothing(queue)) return DrawVerdict::Nothing; rejected = Graphics::DepthMaintenanceRejection(queue); if (!rejected.empty()) return DrawVerdict::Rejected; diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 226c792301..218d58b366 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -3828,6 +3828,10 @@ std::optional VulkanDevice::KnownDrawRejection(const Graphics::Stat return Graphics::KnownValidationFailure(graphicsContext(), shaders, graphics); } +void VulkanDevice::DepthCopyPass(const Graphics::DepthCopyPass& pass) { + Graphics::RunDepthCopyPass(graphicsContext(), pass); +} + void VulkanDevice::ColorMetadataPass(const Graphics::ColorMetadataPass& pass) { Graphics::RunColorMetadataPass(graphicsContext(), pass); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp index 726701cb8c..9dffa61679 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp @@ -62,6 +62,7 @@ VkFormat HostDepthFormat(const Context& context, VkFormat format); float HostDepthBiasConstant(const Context& context, VkFormat format, float constant); std::shared_ptr DepthSurfaceStorage(const Context& context, std::span words, const GuestTextureResource& resource, std::uint32_t mip); bool DepthSurfaceStoreBack(const Context& context, const StorageTexture& storage); +void CopyDepthSample(const Context& context, std::uint64_t depthAddress, std::uint32_t sample, StorageTexture& destination, VkImageView destinationView, VkExtent2D extent); struct StorageImageClear { std::array image{}; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Draw.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Draw.hpp index 8e7623ea87..b79528bf72 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Draw.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Draw.hpp @@ -127,6 +127,7 @@ void CountDrawSkip(DrawSkip kind, double us); std::shared_ptr>> DrawCopiedWriters(); void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass); +void RunDepthCopyPass(const Context& context, const DepthCopyPass& pass); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp index 22eb13371f..649ce487ea 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp @@ -192,6 +192,14 @@ struct ColorMetadataPass { std::optional source; }; std::optional DecodeColorMetadataPass(const QueueState& queue); +struct DepthCopyPass { + std::uint64_t depthAddress; + VkExtent2D extent; + std::uint32_t samples; + std::uint32_t sample; + ColorTarget color; +}; +std::optional DecodeDepthCopyPass(const QueueState& queue); std::string DepthMaintenanceRejection(const QueueState& queue); bool DepthResummarizeDrawsNothing(const QueueState& queue); // The message DecodeState (or the pixel stage decode after it) would throw for the register rules diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp index cc70a270e8..82a9c77e77 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp @@ -56,6 +56,7 @@ namespace AgcDriver::Graphics { void DispatchCmaskClear(VkCommandBuffer commands, VkBuffer cmask, std::uint64_t cmaskOffset, std::size_t cmaskBytes, VkImageView view, std::uint32_t width, std::uint32_t height, std::uint32_t elementBytes, const std::array& clearWords, bool write); std::uint32_t CmaskErrors(); void DispatchMultisampleReadback(VkCommandBuffer commands, VkFormat format, VkBuffer destination, std::uint64_t offset, std::uint64_t bytes, VkImageView arrayView, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t fragments, std::uint32_t firstElement, std::uint32_t elements); + void DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample); private: VkPipeline multisampleReadbackPipeline(std::uint32_t elementBytes, bool depth); @@ -82,6 +83,11 @@ namespace AgcDriver::Graphics { VkPipelineLayout cmaskPipelineLayout = VK_NULL_HANDLE; VkPipeline cmaskPipeline = VK_NULL_HANDLE; DescriptorRing cmaskDescriptorSets; + VkDescriptorSetLayout depthSampleLayout = VK_NULL_HANDLE; + VkPipelineLayout depthSamplePipelineLayout = VK_NULL_HANDLE; + VkPipeline depthSamplePipeline = VK_NULL_HANDLE; + VkSampler depthSampler = VK_NULL_HANDLE; + DescriptorRing depthSampleSets; std::unique_ptr cmaskErrors; VkDescriptorSetLayout imageDescriptorLayout = VK_NULL_HANDLE; VkPipelineLayout imagePipelineLayout = VK_NULL_HANDLE; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp b/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp new file mode 100644 index 0000000000..fa83092614 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp @@ -0,0 +1,18 @@ +#version 450 + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(set = 0, binding = 0) uniform sampler2DMS depth; +layout(set = 0, binding = 1, r32f) writeonly uniform image2D target; + +layout(push_constant) uniform Push { + uint width; + uint height; + int sampleIndex; +} push; + +void main() { + uvec2 pixel = gl_GlobalInvocationID.xy; + if (pixel.x >= push.width || pixel.y >= push.height) return; + imageStore(target, ivec2(pixel), vec4(texelFetch(depth, ivec2(pixel), push.sampleIndex).r, 0.0, 0.0, 0.0)); +} diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp index 7df4340010..6be0b9cdb9 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp @@ -491,6 +491,31 @@ public: else Recorder::CountBarriers(Recorder::CommandClass::Draw, 2); } + void CopySample(std::uint32_t sample, StorageTexture& destination, VkImageView destinationView) { + auto* recorder = Recorder::Active(); + Require(recorder != nullptr, "depth sample copies need an active recorder"); + Require(sampleable, "DB depth copy from a depth surface the device cannot sample at its sample count"); + Require(target.samples == 1 || samplingActive, "native multisample depth sampling requires its creation-time owner"); + ApplyFastClear(); + if (useSampleShadow) { + if (sampleShadow == nullptr) createSampleShadow(); + refreshSampleShadow(false); + recorder->Keep(sampleShadow); + } + if (depthView == VK_NULL_HANDLE) { + VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + viewInfo.image = useSampleShadow ? sampleShadow->image : image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = hostFormat; + viewInfo.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1}; + Check(context.Function("vkCreateImageView")(context.device, &viewInfo, nullptr, &depthView), "vkCreateImageView depth sample copy"); + } + Require(context.detiler != nullptr, "depth sample copies need the texture detiler"); + context.detiler->DispatchDepthSample(recorder->Commands(), depthView, destinationView, target.extent.width, target.extent.height, sample); + Recorder::CountBarriers(Recorder::CommandClass::Copy, 2); + destination.MarkDirty(); + } + bool Holds(const Texture* texture) const { const auto held = [&](const auto& entry) { return entry.second.get() == texture; }; return std::any_of(textures.begin(), textures.end(), held) || std::any_of(stencilCopy.textures.begin(), stencilCopy.textures.end(), held); @@ -740,6 +765,7 @@ public: const VkFormat hostFormat; VkImage image = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; + VkImageView depthView = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; std::shared_ptr> active = std::make_shared>(true); bool sampleable = true; @@ -978,6 +1004,7 @@ private: releaseStencilCopy(); mirrorPlane = {}; textures.clear(); + if (depthView) context.Function("vkDestroyImageView")(context.device, depthView, nullptr); if (view) context.Function("vkDestroyImageView")(context.device, view, nullptr); if (owned != nullptr) owned.reset(); else { @@ -2178,6 +2205,16 @@ std::shared_ptr DepthSurfaceStorage(const Context& context, std: return layeredDepthStorage(context, surface, resource, mip); } +void CopyDepthSample(const Context& context, std::uint64_t depthAddress, std::uint32_t sample, StorageTexture& destination, VkImageView destinationView, VkExtent2D extent) { + std::lock_guard lock(surfacesMutex()); + const auto& list = surfaces(); + const auto found = std::find_if(list.rbegin(), list.rend(), [&](const auto& surface) { return surface->context.device == context.device && surface->target.address == depthAddress; }); + Require(found != list.rend(), "DB depth copy from a depth plane no draw rendered"); + Require((*found)->target.extent.width == extent.width && (*found)->target.extent.height == extent.height, "DB depth copy from a depth surface of another size"); + Require(sample < (*found)->target.samples, "DB depth copy of a sample the depth surface does not have"); + (*found)->CopySample(sample, destination, destinationView); +} + bool DepthSurfaceStoreBack(const Context& context, const StorageTexture& storage) { std::lock_guard lock(surfacesMutex()); for (auto& [key, entry] : layeredStorages()) { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index 63125623ca..80f54edd07 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -2918,6 +2918,12 @@ std::shared_ptr metadataPassResident(const Context& context, con } +void RunDepthCopyPass(const Context& context, const DepthCopyPass& pass) { + Require(pass.color.tileMode != ColorTileMode::Linear && context.detiler != nullptr, "DB depth copy into a target without a resident image is not implemented"); + const auto resident = CachedStorageSurface(context, SurfaceForTarget(pass.color)); + CopyDepthSample(context, pass.depthAddress, pass.sample, *resident, resident->StorageView(0, false), pass.extent); +} + void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass) { if (pass.mode == ColorMetadataPass::Mode::Resolve) { Require(pass.source.has_value() && pass.targets.size() == 1, "CB resolve without its source and destination"); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index a9f9e6026b..76024a6af6 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -1787,6 +1787,55 @@ ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot, bool allo return color; } +namespace { + +bool drawCovers(const Registers& cx, VkExtent2D extent) { + const auto viewportControl = read(cx, 0x206); + if (viewportControl != 0x43fu) throw std::runtime_error(vteMessage(viewportControl)); + const auto xs = std::fabs(readFloat(cx, 0x10f)); + const auto xo = readFloat(cx, 0x110); + const auto ys = std::fabs(readFloat(cx, 0x111)); + const auto yo = readFloat(cx, 0x112); + VkRect2D covered{{0, 0}, {0x7fffu, 0x7fffu}}; + intersect(covered, cx, 0xc, true); + intersect(covered, cx, 0x81, false); + intersect(covered, cx, 0x90, false); + if ((read(cx, 0x292) & 2u) != 0) intersect(covered, cx, 0x94, false); + const auto width = static_cast(extent.width); + const auto height = static_cast(extent.height); + const bool viewportCovers = xo - xs <= 0.0f && xo + xs >= width && yo - ys <= 0.0f && yo + ys >= height; + const bool scissorCovers = covered.offset.x == 0 && covered.offset.y == 0 && covered.extent.width >= extent.width && covered.extent.height >= extent.height; + return viewportCovers && scissorCovers; +} + +} + +std::optional DecodeDepthCopyPass(const QueueState& queue) { + const auto& cx = queue.context; + const auto control = find(cx, 0x000); + if (control == cx.end() || (control->second & 4u) == 0) return std::nullopt; + Require((control->second & ~0xf84u) == 0, "DB depth copy combined with a stencil copy, clear, resummarize or decompress"); + Require((read(cx, 0x200) & 0xfu) == 0, "DB depth copy with depth or stencil tests"); + Require((read(cx, 0x010) & 3u) == 3u, "DB depth copy from a depth plane that is not Z_32_FLOAT"); + Require(ColorWriteMask(cx) == 1u, "DB depth copy into anything but the red channel of color target 0"); + DepthCopyPass pass{}; + const auto high = find(cx, 0x01a); + pass.depthAddress = (high == cx.end() ? 0ull : static_cast(high->second & 0xffu) << 40u) | (static_cast(read(cx, 0x012)) << 8u); + const auto size = read(cx, 0x007); + pass.extent = {(size & 0x3fffu) + 1u, ((size >> 16u) & 0x3fffu) + 1u}; + pass.samples = 1u << ((read(cx, 0x010) >> 2u) & 3u); + const auto maskLow = find(cx, 0x30e); + const auto maskHigh = find(cx, 0x30f); + Require((maskLow == cx.end() || maskLow->second == 0xffffffffu) && (maskHigh == cx.end() || maskHigh->second == 0xffffffffu), "DB depth copy with a sample mask is unsupported"); + pass.sample = (control->second & 0x80u) != 0 ? 0u : (control->second >> 8u) & 0xfu; + Require(pass.sample < pass.samples, "DB depth copy of a sample the depth plane does not have"); + pass.color = DecodeColorBuffer(cx, 0); + Require(pass.color.format == VK_FORMAT_R32_SFLOAT && pass.color.samples == 1 && pass.color.mip == 0 && pass.color.layers == 1, "DB depth copy into a color target that is not a single-sample R32 float"); + Require(pass.color.extent.width == pass.extent.width && pass.color.extent.height == pass.extent.height, "DB depth copy into a color target of another size"); + Require(drawCovers(cx, pass.extent), "DB depth copy over part of the depth surface"); + return pass; +} + std::optional DecodeColorMetadataPass(const QueueState& queue) { const auto& cx = queue.context; const auto control = find(cx, 0x202); @@ -1818,24 +1867,7 @@ std::optional DecodeColorMetadataPass(const QueueState& queue const auto samples = resolve && rasterizerSamples && multisampleRaster ? decodeSamples(cx) : 1u; if (!resolve) zero(cx, 0x2f8, ~0u, "multisampling or coverage conversion"); zero(cx, 0x80, ~0u, "window offset"); - const auto viewportControl = read(cx, 0x206); - if (viewportControl != 0x43fu) throw std::runtime_error(vteMessage(viewportControl)); - const auto xs = std::fabs(readFloat(cx, 0x10f)); - const auto xo = readFloat(cx, 0x110); - const auto ys = std::fabs(readFloat(cx, 0x111)); - const auto yo = readFloat(cx, 0x112); - VkRect2D covered{{0, 0}, {0x7fffu, 0x7fffu}}; - intersect(covered, cx, 0xc, true); - intersect(covered, cx, 0x81, false); - intersect(covered, cx, 0x90, false); - if ((read(cx, 0x292) & 2u) != 0) intersect(covered, cx, 0x94, false); - const auto requireCovered = [&](const ColorTarget& target) { - const auto width = static_cast(target.extent.width); - const auto height = static_cast(target.extent.height); - const bool viewportCovers = xo - xs <= 0.0f && xo + xs >= width && yo - ys <= 0.0f && yo + ys >= height; - const bool scissorCovers = covered.offset.x == 0 && covered.offset.y == 0 && covered.extent.width >= target.extent.width && covered.extent.height >= target.extent.height; - Require(viewportCovers && scissorCovers, "CB metadata pass over part of a color target"); - }; + const auto requireCovered = [&](const ColorTarget& target) { Require(drawCovers(cx, target.extent), "CB metadata pass over part of a color target"); }; if (resolve) { const auto source = DecodeColorBuffer(cx, 0); const auto destination = DecodeColorBuffer(cx, 1); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp index 74621bdba1..4eda0ce57e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp @@ -4,6 +4,7 @@ #include "prx/libSceAgcDriver/Graphics/include/TextureTiling.hpp" #include "prx/libSceAgcDriver/Graphics/shaders/TextureDetile_spv.h" #include "prx/libSceAgcDriver/Graphics/shaders/CmaskClear_spv.h" +#include "prx/libSceAgcDriver/Graphics/shaders/DepthSample_spv.h" #include "prx/libSceAgcDriver/Graphics/shaders/TextureDetileImage_spv.h" #include "prx/libSceAgcDriver/Graphics/shaders/TextureRepack_spv.h" #include @@ -117,6 +118,11 @@ void TextureDetiler::release() noexcept { cmaskErrors.reset(); for (const auto pool : descriptorSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); for (const auto pool : imageDescriptorSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); + for (const auto pool : depthSampleSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); + if (depthSamplePipeline) context.Function("vkDestroyPipeline")(context.device, depthSamplePipeline, nullptr); + if (depthSamplePipelineLayout) context.Function("vkDestroyPipelineLayout")(context.device, depthSamplePipelineLayout, nullptr); + if (depthSampleLayout) context.Function("vkDestroyDescriptorSetLayout")(context.device, depthSampleLayout, nullptr); + if (depthSampler) context.Function("vkDestroySampler")(context.device, depthSampler, nullptr); if (imageModule) context.Function("vkDestroyShaderModule")(context.device, imageModule, nullptr); if (repackPipeline) context.Function("vkDestroyPipeline")(context.device, repackPipeline, nullptr); if (repackModule) context.Function("vkDestroyShaderModule")(context.device, repackModule, nullptr); @@ -394,6 +400,76 @@ void TextureDetiler::DispatchCmaskClear(VkCommandBuffer commands, VkBuffer cmask barrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, anyAccess); } +void TextureDetiler::DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample) { + Require(commands != VK_NULL_HANDLE && depth != VK_NULL_HANDLE && target != VK_NULL_HANDLE, "a depth sample copy needs a command buffer, the depth view and the target view"); + if (depthSamplePipeline == VK_NULL_HANDLE) { + VkSamplerCreateInfo samplerInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + samplerInfo.magFilter = VK_FILTER_NEAREST; + samplerInfo.minFilter = VK_FILTER_NEAREST; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + Check(context.Function("vkCreateSampler")(context.device, &samplerInfo, nullptr, &depthSampler), "vkCreateSampler depth sample copy"); + std::array bindings{}; + bindings[0] = {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, &depthSampler}; + bindings[1] = {1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr}; + VkDescriptorSetLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; + layoutInfo.bindingCount = static_cast(bindings.size()); + layoutInfo.pBindings = bindings.data(); + Check(context.Function("vkCreateDescriptorSetLayout")(context.device, &layoutInfo, nullptr, &depthSampleLayout), "vkCreateDescriptorSetLayout depth sample copy"); + const VkPushConstantRange pushRange{VK_SHADER_STAGE_COMPUTE_BIT, 0, 3 * sizeof(std::uint32_t)}; + VkPipelineLayoutCreateInfo pipelineLayoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &depthSampleLayout; + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushRange; + Check(context.Function("vkCreatePipelineLayout")(context.device, &pipelineLayoutInfo, nullptr, &depthSamplePipelineLayout), "vkCreatePipelineLayout depth sample copy"); + VkShaderModuleCreateInfo moduleInfo{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; + moduleInfo.codeSize = sizeof(DEPTH_SAMPLE_SPV); + moduleInfo.pCode = DEPTH_SAMPLE_SPV; + VkShaderModule depthModule = VK_NULL_HANDLE; + Check(context.Function("vkCreateShaderModule")(context.device, &moduleInfo, nullptr, &depthModule), "vkCreateShaderModule depth sample copy"); + VkComputePipelineCreateInfo pipelineInfo{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; + pipelineInfo.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO}; + pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + pipelineInfo.stage.module = depthModule; + pipelineInfo.stage.pName = "main"; + pipelineInfo.layout = depthSamplePipelineLayout; + const auto created = context.Function("vkCreateComputePipelines")(context.device, context.pipelineCache, 1, &pipelineInfo, nullptr, &depthSamplePipeline); + context.Function("vkDestroyShaderModule")(context.device, depthModule, nullptr); + Check(created, "vkCreateComputePipelines depth sample copy"); + } + const auto set = allocateFrom(depthSampleSets, depthSampleLayout, {{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 64}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 64}}, "vkCreateDescriptorPool depth sample copy", "vkAllocateDescriptorSets depth sample copy", "vkResetDescriptorPool depth sample copy"); + const VkDescriptorImageInfo depthInfo{VK_NULL_HANDLE, depth, VK_IMAGE_LAYOUT_GENERAL}; + const VkDescriptorImageInfo targetInfo{VK_NULL_HANDLE, target, VK_IMAGE_LAYOUT_GENERAL}; + std::array writes{}; + for (std::uint32_t index = 0; index < writes.size(); ++index) { + writes[index].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[index].dstSet = set; + writes[index].dstBinding = index; + writes[index].descriptorCount = 1; + writes[index].descriptorType = index == 0 ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER : VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + } + writes[0].pImageInfo = &depthInfo; + writes[1].pImageInfo = &targetInfo; + context.Function("vkUpdateDescriptorSets")(context.device, static_cast(writes.size()), writes.data(), 0, nullptr); + const auto barrier = [&](VkPipelineStageFlags source, VkPipelineStageFlags destination, VkAccessFlags sourceAccess, VkAccessFlags destinationAccess) { + VkMemoryBarrier memory{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; + memory.srcAccessMask = sourceAccess; + memory.dstAccessMask = destinationAccess; + context.Function("vkCmdPipelineBarrier")(commands, source, destination, 0, 1, &memory, 0, nullptr, 0, nullptr); + }; + constexpr VkAccessFlags anyWrite = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT; + constexpr VkAccessFlags anyAccess = anyWrite | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_HOST_READ_BIT; + barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, anyWrite, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + context.Function("vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, depthSamplePipeline); + context.Function("vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, depthSamplePipelineLayout, 0, 1, &set, 0, nullptr); + const std::array push{width, height, sample}; + context.Function("vkCmdPushConstants")(commands, depthSamplePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push), push.data()); + context.Function("vkCmdDispatch")(commands, (width + 7u) / 8u, (height + 7u) / 8u, 1); + barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_WRITE_BIT, anyAccess); +} + void TextureDetiler::DispatchImage(VkCommandBuffer commands, TextureTileMode tileMode, std::uint32_t elementBytes, VkBuffer tiled, std::uint64_t tiledOffset, VkImageView view, const TileMipLayout& layout, bool retile, std::uint32_t slice, const DetileWindow& window) { Require(commands != VK_NULL_HANDLE, "texture detiling requires an active command buffer"); Require(window.rangeBegin < window.rangeEnd && window.tiledBase <= window.rangeBegin, "texture detiling window is empty"); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 6c37520b87..a20d32de36 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2688,6 +2688,49 @@ void DepthBoundsBiasTests() { alignas(256) std::array dccKeys{}; +void depthCopyPassTests() { + using AgcDriver::Graphics::DecodeDepthCopyPass; + auto queue = makeState(); + queue.context[0x0] = 0; + Require(!DecodeDepthCopyPass(queue).has_value(), "a draw without DEPTH_COPY decoded as a DB depth copy"); + queue.context[0x000] = 0x84; + queue.context[0x010] = 0x80000187; + queue.context[0x011] = 0x20000180; + queue.context[0x012] = 0x00001000; + queue.context[0x007] = 0x0003003f; + queue.context[0x31c] = 0x40710; + queue.context[0x8e] = 1; + auto pass = DecodeDepthCopyPass(queue); + Require(pass.has_value() && pass->depthAddress == 0x100000 && pass->samples == 2 && pass->sample == 0 && pass->extent.width == 64 && pass->extent.height == 4, "a centroid DB depth copy of a two-sample depth plane decoded differently"); + Require(pass->color.format == VK_FORMAT_R32_SFLOAT && pass->color.samples == 1 && pass->color.address == reinterpret_cast(colorMemory.data()), "the DB depth copy target decoded differently"); + queue.context[0x000] = 0x104; + Require(DecodeDepthCopyPass(queue)->sample == 1, "COPY_SAMPLE without COPY_CENTROID did not select the sample"); + queue.context[0x000] = 0x204; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "a sample the depth plane does not have"); + queue.context[0x000] = 0x8c; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "stencil copy"); + queue.context[0x000] = 0x84; + queue.context[0x30e] = 0xfffffffe; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "sample mask"); + queue.context[0x30e] = 0xffffffff; + queue.context[0x200] = 2; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "depth or stencil tests"); + queue.context[0x200] = 0; + queue.context[0x010] = 0x80000185; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "Z_32_FLOAT"); + queue.context[0x010] = 0x80000187; + queue.context[0x8e] = 0xf; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "red channel"); + queue.context[0x8e] = 1; + queue.context[0x31c] = 0x28028; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "single-sample R32 float"); + queue.context[0x31c] = 0x40710; + queue.context[0x10f] = std::bit_cast(16.0f); + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "over part of the depth surface"); + queue.context[0x10f] = std::bit_cast(32.0f); + Require(DecodeDepthCopyPass(queue).has_value(), "the restored DB depth copy was refused"); +} + void metadataPassTests() { using AgcDriver::Graphics::ColorMetadataPass; using AgcDriver::Graphics::DecodeColorMetadataPass; @@ -10454,6 +10497,7 @@ int main(int argc, char** argv) { DualSourceBlendTests(); ReversedBlendShaderTests(); metadataPassTests(); + depthCopyPassTests(); cmaskTests(); uint16ExportTests(); uint8x2TargetTests(); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp index 3ab804d307..a8adc31516 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp @@ -1,6 +1,9 @@ #include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp" #include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp" +#include "prx/libSceAgcDriver/Graphics/include/ColorTargetLayout.hpp" #include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp" +#include "prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp" #include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp" #include "prx/libSceAgcDriver/Graphics/include/Texture.hpp" #include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp" @@ -29,6 +32,7 @@ using namespace AgcDriver::Graphics; constexpr std::uint32_t Side = 64; constexpr std::size_t BlockBytes = 65536; alignas(4096) std::array Storage{}; +alignas(4096) std::array CopyStorage{}; constexpr std::uint32_t Format32Float = 22; constexpr std::uint32_t Format8Uint = 5; @@ -301,6 +305,53 @@ void RunPairings(Context context, std::uint8_t* block) { ExpectFailure([&] { static_cast(DepthSurfaceStoreBack(context, *depth)); }, "through a view that another view of the plane", "a store back through a replaced view was taken by an older pairing that covers the plane"); } +void DepthCopyCase(Context context, std::uint8_t* block) { + const auto colorAddress = reinterpret_cast(block); + const auto depthAddress = colorAddress + BlockBytes / 4; + Recorder recorder(context); + recorder.Activate(); + const auto submit = [&] { + recorder.Submit(); + Check(context.Function("vkQueueWaitIdle")(context.queue), "vkQueueWaitIdle"); + }; + ColorTarget color{}; + color.address = colorAddress; + color.surfaceAddress = colorAddress; + color.extent = {Side, Side}; + color.surfaceExtent = color.extent; + color.format = VK_FORMAT_R32_SFLOAT; + color.elementBytes = 4; + color.tileMode = ColorTileMode::RenderTarget; + color.bytes = ColorTargetLayout(Side, Side, ColorTileMode::RenderTarget, 4).Bytes(); + const auto copy = [&](std::uint32_t samples, std::uint32_t sample, VkExtent2D extent) { + RunDepthCopyPass(context, DepthCopyPass{depthAddress, extent, samples, sample, color}); + submit(); + const auto resident = CachedStorageSurface(context, SurfaceForTarget(color)); + resident->MarkDirty(); + resident->WriteBack(); + submit(); + }; + const auto countBlock = [&](float value) { + std::size_t count = 0; + for (std::size_t offset = 0; offset < BlockBytes; offset += sizeof(float)) { + float texel = 0; + std::memcpy(&texel, block + offset, sizeof(texel)); + if (texel == value) ++count; + } + return count; + }; + std::fill(block, block + BlockBytes, std::uint8_t{0}); + ExpectFailure([&] { copy(2, 0, {Side, Side}); }, "no draw rendered", "a DB depth copy from memory without a depth surface was accepted"); + Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.375f, 0, 0, false, 2}) != VK_NULL_HANDLE, "the two-sample depth surface was not created"); + copy(2, 0, {Side, Side}); + Require(countBlock(0.375f) == Side * Side, "the DB depth copy of sample 0 did not store the cleared depth in every texel of the R32 float target"); + std::fill(block, block + BlockBytes, std::uint8_t{0}); + copy(2, 1, {Side, Side}); + Require(countBlock(0.375f) == Side * Side, "the DB depth copy of sample 1 did not store the cleared depth in every texel of the R32 float target"); + ExpectFailure([&] { copy(2, 2, {Side, Side}); }, "does not have", "a DB depth copy of a third sample of a two-sample surface was accepted"); + ExpectFailure([&] { copy(2, 0, {Side / 2, Side / 2}); }, "of another size", "a DB depth copy of another extent than the depth surface was accepted"); +} + } int main() { @@ -321,6 +372,11 @@ int main() { { std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex()); Run(context, block); + auto* copyBlock = CopyStorage.data() + (BlockBytes - reinterpret_cast(CopyStorage.data()) % BlockBytes) % BlockBytes; + GuestAllocations::Mutation().Add(copyBlock, BlockBytes, true, true, true); + DepthCopyCase(context, copyBlock); + ClearCachedTextures(context.device); + GuestAllocations::Mutation().Remove(copyBlock); ClearDepthSurfaces(context.device); RunPairings(context, block); ClearDepthSurfaces(context.device); diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index d4aad524e5..35a404a973 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -153,6 +153,7 @@ - [Multisampled texture no draw rendered to](../../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp) (libSceAgcDriver) - sampling a multisampled texture whose surface no draw rendered to creates the host target from the T# and clears it to the color a single 32-bit value repeated over the whole footprint encodes, padding up to whole tile blocks included. Inferred, not measured on the console: that the samples of such a surface are the guest color memory, as a title that clears its canvas on the CPU (ps5-opengl `ps5_clear_msaa4_color`) relies on. Memory that is not one repeated value, tile modes other than Standard4KB, Standard64KB and RenderTarget, unmapped memory and values with no exact clear color throw. CPU writes to the surface after its host target exists are not seen by the host. - [Multisampled targets in CB passes](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - a CB eliminate or DCC pass over a multisampled target throws, since its texels are on the host and only a CB resolve and an FMASK decompress (`CB_COLOR_CONTROL` mode 5) are modeled. The FMASK decompress only applies the pending CMASK fast clear to the host image; it requires a CMASK address, and the target's DCC and FMASK contents are not interpreted. DCC over a multisampled target is accepted only in that pass and throws everywhere else. A CB resolve into a fast-clear destination throws: the resolve writes the destination's texels but not its CMASK, which would still read as cleared. - [Multisampled target guest access](../../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp) (libSceAgcDriver) - the memory a multisampled color target throws on (CPU read, index buffer read, fixed-function vertex buffer read, DMA copy, texture or storage image read) uses fragment-aware 64 KiB render-target blocks. [AMD Addrlib's GFX10 thin-block rule](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/core/addrlib2.cpp#L1680-L1712) gives the block width and height from element bytes and fragment count; its [surface-size calculation](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp#L4000-L4005) rounds both extents to those blocks and multiplies by element bytes and fragments. Thus a 1920x1080 4x RGBA8 surface occupies 0x1fe0000 bytes, leaving the next allocation at +0x2000000 outside the guard. The 2x and 8x block orientations differ from treating the fragments as a larger element. Console address-layout equivalence has not been measured. Other tile modes retain the conservative `max(CB bytes * samples, width * height * element bytes * samples)` bound, which may still reject a distinct allocation. Live targets remain cached per device, surface address, format, extent and sample count. Retired cache owners are removed on the next target, sampled or storage lookup; outstanding recorded work and sampled views retain their images. Ordinary sampled and storage bindings check the color-target guard before resource reuse, including precollected sampled lookups. Reused image tables do not invalidate on multisample color-target registration yet; their per-element fault handling and native multisample sampling require a separate reuse proof. +- [DB depth copies](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - a draw with `DB_RENDER_CONTROL.DEPTH_COPY` is run as a compute pass that copies one sample of the `Z_32_FLOAT` depth surface into a single-sample R32 float color target over the whole surface. With `COPY_CENTROID` the sample is the first one the draw covers, which is sample 0 under the full `PA_SC_AA_MASK` the driver requires: [radeonsi copies one sample per draw with `COPY_CENTROID` and a one-sample mask](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-24.0.0/src/gallium/drivers/radeonsi/si_state.c#L1679-1681); the console is not measured. Stencil copies, depth tests, other depth formats, multisampled or other-format targets and copies over part of the surface throw. - [CB resolve sample count](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - a draw with CB_COLOR_CONTROL in resolve mode is a CB resolve when either PA_SC_AA_CONFIG or CB_COLOR0_ATTRIB.NUM_SAMPLES is multisampled, and takes its sample count from CB0. Inferred, not from documentation: PPSA36082 resolves its 4x targets with no sample count in PA_SC_AA_CONFIG, and radeonsi sets PA_SC_AA_CONFIG to 0 for a multisampled framebuffer when rasterizer multisampling and line smoothing are off. PA_SC_AA_CONFIG is only compared with CB0 while PA_SC_MODE_CNTL_0.MSAA_ENABLE is set: PPSA08270 resolves a 4x 512x512 target with MSAA_ENABLE off and PA_SC_AA_CONFIG still at 2x (0x00108001) from earlier draws. - [Custom MSAA sample locations](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) support a uniform 1x1 pattern for 2, 4 or 8 samples through VK_EXT_sample_locations. Patterns varying across the guest's 2x2 pixel grid use a 2x2 sample location grid, anchored at the framebuffer origin like the guest's while the window offset is zero, on devices whose `maxSampleLocationGridSize` for the sample count is a multiple of 2x2. On other devices every pixel takes the pattern's pixel (0,0) locations (`PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_*`, its first registers and the corner the grid is anchored at), and the first such pipeline of each sample count logs `[gpu] no 2x2 sample location grid`. The RTX 5070 reports 2x4 for 2 samples, 2x2 for 4 and 1x2 for 8; an AMD Radeon integrated GPU reports 2x2 for each; an RTX 3070 Laptop GPU (driver 617.42) reports 1x1 for every count. With that stand-in the top-left pixel of every 2x2 quad matches the console and the other three use its positions. A pattern that only reorders one set of positions between pixels, as a 2x checkerboard pattern puts its samples at x = +4/16 and -4/16 and swaps them on odd rows, keeps every position and the number of samples a primitive covers in each pixel, so a resolve that averages a pixel's samples, or takes their minimum or maximum, sees the console's values. Sample i of an odd row then holds what the console stores in sample 1 - i: a pass that reads the samples by index, as checkerboard reconstruction does, exchanges the two half-pixels of odd rows, which can show as combing along vertical edges, and per-sample shading, sample-mask exports and coverage inputs see the swapped indices too. A pattern whose pixels hold different positions draws pixel (0,0)'s positions in each pixel whose own differ. A device's pipelines and render pass begins take the same stand-in for a given pattern, so a sample-location-compatible depth image is not drawn with one set of locations and transitioned or tested with another. A multisampled rasterizer drawing into one-sample color targets, and a one-sample depth target if any, with MSAA_ENABLE set, no alpha-to-coverage and no coverage pixel input is drawn with one sample at sample 0's location in each pixel of the grid, as the targets keep only sample 0's coverage. Without PS_ITER_SAMPLE such a draw also refuses centroid inputs, and keeps PERSP_SAMPLE, LINEAR_SAMPLE and ANCILLARY: a pixel shaded once is taken to get sample 0's barycentrics and sample index 0. With PS_ITER_SAMPLE only sample 0's invocation reaches the targets, so its sample and centroid barycentrics are taken to be sample 0's and the draw is shaded once. Both are inferred, not measured. The pixel shader runs once per pixel with sample number 0, so ANCILLARY reads sample 0 and POS_X_FLOAT and POS_Y_FLOAT keep the position `SPI_BARYC_CNTL` names. PERSP_CENTER and LINEAR_CENTER inputs on this path are interpolated at sample 0's location on the host, where the console uses the pixel center; one-sample depth images are created with `SAMPLE_LOCATIONS_COMPATIBLE_DEPTH` when the device supports it, as multisampled ones already were; this is how VK_AMD_mixed_attachment_samples defines the case, and it is not measured on PS5 hardware. Other mixed sample counts throw. Devices without the required sample count, four subpixel bits or the full guest coordinate range refuse custom patterns. Depth images remain in GENERAL between draws, and render passes cannot continue across pattern changes. Guest offsets retain the existing decoder's signed four-bit, sixteenth-pixel interpretation; PS5 coverage and interpolation equivalence have not been measured on hardware. - [Single-sample scan into a multisampled depth target](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - a draw with `PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES` = 0 and `PA_SC_MODE_CNTL_0.MSAA_ENABLE` clear into a multisampled depth target rasterizes the depth target's sample count with every sample at the pixel center, as a single-sample scan into a multisampled color target already does. radeonsi programs this `MSAA_ENABLE` and `PA_SC_AA_CONFIG` with the depth buffer's own sample count when GL multisampling is disabled on a multisampled framebuffer ([L1066-L1068](https://github.com/openbsd/xenocara/blob/1b39033df8d928ec247ca64448c4a63c72302db7/lib/mesa/src/gallium/drivers/radeonsi/si_state.c#L1066-L1068), [L3485-L3512](https://github.com/openbsd/xenocara/blob/1b39033df8d928ec247ca64448c4a63c72302db7/lib/mesa/src/gallium/drivers/radeonsi/si_state.c#L3485-L3512)), although its comment lists Z samples as at most the coverage samples ([L3439](https://github.com/openbsd/xenocara/blob/1b39033df8d928ec247ca64448c4a63c72302db7/lib/mesa/src/gallium/drivers/radeonsi/si_state.c#L3439)) and it sets `DB_EQAA.MAX_ANCHOR_SAMPLES` to the Z sample count, where PPSA18216 sets `DB_EQAA` to 0. GL then lets every sample take the single-sample depth ([L185-L190](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L185-L190)) and the fragment be treated as in multisampling with full coverage ([L422-L425](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L422-L425)), which is what this draws: each depth and stencil sample is tested and written with the depth at the pixel center. GL also lets an implementation test only a centermost sample and set every sample from it ([L425-L434](https://github.com/KhronosGroup/OpenGL-Registry/blob/6af574a14089ccfee87efe230ebcdd8742859813/extensions/ARB/ARB_multisample.txt#L425-L434)), which differs when a pixel's stored samples differ; which one the console does, whether it instead evaluates the depth plane at each sample position, and how its occlusion queries count such a draw, is not measured on hardware. Points, lines, alpha-to-coverage, `DB_EQAA` Z interpolation or overrasterization, a pixel program reading the sample index, a sample-mask export and a single-sample color target next to the multisampled depth target throw From 120900a3428292d684f1ed607b8ab5ff46a8172a Mon Sep 17 00:00:00 2001 From: milescoviello Date: Tue, 20 Oct 2026 16:40:31 +0000 Subject: [PATCH 110/523] fix(agc): read one-sample DB depth copies through sampler2D A one-sample depth surface was bound to the copy shader's sampler2DMS, which Vulkan only allows for a multisampled view. The shader is now built in a single-sample and a multisample variant, and the copy picks one by the surface's sample count. The copy also no longer requires the guest-memory import state of native multisample depth sampling: it reads the host image the draws rendered into. Its barriers name the host stage their host access bits need, as the CMASK clear's do. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 33 ++++++++++++------- .../Graphics/include/TextureDetiler.hpp | 4 +-- .../Graphics/shaders/DepthSample.comp | 11 ++++++- .../Graphics/src/DepthSurface.cpp | 3 +- .../Graphics/src/TextureDetiler.cpp | 20 ++++++----- .../prx/libSceAgcDriver/tests/Graphics.cpp | 5 +++ .../tests/execution/DepthStorage.cpp | 6 ++++ 7 files changed, 57 insertions(+), 25 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index e784a11285..771f4cd014 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -131,17 +131,26 @@ add_custom_command( ) set(agcDepthSampleShaderSource ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/shaders/DepthSample.comp) -set(agcDepthSampleShaderSpv ${agcTextureDetileGeneratedDir}/DepthSample.spv) -set(agcDepthSampleShaderHeader ${agcTextureDetileGeneratedDir}/DepthSample_spv.h) -add_custom_command( - OUTPUT "${agcDepthSampleShaderHeader}" - COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTextureDetileGeneratedDir}" - COMMAND "$" -V --target-env vulkan1.1 -o "${agcDepthSampleShaderSpv}" "${agcDepthSampleShaderSource}" - COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthSampleShaderSpv} -DOUTPUT=${agcDepthSampleShaderHeader} - -DSYMBOL=DEPTH_SAMPLE_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake" - DEPENDS "${agcDepthSampleShaderSource}" glslang-standalone - VERBATIM -) +foreach(agcDepthSampleKind IN ITEMS Single Multisample) + string(TOUPPER ${agcDepthSampleKind} agcDepthSampleSymbol) + if(agcDepthSampleKind STREQUAL "Multisample") + set(agcDepthSampleMultisampled 1) + else() + set(agcDepthSampleMultisampled 0) + endif() + set(agcDepthSampleShaderSpv ${agcTextureDetileGeneratedDir}/DepthSample${agcDepthSampleKind}.spv) + set(agcDepthSampleShaderHeader ${agcTextureDetileGeneratedDir}/DepthSample${agcDepthSampleKind}_spv.h) + add_custom_command( + OUTPUT "${agcDepthSampleShaderHeader}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTextureDetileGeneratedDir}" + COMMAND "$" -V --target-env vulkan1.1 -DMULTISAMPLED=${agcDepthSampleMultisampled} -o "${agcDepthSampleShaderSpv}" "${agcDepthSampleShaderSource}" + COMMAND ${CMAKE_COMMAND} -DINPUT=${agcDepthSampleShaderSpv} -DOUTPUT=${agcDepthSampleShaderHeader} + -DSYMBOL=DEPTH_SAMPLE_${agcDepthSampleSymbol}_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake" + DEPENDS "${agcDepthSampleShaderSource}" glslang-standalone + VERBATIM + ) + list(APPEND agcDepthSampleShaderHeaders ${agcDepthSampleShaderHeader}) +endforeach() set(agcSampleCounterShaderSource ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/shaders/SampleCounter.comp) set(agcSampleCounterShaderSpv ${agcTextureDetileGeneratedDir}/SampleCounter.spv) @@ -316,7 +325,7 @@ set(agcDriverSources ${agcTextureDetileShaderHeader} ${agcReadbackHeaders} ${agcCmaskClearShaderHeader} - ${agcDepthSampleShaderHeader} + ${agcDepthSampleShaderHeaders} ${agcMeshArgumentsShaderHeader} ${agcTextureDetileImageShaderHeader} ${agcTextureRepackShaderHeader} diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp index 82a9c77e77..316ae0cb63 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp @@ -56,7 +56,7 @@ namespace AgcDriver::Graphics { void DispatchCmaskClear(VkCommandBuffer commands, VkBuffer cmask, std::uint64_t cmaskOffset, std::size_t cmaskBytes, VkImageView view, std::uint32_t width, std::uint32_t height, std::uint32_t elementBytes, const std::array& clearWords, bool write); std::uint32_t CmaskErrors(); void DispatchMultisampleReadback(VkCommandBuffer commands, VkFormat format, VkBuffer destination, std::uint64_t offset, std::uint64_t bytes, VkImageView arrayView, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t fragments, std::uint32_t firstElement, std::uint32_t elements); - void DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample); + void DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, bool multisampled, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample); private: VkPipeline multisampleReadbackPipeline(std::uint32_t elementBytes, bool depth); @@ -85,7 +85,7 @@ namespace AgcDriver::Graphics { DescriptorRing cmaskDescriptorSets; VkDescriptorSetLayout depthSampleLayout = VK_NULL_HANDLE; VkPipelineLayout depthSamplePipelineLayout = VK_NULL_HANDLE; - VkPipeline depthSamplePipeline = VK_NULL_HANDLE; + std::array depthSamplePipelines{}; VkSampler depthSampler = VK_NULL_HANDLE; DescriptorRing depthSampleSets; std::unique_ptr cmaskErrors; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp b/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp index fa83092614..36f1883556 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp +++ b/core/libs/prx/libSceAgcDriver/Graphics/shaders/DepthSample.comp @@ -2,7 +2,11 @@ layout(local_size_x = 8, local_size_y = 8) in; +#if MULTISAMPLED layout(set = 0, binding = 0) uniform sampler2DMS depth; +#else +layout(set = 0, binding = 0) uniform sampler2D depth; +#endif layout(set = 0, binding = 1, r32f) writeonly uniform image2D target; layout(push_constant) uniform Push { @@ -14,5 +18,10 @@ layout(push_constant) uniform Push { void main() { uvec2 pixel = gl_GlobalInvocationID.xy; if (pixel.x >= push.width || pixel.y >= push.height) return; - imageStore(target, ivec2(pixel), vec4(texelFetch(depth, ivec2(pixel), push.sampleIndex).r, 0.0, 0.0, 0.0)); +#if MULTISAMPLED + float value = texelFetch(depth, ivec2(pixel), push.sampleIndex).r; +#else + float value = texelFetch(depth, ivec2(pixel), 0).r; +#endif + imageStore(target, ivec2(pixel), vec4(value, 0.0, 0.0, 0.0)); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp index 6be0b9cdb9..5585ea8da5 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp @@ -495,7 +495,6 @@ public: auto* recorder = Recorder::Active(); Require(recorder != nullptr, "depth sample copies need an active recorder"); Require(sampleable, "DB depth copy from a depth surface the device cannot sample at its sample count"); - Require(target.samples == 1 || samplingActive, "native multisample depth sampling requires its creation-time owner"); ApplyFastClear(); if (useSampleShadow) { if (sampleShadow == nullptr) createSampleShadow(); @@ -511,7 +510,7 @@ public: Check(context.Function("vkCreateImageView")(context.device, &viewInfo, nullptr, &depthView), "vkCreateImageView depth sample copy"); } Require(context.detiler != nullptr, "depth sample copies need the texture detiler"); - context.detiler->DispatchDepthSample(recorder->Commands(), depthView, destinationView, target.extent.width, target.extent.height, sample); + context.detiler->DispatchDepthSample(recorder->Commands(), depthView, target.samples > 1, destinationView, target.extent.width, target.extent.height, sample); Recorder::CountBarriers(Recorder::CommandClass::Copy, 2); destination.MarkDirty(); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp index 4eda0ce57e..d2a6964f6b 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp @@ -4,7 +4,8 @@ #include "prx/libSceAgcDriver/Graphics/include/TextureTiling.hpp" #include "prx/libSceAgcDriver/Graphics/shaders/TextureDetile_spv.h" #include "prx/libSceAgcDriver/Graphics/shaders/CmaskClear_spv.h" -#include "prx/libSceAgcDriver/Graphics/shaders/DepthSample_spv.h" +#include "prx/libSceAgcDriver/Graphics/shaders/DepthSampleMultisample_spv.h" +#include "prx/libSceAgcDriver/Graphics/shaders/DepthSampleSingle_spv.h" #include "prx/libSceAgcDriver/Graphics/shaders/TextureDetileImage_spv.h" #include "prx/libSceAgcDriver/Graphics/shaders/TextureRepack_spv.h" #include @@ -119,7 +120,7 @@ void TextureDetiler::release() noexcept { for (const auto pool : descriptorSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); for (const auto pool : imageDescriptorSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); for (const auto pool : depthSampleSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); - if (depthSamplePipeline) context.Function("vkDestroyPipeline")(context.device, depthSamplePipeline, nullptr); + for (const auto pipeline : depthSamplePipelines) if (pipeline) context.Function("vkDestroyPipeline")(context.device, pipeline, nullptr); if (depthSamplePipelineLayout) context.Function("vkDestroyPipelineLayout")(context.device, depthSamplePipelineLayout, nullptr); if (depthSampleLayout) context.Function("vkDestroyDescriptorSetLayout")(context.device, depthSampleLayout, nullptr); if (depthSampler) context.Function("vkDestroySampler")(context.device, depthSampler, nullptr); @@ -400,9 +401,9 @@ void TextureDetiler::DispatchCmaskClear(VkCommandBuffer commands, VkBuffer cmask barrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, anyAccess); } -void TextureDetiler::DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample) { +void TextureDetiler::DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, bool multisampled, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample) { Require(commands != VK_NULL_HANDLE && depth != VK_NULL_HANDLE && target != VK_NULL_HANDLE, "a depth sample copy needs a command buffer, the depth view and the target view"); - if (depthSamplePipeline == VK_NULL_HANDLE) { + if (depthSamplePipelineLayout == VK_NULL_HANDLE) { VkSamplerCreateInfo samplerInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; samplerInfo.magFilter = VK_FILTER_NEAREST; samplerInfo.minFilter = VK_FILTER_NEAREST; @@ -424,9 +425,12 @@ void TextureDetiler::DispatchDepthSample(VkCommandBuffer commands, VkImageView d pipelineLayoutInfo.pushConstantRangeCount = 1; pipelineLayoutInfo.pPushConstantRanges = &pushRange; Check(context.Function("vkCreatePipelineLayout")(context.device, &pipelineLayoutInfo, nullptr, &depthSamplePipelineLayout), "vkCreatePipelineLayout depth sample copy"); + } + auto& depthSamplePipeline = depthSamplePipelines[multisampled ? 1u : 0u]; + if (depthSamplePipeline == VK_NULL_HANDLE) { VkShaderModuleCreateInfo moduleInfo{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; - moduleInfo.codeSize = sizeof(DEPTH_SAMPLE_SPV); - moduleInfo.pCode = DEPTH_SAMPLE_SPV; + moduleInfo.codeSize = multisampled ? sizeof(DEPTH_SAMPLE_MULTISAMPLE_SPV) : sizeof(DEPTH_SAMPLE_SINGLE_SPV); + moduleInfo.pCode = multisampled ? DEPTH_SAMPLE_MULTISAMPLE_SPV : DEPTH_SAMPLE_SINGLE_SPV; VkShaderModule depthModule = VK_NULL_HANDLE; Check(context.Function("vkCreateShaderModule")(context.device, &moduleInfo, nullptr, &depthModule), "vkCreateShaderModule depth sample copy"); VkComputePipelineCreateInfo pipelineInfo{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; @@ -461,13 +465,13 @@ void TextureDetiler::DispatchDepthSample(VkCommandBuffer commands, VkImageView d }; constexpr VkAccessFlags anyWrite = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT; constexpr VkAccessFlags anyAccess = anyWrite | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_HOST_READ_BIT; - barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, anyWrite, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, anyWrite, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); context.Function("vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, depthSamplePipeline); context.Function("vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, depthSamplePipelineLayout, 0, 1, &set, 0, nullptr); const std::array push{width, height, sample}; context.Function("vkCmdPushConstants")(commands, depthSamplePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push), push.data()); context.Function("vkCmdDispatch")(commands, (width + 7u) / 8u, (height + 7u) / 8u, 1); - barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_WRITE_BIT, anyAccess); + barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_SHADER_WRITE_BIT, anyAccess); } void TextureDetiler::DispatchImage(VkCommandBuffer commands, TextureTileMode tileMode, std::uint32_t elementBytes, VkBuffer tiled, std::uint64_t tiledOffset, VkImageView view, const TileMipLayout& layout, bool retile, std::uint32_t slice, const DetileWindow& window) { diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index a20d32de36..3566cc9bd2 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2729,6 +2729,11 @@ void depthCopyPassTests() { expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "over part of the depth surface"); queue.context[0x10f] = std::bit_cast(32.0f); Require(DecodeDepthCopyPass(queue).has_value(), "the restored DB depth copy was refused"); + queue.context[0x010] = 0x80000183; + pass = DecodeDepthCopyPass(queue); + Require(pass.has_value() && pass->samples == 1 && pass->sample == 0, "a centroid DB depth copy of a one-sample depth plane decoded differently"); + queue.context[0x000] = 0x104; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "a sample the depth plane does not have"); } void metadataPassTests() { diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp index a8adc31516..528998474e 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp @@ -350,6 +350,12 @@ void DepthCopyCase(Context context, std::uint8_t* block) { Require(countBlock(0.375f) == Side * Side, "the DB depth copy of sample 1 did not store the cleared depth in every texel of the R32 float target"); ExpectFailure([&] { copy(2, 2, {Side, Side}); }, "does not have", "a DB depth copy of a third sample of a two-sample surface was accepted"); ExpectFailure([&] { copy(2, 0, {Side / 2, Side / 2}); }, "of another size", "a DB depth copy of another extent than the depth surface was accepted"); + ClearDepthSurfaces(context.device); + Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.625f, 0, 0, false, 1}) != VK_NULL_HANDLE, "the one-sample depth surface was not created"); + std::fill(block, block + BlockBytes, std::uint8_t{0}); + copy(1, 0, {Side, Side}); + Require(countBlock(0.625f) == Side * Side, "the DB depth copy of a one-sample depth surface did not store the cleared depth in every texel of the R32 float target"); + ExpectFailure([&] { copy(1, 1, {Side, Side}); }, "does not have", "a DB depth copy of a second sample of a one-sample surface was accepted"); } } From bcc36571bfc687770ec32b52b40a99bb821dbcf4 Mon Sep 17 00:00:00 2001 From: JJenkx Date: Wed, 21 Oct 2026 04:10:43 -0400 Subject: [PATCH 111/523] test(agc): bind a misaligned sparse descriptor past its committed prefix without reading the tail Co-Authored-By: Claude Opus 5.5 --- .../prx/libSceAgcDriver/tests/BdaResources.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp b/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp index c52c6b4a02..7d4ccd4701 100644 --- a/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp @@ -2224,6 +2224,21 @@ void sparseImportedHeapTests(const Context& context, const BdaTestAccess& access addressed.WriteBack(); } } + { + auto misaligned = importing; + misaligned.limits.minStorageBufferOffsetAlignment = 256; + std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex()); + Recorder recorder(misaligned); + recorder.Activate(); + GuestBufferMemory memory(misaligned); + memory.AddReadable(base + 16, 2 * bytes - 16); + memory.Upload(false); + std::uint32_t adjustment = 0; + const auto view = memory.Descriptor(base + 16, 2 * bytes - 16, adjustment); + Require(view.buffer != imported->buffer, "a misaligned sparse descriptor bound its import in place"); + Require(view.range != 0 && view.range <= bytes && memory.CopiedBytes() <= bytes && access.bytes(view.buffer)[view.offset + adjustment] == std::byte{0x57}, "a misaligned sparse descriptor does not serve its committed prefix alone"); + memory.WriteBack(); + } { GuestBufferMemory written(importing); written.AddWritable(base, 2 * bytes); From 2baa2b5a6ee4d333a8f6dfab4747989af5315831 Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Wed, 21 Oct 2026 21:16:31 +1300 Subject: [PATCH 112/523] docs(dev): describe the missing-interpolator debt entry without naming a title Name no title, title ID or shader address in the entry. The mechanism text is unchanged. --- docs/dev/debt/shader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/shader.md b/docs/dev/debt/shader.md index 996e1becf4..1bac118a65 100644 --- a/docs/dev/debt/shader.md +++ b/docs/dev/debt/shader.md @@ -25,7 +25,7 @@ ### Functional -- [Pixel interpolators past `SPI_PS_IN_CONTROL.NUM_INTERP`](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp) (shader recompiler) - when the IR reads an attribute at or past the guest interpolator count, the missing `SPI_PS_INPUT_CNTL` slots are filled as DEFAULT (`OFFSET` 0x20, constant 0/1) before info collection, and SPIR-V emission of `GetInterpolationParameter` for a missing binding returns the same defaults instead of throwing, matching Mesa's treatment of a PS input with no VS output. Component and mode bounds still throw. PPSA03541 (UFC 5) prepares pixel shader `0x114073b400` with `NUM_INTERP` 0 while it still issues `v_interp` on attribute 0 (`undefined attribute` after the collector fill alone). Console behaviour for that pairing is not measured. +- [Pixel interpolators past `SPI_PS_IN_CONTROL.NUM_INTERP`](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp) (shader recompiler) - when the IR reads an attribute at or past the guest interpolator count, the missing `SPI_PS_INPUT_CNTL` slots are filled as DEFAULT (`OFFSET` 0x20, constant 0/1) before info collection, and SPIR-V emission of `GetInterpolationParameter` for a missing binding returns the same defaults instead of throwing, matching Mesa's treatment of a PS input with no VS output. Component and mode bounds still throw. A retail title prepares a pixel shader with `NUM_INTERP` 0 while it still issues `v_interp` on attribute 0 (`undefined attribute` after the collector fill alone). Console behaviour for that pairing is not measured. - [Windows cache housekeeping](../../../core/shader/recompiler/ShaderDiskCache.cpp): eviction of old versions and temporary files is skipped for `\\?\` and `\\.\` cache roots because the supported WinLibs directory iterator can enumerate the process working directory for long prefixed paths. The active version's `last-used` stamp update still runs; it fails for long `\\.\` aliases with the supported toolchain. Ordinary Windows directory enumeration remains subject to MAX_PATH. - [f64 compares](../../../core/shader/recompiler/Translation/src/CompareInstructions.cpp) flush denormal inputs to signed zero when bit 0 of the `FLOAT_MODE` f16/f64 denormal field is clear (modes 0 and 2); modes 1 and 3 and an unknown mode keep them. This follows the RDNA2 ISA section 6.4 and LLVM's sign-preserving AMDGPU denormal modes. The gfx1036 audit in [#981](https://github.com/boykopovar/AnyPS5/issues/981#issuecomment-6070046381) reports mode-sensitive `VopcCompareF64` results. Class compares still inspect the original bits. The full four-mode matrix has not been newly measured on AMD hardware or the PS5's gfx1013. From 127aba3ed65c12f064a57ff12a8cf6c7768deb06 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Wed, 21 Oct 2026 08:37:14 +0000 Subject: [PATCH 113/523] fix(agc): order and address DB depth copies like the surfaces they read - Drain the draw pipeline before recording the copy, as the CB metadata pass does, so the copy follows the queued draws that render the plane. - Address the plane as decodeDepth registers it: a multisampled plane's pipe-bank XOR bits are stripped and a DB_DEPTH_VIEW slice is added. Mipmapped or partially resident planes are refused. - Refuse a color target with CMASK fast clears, as the CB resolve does. - Find the source through the depth owner (planeSurface) and require a 32-bit float surface with the decoded sample count. Co-Authored-By: Claude Opus 5.5 --- .../Execution/src/Driver/Draw/DrawPrecheck.cpp | 1 + .../Graphics/include/DepthSurface.hpp | 2 +- .../libSceAgcDriver/Graphics/src/DepthSurface.cpp | 15 ++++++++------- .../prx/libSceAgcDriver/Graphics/src/Draw.cpp | 2 +- .../prx/libSceAgcDriver/Graphics/src/State.cpp | 6 ++++++ core/libs/prx/libSceAgcDriver/tests/Graphics.cpp | 11 +++++++++++ 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp index 157789875f..6311d6461f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawPrecheck.cpp @@ -27,6 +27,7 @@ std::optional Driver::precheckDraw(const QueueState& queue, const S } if (const auto copy = Graphics::DecodeDepthCopyPass(queue)) { require(!drawParameters.indirect, "indirect DB depth copies are unsupported"); + if (DrawPipeline::Active()) DrawPipeline::Queue0().Drain(DrawPipeline::DrainReason::Packet, (packet[0] >> 8u) & 0xffu); GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Draw); std::lock_guard gpuLock(GuestMemory::GpuMutex()); if (device == nullptr) device = std::make_shared(); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp index 9dffa61679..66a816656e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp @@ -62,7 +62,7 @@ VkFormat HostDepthFormat(const Context& context, VkFormat format); float HostDepthBiasConstant(const Context& context, VkFormat format, float constant); std::shared_ptr DepthSurfaceStorage(const Context& context, std::span words, const GuestTextureResource& resource, std::uint32_t mip); bool DepthSurfaceStoreBack(const Context& context, const StorageTexture& storage); -void CopyDepthSample(const Context& context, std::uint64_t depthAddress, std::uint32_t sample, StorageTexture& destination, VkImageView destinationView, VkExtent2D extent); +void CopyDepthSample(const Context& context, std::uint64_t depthAddress, std::uint32_t samples, std::uint32_t sample, StorageTexture& destination, VkImageView destinationView, VkExtent2D extent); struct StorageImageClear { std::array image{}; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp index 5585ea8da5..f2274a6a5e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp @@ -2204,14 +2204,15 @@ std::shared_ptr DepthSurfaceStorage(const Context& context, std: return layeredDepthStorage(context, surface, resource, mip); } -void CopyDepthSample(const Context& context, std::uint64_t depthAddress, std::uint32_t sample, StorageTexture& destination, VkImageView destinationView, VkExtent2D extent) { +void CopyDepthSample(const Context& context, std::uint64_t depthAddress, std::uint32_t samples, std::uint32_t sample, StorageTexture& destination, VkImageView destinationView, VkExtent2D extent) { std::lock_guard lock(surfacesMutex()); - const auto& list = surfaces(); - const auto found = std::find_if(list.rbegin(), list.rend(), [&](const auto& surface) { return surface->context.device == context.device && surface->target.address == depthAddress; }); - Require(found != list.rend(), "DB depth copy from a depth plane no draw rendered"); - Require((*found)->target.extent.width == extent.width && (*found)->target.extent.height == extent.height, "DB depth copy from a depth surface of another size"); - Require(sample < (*found)->target.samples, "DB depth copy of a sample the depth surface does not have"); - (*found)->CopySample(sample, destination, destinationView); + auto* found = planeSurface(context.device, depthAddress); + Require(found != nullptr && found->target.address == depthAddress, "DB depth copy from a depth plane no draw rendered"); + Require(found->target.format == VK_FORMAT_D32_SFLOAT || found->target.format == VK_FORMAT_D32_SFLOAT_S8_UINT, "DB depth copy from a depth surface that is not 32-bit float"); + Require(found->target.samples == samples, "DB depth copy from a depth surface of another sample count"); + Require(found->target.extent.width == extent.width && found->target.extent.height == extent.height, "DB depth copy from a depth surface of another size"); + Require(sample < found->target.samples, "DB depth copy of a sample the depth surface does not have"); + found->CopySample(sample, destination, destinationView); } bool DepthSurfaceStoreBack(const Context& context, const StorageTexture& storage) { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index 80f54edd07..e46bef931f 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -2921,7 +2921,7 @@ std::shared_ptr metadataPassResident(const Context& context, con void RunDepthCopyPass(const Context& context, const DepthCopyPass& pass) { Require(pass.color.tileMode != ColorTileMode::Linear && context.detiler != nullptr, "DB depth copy into a target without a resident image is not implemented"); const auto resident = CachedStorageSurface(context, SurfaceForTarget(pass.color)); - CopyDepthSample(context, pass.depthAddress, pass.sample, *resident, resident->StorageView(0, false), pass.extent); + CopyDepthSample(context, pass.depthAddress, pass.samples, pass.sample, *resident, resident->StorageView(0, false), pass.extent); } void RunColorMetadataPass(const Context& context, const ColorMetadataPass& pass) { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index 76024a6af6..79d2c04415 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -1818,12 +1818,17 @@ std::optional DecodeDepthCopyPass(const QueueState& queue) { Require((read(cx, 0x200) & 0xfu) == 0, "DB depth copy with depth or stencil tests"); Require((read(cx, 0x010) & 3u) == 3u, "DB depth copy from a depth plane that is not Z_32_FLOAT"); Require(ColorWriteMask(cx) == 1u, "DB depth copy into anything but the red channel of color target 0"); + const auto viewRegister = find(cx, 0x002); + const auto view = viewRegister == cx.end() ? 0u : viewRegister->second; + Require((read(cx, 0x010) & 0x000f1000u) == 0 && (view & 0x3c000000u) == 0, "DB depth copy from a mipmapped or partially resident depth plane"); DepthCopyPass pass{}; const auto high = find(cx, 0x01a); pass.depthAddress = (high == cx.end() ? 0ull : static_cast(high->second & 0xffu) << 40u) | (static_cast(read(cx, 0x012)) << 8u); const auto size = read(cx, 0x007); pass.extent = {(size & 0x3fffu) + 1u, ((size >> 16u) & 0x3fffu) + 1u}; pass.samples = 1u << ((read(cx, 0x010) >> 2u) & 3u); + if (pass.samples != 1u) pass.depthAddress &= ~std::uint64_t{65535u}; + if (const auto slice = view & 0x1fffu; slice != 0) pass.depthAddress += static_cast(slice) * DepthSliceBytes(pass.extent, 4u); const auto maskLow = find(cx, 0x30e); const auto maskHigh = find(cx, 0x30f); Require((maskLow == cx.end() || maskLow->second == 0xffffffffu) && (maskHigh == cx.end() || maskHigh->second == 0xffffffffu), "DB depth copy with a sample mask is unsupported"); @@ -1832,6 +1837,7 @@ std::optional DecodeDepthCopyPass(const QueueState& queue) { pass.color = DecodeColorBuffer(cx, 0); Require(pass.color.format == VK_FORMAT_R32_SFLOAT && pass.color.samples == 1 && pass.color.mip == 0 && pass.color.layers == 1, "DB depth copy into a color target that is not a single-sample R32 float"); Require(pass.color.extent.width == pass.extent.width && pass.color.extent.height == pass.extent.height, "DB depth copy into a color target of another size"); + Require(pass.color.cmaskAddress == 0, "DB depth copy into a color target with CMASK fast clears is unsupported"); Require(drawCovers(cx, pass.extent), "DB depth copy over part of the depth surface"); return pass; } diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 3566cc9bd2..3f2dd54765 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -2728,6 +2728,17 @@ void depthCopyPassTests() { queue.context[0x10f] = std::bit_cast(16.0f); expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "over part of the depth surface"); queue.context[0x10f] = std::bit_cast(32.0f); + queue.context[0x010] = 0x80001187; + expectFailure([&] { (void)DecodeDepthCopyPass(queue); }, "mipmapped or partially resident"); + queue.context[0x010] = 0x80000187; + queue.context[0x012] = 0x00001003; + pass = DecodeDepthCopyPass(queue); + Require(pass.has_value() && pass->depthAddress == 0x100000, "a DB depth copy kept the pipe-bank XOR of a two-sample depth plane in its address"); + queue.context[0x012] = 0x00001000; + queue.context[0x002] = 1u | (1u << 13u); + pass = DecodeDepthCopyPass(queue); + Require(pass.has_value() && pass->depthAddress == 0x100000 + AgcDriver::Graphics::DepthSliceBytes({64, 4}, 4u), "a DB depth copy of slice 1 did not address the slice the depth surface registers"); + queue.context[0x002] = 0; Require(DecodeDepthCopyPass(queue).has_value(), "the restored DB depth copy was refused"); queue.context[0x010] = 0x80000183; pass = DecodeDepthCopyPass(queue); From 223e90289b643ba2465e82ffcd7d73c8c6794e26 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Wed, 21 Oct 2026 09:38:47 +0000 Subject: [PATCH 114/523] test(agc): copy multisample depth at a count the device samples The DB depth copy case drew a two-sample depth surface, which lavapipe cannot sample (it samples depth at 1 and 4 samples only). The copy then threw outside the expected-failure checks and the test crashed on exit. Pick 2 samples when the device samples them, else 4, else skip the multisample half, and copy every sample of that count. Co-Authored-By: Claude Opus 5.5 --- .../tests/execution/DepthStorage.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp index 528998474e..4aba6f4a27 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp @@ -341,16 +341,20 @@ void DepthCopyCase(Context context, std::uint8_t* block) { return count; }; std::fill(block, block + BlockBytes, std::uint8_t{0}); - ExpectFailure([&] { copy(2, 0, {Side, Side}); }, "no draw rendered", "a DB depth copy from memory without a depth surface was accepted"); - Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.375f, 0, 0, false, 2}) != VK_NULL_HANDLE, "the two-sample depth surface was not created"); - copy(2, 0, {Side, Side}); - Require(countBlock(0.375f) == Side * Side, "the DB depth copy of sample 0 did not store the cleared depth in every texel of the R32 float target"); - std::fill(block, block + BlockBytes, std::uint8_t{0}); - copy(2, 1, {Side, Side}); - Require(countBlock(0.375f) == Side * Side, "the DB depth copy of sample 1 did not store the cleared depth in every texel of the R32 float target"); - ExpectFailure([&] { copy(2, 2, {Side, Side}); }, "does not have", "a DB depth copy of a third sample of a two-sample surface was accepted"); - ExpectFailure([&] { copy(2, 0, {Side / 2, Side / 2}); }, "of another size", "a DB depth copy of another extent than the depth surface was accepted"); - ClearDepthSurfaces(context.device); + ExpectFailure([&] { copy(1, 0, {Side, Side}); }, "no draw rendered", "a DB depth copy from memory without a depth surface was accepted"); + const auto sampledCounts = context.limits.sampledImageDepthSampleCounts & context.limits.framebufferDepthSampleCounts; + const std::uint32_t samples = (sampledCounts & VK_SAMPLE_COUNT_2_BIT) != 0 ? 2u : (sampledCounts & VK_SAMPLE_COUNT_4_BIT) != 0 ? 4u : 0u; + if (samples != 0) { + Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.375f, 0, 0, false, samples}) != VK_NULL_HANDLE, "the multisample depth surface was not created"); + for (std::uint32_t sample = 0; sample < samples; ++sample) { + std::fill(block, block + BlockBytes, std::uint8_t{0}); + copy(samples, sample, {Side, Side}); + Require(countBlock(0.375f) == Side * Side, "the DB depth copy of sample " + std::to_string(sample) + " of " + std::to_string(samples) + " did not store the cleared depth in every texel of the R32 float target"); + } + ExpectFailure([&] { copy(samples, samples, {Side, Side}); }, "does not have", "a DB depth copy of a sample past the last of a multisample surface was accepted"); + ExpectFailure([&] { copy(samples, 0, {Side / 2, Side / 2}); }, "of another size", "a DB depth copy of another extent than the depth surface was accepted"); + ClearDepthSurfaces(context.device); + } Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.625f, 0, 0, false, 1}) != VK_NULL_HANDLE, "the one-sample depth surface was not created"); std::fill(block, block + BlockBytes, std::uint8_t{0}); copy(1, 0, {Side, Side}); From 9f311ca8c14d0b1b9cd49547253376975fcfcdd1 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Tue, 20 Oct 2026 12:21:00 +0530 Subject: [PATCH 115/523] feat(libSceFreeType): support memory fonts and face cleanup --- core/libs/prx/libSceFreeType/Export.cpp | 72 ++++++++- .../libSceFreeType/include/GuestFreeType.hpp | 5 +- .../libSceFreeType/include/GuestObjects.hpp | 5 + .../prx/libSceFreeType/src/GuestObjects.cpp | 4 +- core/libs/tests/GuestFreeType.cpp | 144 +++++++++++++++++- docs/dev/debt/libSceFreeType.md | 4 +- 6 files changed, 221 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceFreeType/Export.cpp b/core/libs/prx/libSceFreeType/Export.cpp index 0a098e6024..45eaafe81e 100644 --- a/core/libs/prx/libSceFreeType/Export.cpp +++ b/core/libs/prx/libSceFreeType/Export.cpp @@ -15,6 +15,40 @@ using namespace FreeType; namespace { +void FinalizeSlot(void* object) { + auto* host = static_cast(object); + auto* face = static_cast(host->generic.data); + face->rec.glyph = nullptr; + if (face->slot.generic.finalizer) face->slot.generic.finalizer(&face->slot); +} + +void FinalizeSize(void* object) { + auto* host = static_cast(object); + auto* face = static_cast(host->generic.data); + if (face->size.generic.finalizer) face->size.generic.finalizer(&face->size); + face->rec.size = nullptr; +} + +void FinalizeFace(void* object) { + auto* host = static_cast(object); + auto* face = static_cast(host->generic.data); + if (face->rec.generic.finalizer) face->rec.generic.finalizer(&face->rec); +} + +void PublishFace(GuestLibrary* library, std::unique_ptr face, FT_Face host, GuestFaceRec** aface) { + std::unique_ptr hostOwner(host, &FT_Done_Face); + face->library = library; + face->host = host; + SyncFace(*face); + auto* owner = face.get(); + library->faces.push_back(std::move(face)); + host->generic = {owner, FinalizeFace}; + if (host->glyph) host->glyph->generic = {owner, FinalizeSlot}; + if (host->size) host->size->generic = {owner, FinalizeSize}; + *aface = &owner->rec; + hostOwner.release(); +} + bool ReadFontFile(const char* path, std::vector& bytes) { std::filesystem::path hostPath; if (ResolvePathError_nid_no_patch(path, hostPath) != 0) return false; @@ -58,13 +92,19 @@ extern "C" { int APS5_VABI FT_Init_FreeType_nid_postfix(GuestLibrary** alibrary) { if (!alibrary) return FT_Err_Invalid_Argument; - FT_Library host = nullptr; - const FT_Error error = FT_Init_FreeType(&host); + auto library = std::make_unique(); + const FT_Error error = FT_Init_FreeType(&library->host); if (error != 0) return error; - *alibrary = new GuestLibrary{host}; + *alibrary = library.release(); return 0; } +int APS5_VABI FT_Done_FreeType_nid_postfix(GuestLibrary* library) { + const FT_Error error = FT_Done_FreeType(library ? library->host : nullptr); + if (error == 0) delete library; + return error; +} + int APS5_VABI FT_New_Face_nid_postfix(GuestLibrary* library, const char* filepathname, std::int64_t face_index, GuestFaceRec** aface) { if (!filepathname) return FT_Err_Invalid_Argument; const std::int64_t faceIndex = OpenFaceIndex(face_index); @@ -77,13 +117,31 @@ int APS5_VABI FT_New_Face_nid_postfix(GuestLibrary* library, const char* filepat FT_Face host = nullptr; const FT_Error error = FT_New_Memory_Face(library->host, face->bytes.data(), static_cast(face->bytes.size()), static_cast(faceIndex), aface ? &host : nullptr); if (error != 0 || !aface) return error; - face->library = library; - face->host = host; - SyncFace(*face); - *aface = &face.release()->rec; + PublishFace(library, std::move(face), host, aface); return 0; } +int APS5_VABI FT_New_Memory_Face_nid_postfix(GuestLibrary* library, const unsigned char* fileBase, std::int64_t fileSize, std::int64_t faceIndex, GuestFaceRec** aface) { + if (!fileBase) return FT_Err_Invalid_Argument; + if (!library) return FT_Err_Invalid_Library_Handle; + if (fileSize < std::numeric_limits::min() || fileSize > std::numeric_limits::max()) { + throw std::runtime_error("FT_New_Memory_Face: a font buffer size outside the host FT_Long range is not implemented"); + } + auto face = std::make_unique(); + FT_Face host = nullptr; + const FT_Error error = FT_New_Memory_Face(library->host, fileBase, static_cast(fileSize), static_cast(OpenFaceIndex(faceIndex)), aface ? &host : nullptr); + if (error != 0 || !aface) return error; + PublishFace(library, std::move(face), host, aface); + return 0; +} + +int APS5_VABI FT_Done_Face_nid_postfix(GuestFaceRec* face) { + GuestFace* owner = FaceOf(face); + const FT_Error error = FT_Done_Face(owner ? owner->host : nullptr); + if (error == 0) owner->library->faces.remove_if([owner](const auto& entry) { return entry.get() == owner; }); + return error; +} + int APS5_VABI FT_Set_Pixel_Sizes_nid_postfix(GuestFaceRec* face, std::uint32_t pixel_width, std::uint32_t pixel_height) { GuestFace* owner = FaceOf(face); const FT_Error error = FT_Set_Pixel_Sizes(owner ? owner->host : nullptr, pixel_width, pixel_height); diff --git a/core/libs/prx/libSceFreeType/include/GuestFreeType.hpp b/core/libs/prx/libSceFreeType/include/GuestFreeType.hpp index 2cf157b746..1a2a074378 100644 --- a/core/libs/prx/libSceFreeType/include/GuestFreeType.hpp +++ b/core/libs/prx/libSceFreeType/include/GuestFreeType.hpp @@ -1,6 +1,8 @@ #ifndef CORE_LIBS_PRX_LIBSCEFREETYPE_INCLUDE_GUESTFREETYPE_HPP #define CORE_LIBS_PRX_LIBSCEFREETYPE_INCLUDE_GUESTFREETYPE_HPP +#include "prx/libc/include/general/VabiMacros.hpp" + #include #include @@ -8,7 +10,7 @@ namespace FreeType { struct GuestGeneric { void* data; - void* finalizer; + void (APS5_VABI *finalizer)(void*); }; struct GuestVector { @@ -147,6 +149,7 @@ struct GuestFaceRec { void* internal; }; +static_assert(sizeof(GuestGeneric) == 16 && offsetof(GuestGeneric, finalizer) == 8); static_assert(sizeof(GuestBitmapSize) == 32 && offsetof(GuestBitmapSize, size) == 8); static_assert(sizeof(GuestCharMapRec) == 16 && offsetof(GuestCharMapRec, platform_id) == 12); static_assert(sizeof(GuestSizeMetrics) == 56 && offsetof(GuestSizeMetrics, x_scale) == 8); diff --git a/core/libs/prx/libSceFreeType/include/GuestObjects.hpp b/core/libs/prx/libSceFreeType/include/GuestObjects.hpp index 9abdae1f81..d237147b89 100644 --- a/core/libs/prx/libSceFreeType/include/GuestObjects.hpp +++ b/core/libs/prx/libSceFreeType/include/GuestObjects.hpp @@ -6,12 +6,17 @@ #include #include FT_FREETYPE_H +#include +#include #include namespace FreeType { +struct GuestFace; + struct GuestLibrary { FT_Library host = nullptr; + std::list> faces; }; struct GuestFace { diff --git a/core/libs/prx/libSceFreeType/src/GuestObjects.cpp b/core/libs/prx/libSceFreeType/src/GuestObjects.cpp index 5319615c39..79662344d2 100644 --- a/core/libs/prx/libSceFreeType/src/GuestObjects.cpp +++ b/core/libs/prx/libSceFreeType/src/GuestObjects.cpp @@ -49,8 +49,8 @@ void SyncFace(GuestFace& face) { rec.max_advance_height = host->max_advance_height; rec.underline_position = host->underline_position; rec.underline_thickness = host->underline_thickness; - rec.glyph = &face.slot; - rec.size = &face.size; + rec.glyph = host->glyph ? &face.slot : nullptr; + rec.size = host->size ? &face.size : nullptr; rec.internal = &face; SyncSize(face); SyncSlot(face); diff --git a/core/libs/tests/GuestFreeType.cpp b/core/libs/tests/GuestFreeType.cpp index bfb51cb340..ea26c80654 100644 --- a/core/libs/tests/GuestFreeType.cpp +++ b/core/libs/tests/GuestFreeType.cpp @@ -23,7 +23,10 @@ using namespace FreeType; extern "C" { int APS5_VABI FT_Init_FreeType_nid_postfix(void**); +int APS5_VABI FT_Done_FreeType_nid_postfix(void*); int APS5_VABI FT_New_Face_nid_postfix(void*, const char*, std::int64_t, GuestFaceRec**); +int APS5_VABI FT_New_Memory_Face_nid_postfix(void*, const unsigned char*, std::int64_t, std::int64_t, GuestFaceRec**); +int APS5_VABI FT_Done_Face_nid_postfix(GuestFaceRec*); int APS5_VABI FT_Set_Pixel_Sizes_nid_postfix(GuestFaceRec*, std::uint32_t, std::uint32_t); std::uint32_t APS5_VABI FT_Get_Char_Index_nid_postfix(GuestFaceRec*, std::uint64_t); int APS5_VABI FT_Load_Glyph_nid_postfix(GuestFaceRec*, std::uint32_t, std::int32_t); @@ -84,6 +87,58 @@ static void SlotMatches(const GuestGlyphSlotRec* slot, FT_GlyphSlot host) { Require(slot->lsb_delta == host->lsb_delta && slot->rsb_delta == host->rsb_delta && slot->control_len == host->control_len); } +struct Finalizers { + std::vector calls; +}; + +static void NativeSlotFinalizer(void* object) { + auto* slot = static_cast(object); + Require(slot->face->glyph == nullptr); + static_cast(slot->generic.data)->calls.push_back(1); +} + +static void NativeSizeFinalizer(void* object) { + auto* size = static_cast(object); + Require(size->face->size == size); + static_cast(size->generic.data)->calls.push_back(2); +} + +static void NativeFaceFinalizer(void* object) { + auto* face = static_cast(object); + Require(face->glyph == nullptr && face->size == nullptr); + static_cast(face->generic.data)->calls.push_back(3); +} + +static void APS5_VABI GuestSlotFinalizer(void* object) { + auto* slot = static_cast(object); + Require(static_cast(slot->face)->glyph == nullptr); + static_cast(slot->generic.data)->calls.push_back(1); +} + +static void APS5_VABI GuestSizeFinalizer(void* object) { + auto* size = static_cast(object); + Require(static_cast(size->face)->size == size); + static_cast(size->generic.data)->calls.push_back(2); +} + +static void APS5_VABI GuestFaceFinalizer(void* object) { + auto* face = static_cast(object); + Require(face->glyph == nullptr && face->size == nullptr); + static_cast(face->generic.data)->calls.push_back(3); +} + +static void SetFinalizers(FT_Face face, Finalizers& events) { + face->generic = {&events, NativeFaceFinalizer}; + face->glyph->generic = {&events, NativeSlotFinalizer}; + face->size->generic = {&events, NativeSizeFinalizer}; +} + +static void SetFinalizers(GuestFaceRec* face, Finalizers& events) { + face->generic = {&events, GuestFaceFinalizer}; + face->glyph->generic = {&events, GuestSlotFinalizer}; + face->size->generic = {&events, GuestSizeFinalizer}; +} + int main() { const auto root = std::filesystem::temp_directory_path() / ("anyps5_guest_freetype-" + std::to_string(std::random_device{}())); std::filesystem::create_directories(root / "app0"); @@ -106,6 +161,8 @@ int main() { Require(FT_Init_FreeType_nid_postfix(nullptr) == FT_Err_Invalid_Argument); void* library = nullptr; Require(FT_Init_FreeType_nid_postfix(&library) == 0 && library != nullptr); + Require(FT_Done_FreeType_nid_postfix(nullptr) == FT_Err_Invalid_Library_Handle); + Require(FT_Done_Face_nid_postfix(nullptr) == FT_Err_Invalid_Face_Handle); GuestFaceRec* face = nullptr; Require(FT_New_Face_nid_postfix(library, nullptr, 0, &face) == FT_Err_Invalid_Argument); @@ -120,6 +177,54 @@ int main() { Require(FT_New_Face_nid_postfix(library, "/app0/font.ttf", 0, &face) == 0 && face != nullptr && face != masked); FaceMatches(face, host, library); + const auto fontSize = static_cast(font.size()); + GuestFaceRec* memoryFace = nullptr; + Require(FT_New_Memory_Face_nid_postfix(library, nullptr, fontSize, 0, &memoryFace) == FT_Err_Invalid_Argument); + Require(FT_New_Memory_Face_nid_postfix(nullptr, font.data(), fontSize, 0, &memoryFace) == FT_Err_Invalid_Library_Handle); + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), fontSize, 0, nullptr) == FT_Err_Invalid_Argument); + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), fontSize, -1, nullptr) == 0); + FT_Face nativeProbe = nullptr; + GuestFaceRec* probe = nullptr; + Require(FT_New_Memory_Face(hostLibrary, font.data(), static_cast(font.size()), -1, &nativeProbe) == 0); + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), fontSize, -1, &probe) == 0 && probe != nullptr); + Require(probe->num_faces == nativeProbe->num_faces && probe->face_index == nativeProbe->face_index); + Require((probe->glyph == nullptr) == (nativeProbe->glyph == nullptr) && (probe->size == nullptr) == (nativeProbe->size == nullptr)); + Finalizers nativeProbeEvents; + Finalizers guestProbeEvents; + nativeProbe->generic = {&nativeProbeEvents, NativeFaceFinalizer}; + probe->generic = {&guestProbeEvents, GuestFaceFinalizer}; + Require(FT_Done_Face(nativeProbe) == 0 && FT_Done_Face_nid_postfix(probe) == 0); + Require(nativeProbeEvents.calls == std::vector({3}) && guestProbeEvents.calls == nativeProbeEvents.calls); + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), fontSize, 1, &memoryFace) == secondFace && memoryFace == nullptr); + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), fontSize, 0x80000001ll, &memoryFace) == secondFace && memoryFace == nullptr); + FT_Face invalidHost = nullptr; + for (const FT_Long length : {0L, 4L}) { + const FT_Error expected = FT_New_Memory_Face(hostLibrary, font.data(), length, 0, &invalidHost); + Require(expected != 0 && invalidHost == nullptr); + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), length, 0, &memoryFace) == expected && memoryFace == nullptr); + } + const unsigned char invalidFont[32]{}; + const FT_Error invalidFormat = FT_New_Memory_Face(hostLibrary, invalidFont, sizeof(invalidFont), 0, &invalidHost); + Require(invalidFormat != 0 && invalidHost == nullptr); + Require(FT_New_Memory_Face_nid_postfix(library, invalidFont, sizeof(invalidFont), 0, &memoryFace) == invalidFormat && memoryFace == nullptr); + if constexpr (sizeof(FT_Long) < sizeof(std::int64_t)) { + for (const std::int64_t length : {0x80000000ll, -0x80000001ll}) { + Require(ThrowsWith([&] { FT_New_Memory_Face_nid_postfix(library, font.data(), length, 0, &memoryFace); }, + "FT_New_Memory_Face: a font buffer size outside the host FT_Long range is not implemented")); + Require(memoryFace == nullptr); + } + } + Require(FT_New_Memory_Face_nid_postfix(library, font.data(), fontSize, 0x180000000ll, &memoryFace) == 0 && memoryFace != nullptr); + FaceMatches(memoryFace, host, library); + Require(memoryFace != face && memoryFace->glyph != face->glyph); + Require(FT_Set_Pixel_Sizes_nid_postfix(memoryFace, 0, 100) == 0 && FT_Set_Pixel_Sizes(host, 0, 100) == 0); + Require(FT_Get_Char_Index_nid_postfix(memoryFace, 'A') == 1); + Require(FT_Load_Glyph_nid_postfix(memoryFace, 1, FT_LOAD_DEFAULT) == 0 && FT_Load_Glyph(host, 1, FT_LOAD_DEFAULT) == 0); + SlotMatches(memoryFace->glyph, host->glyph); + Require(FT_Render_Glyph_nid_postfix(memoryFace->glyph, FT_RENDER_MODE_NORMAL) == 0 && FT_Render_Glyph(host->glyph, FT_RENDER_MODE_NORMAL) == 0); + SlotMatches(memoryFace->glyph, host->glyph); + Require(face->size->metrics.y_ppem == 0); + Require(FT_Set_Pixel_Sizes_nid_postfix(nullptr, 0, 100) == FT_Err_Invalid_Face_Handle); Require(FT_Set_Pixel_Sizes_nid_postfix(face, 0, 100) == 0 && FT_Set_Pixel_Sizes(host, 0, 100) == 0); const auto& metrics = host->size->metrics; @@ -154,8 +259,43 @@ int main() { slot->outline.points = original; Require(FT_Render_Glyph_nid_postfix(slot, FT_RENDER_MODE_NORMAL) == 0 && slot->bitmap_left == 10); - FT_Done_Face(host); - FT_Done_FreeType(hostLibrary); + Finalizers nativeDone; + Finalizers guestDone; + SetFinalizers(host, nativeDone); + SetFinalizers(face, guestDone); + Require(FT_Done_Face(host) == 0); + Require(FT_Done_Face_nid_postfix(face) == 0); + Require(nativeDone.calls == std::vector({1, 2, 3}) && guestDone.calls == nativeDone.calls); + Require(FT_Get_Char_Index_nid_postfix(masked, 'A') == 1); + + FT_Face nativeFirst = nullptr; + FT_Face nativeSecond = nullptr; + Require(FT_New_Memory_Face(hostLibrary, font.data(), static_cast(font.size()), 0, &nativeFirst) == 0); + Require(FT_New_Memory_Face(hostLibrary, font.data(), static_cast(font.size()), 0, &nativeSecond) == 0); + Finalizers nativeFirstEvents; + Finalizers nativeSecondEvents; + Finalizers guestFirstEvents; + Finalizers guestSecondEvents; + SetFinalizers(nativeFirst, nativeFirstEvents); + SetFinalizers(nativeSecond, nativeSecondEvents); + SetFinalizers(masked, guestFirstEvents); + SetFinalizers(memoryFace, guestSecondEvents); + Require(FT_Done_FreeType(hostLibrary) == 0); + + void* independent = nullptr; + GuestFaceRec* independentFace = nullptr; + Require(FT_Init_FreeType_nid_postfix(&independent) == 0); + Require(FT_New_Memory_Face_nid_postfix(independent, font.data(), fontSize, 0, &independentFace) == 0); + Finalizers independentEvents; + SetFinalizers(independentFace, independentEvents); + Require(FT_Done_FreeType_nid_postfix(library) == 0); + Require(guestFirstEvents.calls == nativeFirstEvents.calls && guestSecondEvents.calls == nativeSecondEvents.calls); + Require(guestFirstEvents.calls == nativeDone.calls && independentEvents.calls.empty()); + Require(FT_Get_Char_Index_nid_postfix(independentFace, 'A') == 1); + Require(FT_Done_FreeType_nid_postfix(independent) == 0 && independentEvents.calls == nativeDone.calls); + + void* empty = nullptr; + Require(FT_Init_FreeType_nid_postfix(&empty) == 0 && FT_Done_FreeType_nid_postfix(empty) == 0); std::filesystem::current_path(previous); std::error_code ignored; std::filesystem::remove_all(root, ignored); diff --git a/docs/dev/debt/libSceFreeType.md b/docs/dev/debt/libSceFreeType.md index fa594755cb..19872daed2 100644 --- a/docs/dev/debt/libSceFreeType.md +++ b/docs/dev/debt/libSceFreeType.md @@ -6,6 +6,8 @@ - [FT_Init_FreeType, FT_New_Face, FT_Set_Pixel_Sizes, FT_Get_Char_Index, FT_Load_Glyph, FT_Render_Glyph](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - the PS4 library is taken to be FreeType 2 built for LP64: the face, size, charmap, glyph slot, bitmap and outline layouts follow the public headers of the bundled FreeType 2.13.3 with a 64-bit `long`, which matches the offsets the OpenOrbis samples read (`face->glyph` at 0x98, `slot->advance` at 0x80, `slot->bitmap` at 0x98, `slot->bitmap_left` at 0xc0). Which FreeType version and options the console library is built with is unknown, so the behaviour and error codes are those of FreeType 2.13.3. The OpenOrbis font, keyboard, threading, system and SDL2 samples import these six functions +- [FT_New_Memory_Face, FT_Done_Face and FT_Done_FreeType](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - console signatures, errors and generic-finalizer ordering are unverified; they follow the bundled [FreeType 2.13.3](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/include/freetype/freetype.h) with LP64 guest arguments. A font buffer size outside the host `FT_Long` range throws, excluding buffers of 2 GiB or more on Windows. Caller-owned font bytes must remain valid until the face is freed. + ### Functional -- [libSceFreeType](../../../core/libs/prx/libSceFreeType/src/GuestObjects.cpp) (libSceFreeType) - the guest reads copies of the bundled FreeType's face, size and glyph slot, updated after each call that changes them. Their private fields (`driver`, `memory`, `stream`, `sizes_list`, `extensions`) are null, and `subglyphs` is null whatever `num_subglyphs` reports. The outline points are copied as 64-bit coordinates; changes the guest makes to them and to the outline flags are written back before `FT_Render_Glyph`, while replacing the outline arrays, or a coordinate the host `FT_Pos` cannot hold, throws. `FT_New_Face` resolves the guest path and reads the whole file into memory; on Windows, where FreeType's `long` is 32 bits, a font of 2 GiB or more throws. Libraries and faces are never freed, since `FT_Done_Face` and `FT_Done_FreeType` are not exported. The bundled FreeType's own functions are kept out of the exports with `--exclude-libs` on Windows and Linux only; on macOS they stay exported, so a guest import of another FreeType function could resolve to them instead of failing at start-up +- [libSceFreeType](../../../core/libs/prx/libSceFreeType/src/GuestObjects.cpp) (libSceFreeType) - the guest reads copies of the bundled FreeType's face, size and glyph slot, updated after each call that changes them. Their private fields (`driver`, `memory`, `stream`, `sizes_list`, `extensions`) are null, and `subglyphs` is null whatever `num_subglyphs` reports. The outline points are copied as 64-bit coordinates; changes the guest makes to them and to the outline flags are written back before `FT_Render_Glyph`, while replacing the outline arrays, or a coordinate the host `FT_Pos` cannot hold, throws. `FT_New_Face` resolves the guest path and reads the whole file into memory; on Windows, where FreeType's `long` is 32 bits, a font of 2 GiB or more throws. The bundled FreeType's own functions are kept out of the exports with `--exclude-libs` on Windows and Linux only; on macOS they stay exported, so a guest import of another FreeType function could resolve to them instead of failing at start-up From 303f15c0788362efafc4194de7ca7975e0e1fa37 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Wed, 21 Oct 2026 15:19:49 +0530 Subject: [PATCH 116/523] docs(libSceFreeType): clarify Windows memory face limits --- docs/dev/debt/libSceFreeType.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libSceFreeType.md b/docs/dev/debt/libSceFreeType.md index 19872daed2..6d4f98438d 100644 --- a/docs/dev/debt/libSceFreeType.md +++ b/docs/dev/debt/libSceFreeType.md @@ -6,8 +6,10 @@ - [FT_Init_FreeType, FT_New_Face, FT_Set_Pixel_Sizes, FT_Get_Char_Index, FT_Load_Glyph, FT_Render_Glyph](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - the PS4 library is taken to be FreeType 2 built for LP64: the face, size, charmap, glyph slot, bitmap and outline layouts follow the public headers of the bundled FreeType 2.13.3 with a 64-bit `long`, which matches the offsets the OpenOrbis samples read (`face->glyph` at 0x98, `slot->advance` at 0x80, `slot->bitmap` at 0x98, `slot->bitmap_left` at 0xc0). Which FreeType version and options the console library is built with is unknown, so the behaviour and error codes are those of FreeType 2.13.3. The OpenOrbis font, keyboard, threading, system and SDL2 samples import these six functions -- [FT_New_Memory_Face, FT_Done_Face and FT_Done_FreeType](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - console signatures, errors and generic-finalizer ordering are unverified; they follow the bundled [FreeType 2.13.3](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/include/freetype/freetype.h) with LP64 guest arguments. A font buffer size outside the host `FT_Long` range throws, excluding buffers of 2 GiB or more on Windows. Caller-owned font bytes must remain valid until the face is freed. +- [FT_New_Memory_Face, FT_Done_Face and FT_Done_FreeType](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - console signatures, errors and generic-finalizer ordering are unverified; they follow the bundled [FreeType 2.13.3](https://github.com/freetype/freetype/blob/42608f77f20749dd6ddc9e0536788eaad70ea4b5/include/freetype/freetype.h) with LP64 guest arguments. Caller-owned font bytes must remain valid until the face is freed. ### Functional -- [libSceFreeType](../../../core/libs/prx/libSceFreeType/src/GuestObjects.cpp) (libSceFreeType) - the guest reads copies of the bundled FreeType's face, size and glyph slot, updated after each call that changes them. Their private fields (`driver`, `memory`, `stream`, `sizes_list`, `extensions`) are null, and `subglyphs` is null whatever `num_subglyphs` reports. The outline points are copied as 64-bit coordinates; changes the guest makes to them and to the outline flags are written back before `FT_Render_Glyph`, while replacing the outline arrays, or a coordinate the host `FT_Pos` cannot hold, throws. `FT_New_Face` resolves the guest path and reads the whole file into memory; on Windows, where FreeType's `long` is 32 bits, a font of 2 GiB or more throws. The bundled FreeType's own functions are kept out of the exports with `--exclude-libs` on Windows and Linux only; on macOS they stay exported, so a guest import of another FreeType function could resolve to them instead of failing at start-up +- [libSceFreeType](../../../core/libs/prx/libSceFreeType/src/GuestObjects.cpp) (libSceFreeType) - private fields (`driver`, `memory`, `stream`, `sizes_list`, `extensions`) and `subglyphs` stay null. Face, size and glyph-slot records are copies. Only edits to outline points and flags are written back before `FT_Render_Glyph`; replacing arrays or exceeding the host `FT_Pos` range throws. `FT_New_Face` reads the whole file into memory and throws for files of 2 GiB or more on Windows. On macOS, native FreeType symbols remain exported and could resolve guest imports without adaptation; Windows and Linux hide them with `--exclude-libs`. + +- [FT_New_Memory_Face](../../../core/libs/prx/libSceFreeType/Export.cpp) (libSceFreeType) - on Windows, the host FT_Long is 32 bits, so buffer sizes of 2 GiB or more or below -2 GiB throw before the font buffer is read From e8b9eb93234ae33214c204523e2ada920626476f Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Wed, 21 Oct 2026 12:00:17 +0200 Subject: [PATCH 117/523] test(libc): keep the abort-race flag of the call trace test alive until the process aborts The detached thread of the abort-race case reads a flag that is a local of the enclosing block, which ends before main calls std::abort(). Under AddressSanitizer that read is reported as stack-use-after-scope. Make the flag static. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/tests/GuestCallTrace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/tests/GuestCallTrace.cpp b/core/libs/tests/GuestCallTrace.cpp index 4b7e2091e9..6fc77ae658 100644 --- a/core/libs/tests/GuestCallTrace.cpp +++ b/core/libs/tests/GuestCallTrace.cpp @@ -106,8 +106,8 @@ int main(int argc, char** argv) { while (!locked.load()) std::this_thread::yield(); if (std::strcmp(argv[1], "abort-race") == 0) { for (int call = 0; call < 100; ++call) Require(first(1, 2, 3, 4, 5, 6, 7, 8) == 204); - std::atomic go{false}; - std::thread([&go] { + static std::atomic go{false}; + std::thread([] { while (!go.load()) { } std::abort(); From f57bbc936a7de6835e5ab8ea8dff42e776e1700f Mon Sep 17 00:00:00 2001 From: milescoviello Date: Wed, 21 Oct 2026 08:28:24 +0000 Subject: [PATCH 118/523] perf(agc): store only the depth plane units a flush overlaps A read of memory inside a stale D32 Z_X depth plane (FlushDepthPlanes) stored the whole plane: a copy of the depth image into the mirror, MarkWholeWritten and a write-back of every byte. In Super Meat Boy 3D (#8840) a read-only buffer ending about 3 KiB inside a stale 3840x2160 plane makes ShaderResources::Revalidate flush ~1000 times per 5 s, each one moving 31.9 MiB, bound by transfer bandwidth. A depth surface now tracks its plane as 64 KiB units, each one 128x128 tile of the mirror's block units: a stale bit and a write generation per unit replace memoryStale and the plane-wide syncedGeneration. - FlushDepthPlanes stores only the stale units the range overlaps: the depth to mirror copy takes one region per unit rectangle (UnitRects, from the mirror's slice windows), the mirror marks just those units written (MarkUnitsWritten) and the write-back covers their hull. The other units stay stale and the plane stays registered. Stale bits are cleared only after the copy was recorded, and the stored units' generations advance only when FlushPending stored them. A depth fast clear still pending at the store rewrites the whole image, so it makes every unit stale first. - SyncFromMemory copies from the mirror only the units the guest wrote since their own generation (one ChangedBlocks scan; untracked memory counts as unwritten, as WrittenSince did), so a guest write into one unit no longer replaces GPU depth that is still unstored in the others. - Flushes followed by a retire keep the whole-plane store: the color target flush in Draw and a written storage view (DepthPlaneScope::Whole). The retire of an overwritten surface in DepthSurfaceTexture first stores its remaining stale units the guest did not write. - A mirror without block units (APS5_NO_BLOCK_TRACKING, unaligned base) falls back to the whole-plane copy, and APS5_DEPTH_PLANE_WHOLE_STORE=1 tracks the plane as one unit, the previous behaviour. - DepthPlaneStoreCounts reports stores, units and bytes stored. Co-Authored-By: Claude Opus 5.5 --- .../Graphics/include/DepthSurface.hpp | 9 +- .../Graphics/include/Texture.hpp | 4 + .../Graphics/src/DepthSurface.cpp | 305 +++++++++++++++--- .../prx/libSceAgcDriver/Graphics/src/Draw.cpp | 2 +- .../Graphics/src/ShaderResources.cpp | 2 +- .../libSceAgcDriver/Graphics/src/Texture.cpp | 42 +++ docs/dev/ENVIRONMENT.md | 1 + docs/dev/debt/libSceAgcDriver.md | 2 +- 8 files changed, 314 insertions(+), 53 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp index 521c60721b..c17934d116 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp @@ -20,8 +20,15 @@ class StorageTexture; VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target, bool depthWritten = true); SampleOrderTarget DepthSurfaceOrderTarget(const Context& context, const DepthTarget& target); DepthSampleLocations DepthSurfaceSampleLocations(const Context& context, const DepthTarget& target); -std::vector> FlushDepthPlanes(std::uint64_t address, std::size_t bytes); +enum class DepthPlaneScope { Overlapped, Whole }; +std::vector> FlushDepthPlanes(std::uint64_t address, std::size_t bytes, DepthPlaneScope scope = DepthPlaneScope::Overlapped); bool DepthPlanesStale(); +struct DepthPlaneStoreStatistics { + std::uint64_t stores = 0; + std::uint64_t units = 0; + std::uint64_t bytes = 0; +}; +DepthPlaneStoreStatistics DepthPlaneStoreCounts(); bool SampleLocationsCompatibleDepth(const Context& context, VkFormat format, std::uint32_t samples); std::uint64_t DepthSurfacesSerial(); std::uint64_t DepthSurfacesReleased(); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp index ae4f6dcaec..d79eecb2e6 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp @@ -198,6 +198,10 @@ public: // Deferred write-back (APS5_EAGER_WRITEBACK=1 stores at once instead). void MarkDirty(); void MarkWholeWritten(); + void MarkUnitsWritten(const std::vector& units); + std::vector UnitRects(const std::vector& units) const; + bool BlockUnits() const { return blockUnits; } + std::uint32_t TrackedUnits() const { return trackedLayers; } void MarkSliceDirty(std::uint32_t slice, std::uint32_t count = 1); void Flush(); bool Dirty() const; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp index 3c31c3ab57..8833fef947 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -150,6 +151,21 @@ DepthClearCounters& depthClearCounters() { return counters; } +struct DepthPlaneStoreCounters { + std::atomic stores{0}; + std::atomic units{0}; + std::atomic bytes{0}; +}; + +DepthPlaneStoreCounters& depthPlaneStoreCounters() { + static DepthPlaneStoreCounters counters; + return counters; +} + +bool anyUnit(const std::vector& units) { + return std::find(units.begin(), units.end(), true) != units.end(); +} + thread_local DepthPlaneOverwrite* currentOverwrite = nullptr; std::optional decodeOverwrite(const StorageImageClear& clear) { @@ -698,42 +714,166 @@ public: else Recorder::CountBarriers(Recorder::CommandClass::Copy, 3); } - std::shared_ptr StoreDepthPlane(const GuestTextureResource& resource) { - ApplyFastClear(); + std::shared_ptr Mirror(const GuestTextureResource& resource) { if (mirrorPlane.storage == nullptr) { mirrorPlane.storage = StorageTexture::Create(context, *context.detiler, resource, 0); ++mirrorEpoch; } - copyPlane(mirrorPlane, mirrorPlane.storage, false, true, true); return mirrorPlane.storage; } - bool SyncFromMemory(const GuestTextureResource& resource) { + bool StoresUnits(const StorageTexture& mirror) const { + return mirror.BlockUnits() && mirror.TrackedUnits() == staleUnits.size(); + } + + std::shared_ptr StoreDepthPlane(const GuestTextureResource& resource, std::vector& units) { + ApplyFastClear(); + auto mirror = Mirror(resource); + auto& counters = depthPlaneStoreCounters(); + ++counters.stores; + const auto planeBytes = mirror->GuestBytes(); + if (!StoresUnits(*mirror)) { + units.assign(units.size(), true); + copyPlane(mirrorPlane, mirror, false, true, true); + mirror->MarkWholeWritten(); + counters.units += units.size(); + counters.bytes += planeBytes; + return mirror; + } + const auto rects = mirror->UnitRects(units); + copyPlane(mirrorPlane, mirror, false, true, true, &rects); + mirror->MarkUnitsWritten(units); + for (std::size_t unit = 0; unit < units.size(); ++unit) { + if (!units[unit]) continue; + ++counters.units; + counters.bytes += std::min(unitBytes, planeBytes - unit * unitBytes); + } + return mirror; + } + + void SizeUnits(std::uint64_t planeBytes) { + static const bool whole = std::getenv("APS5_DEPTH_PLANE_WHOLE_STORE") != nullptr; + const std::uint64_t bytes = !whole && target.address % 65536u == 0 ? std::uint64_t{65536} : std::max(planeBytes, 1u); + const auto count = static_cast(std::max((planeBytes + bytes - 1u) / bytes, 1u)); + if (unitBytes == bytes && staleUnits.size() == count) return; + unitBytes = bytes; + staleUnits.assign(count, false); + staleCount = 0; + unitGeneration.assign(count, 0); + } + + bool AnyStale() const { + return staleCount != 0; + } + + void MarkAllStale(std::uint64_t planeBytes) { + SizeUnits(planeBytes); + staleUnits.assign(staleUnits.size(), true); + staleCount = staleUnits.size(); + } + + void ClearStale() { + staleUnits.assign(staleUnits.size(), false); + staleCount = 0; + } + + void ClearStale(const std::vector& units) { + for (std::size_t unit = 0; unit < units.size() && unit < staleUnits.size(); ++unit) { + if (!units[unit] || !staleUnits[unit]) continue; + staleUnits[unit] = false; + --staleCount; + } + } + + std::vector StaleUnitsIn(std::uint64_t planeBytes, std::uint64_t begin, std::uint64_t end) const { + std::vector units(staleUnits.size(), false); + const auto plane = target.address; + if (units.empty() || unitBytes == 0 || begin >= end || begin >= plane + planeBytes || plane >= end) return units; + const auto from = std::max(begin, plane) - plane; + const auto to = std::min(end, plane + planeBytes) - plane; + const auto last = std::min((to - 1u) / unitBytes, units.size() - 1u); + for (auto unit = from / unitBytes; unit <= last; ++unit) units[static_cast(unit)] = staleUnits[static_cast(unit)]; + return units; + } + + std::pair UnitHull(const std::vector& units, std::uint64_t planeBytes) const { + std::uint64_t first = planeBytes; + std::uint64_t last = 0; + for (std::size_t unit = 0; unit < units.size(); ++unit) { + if (!units[unit]) continue; + first = std::min(first, unit * unitBytes); + last = std::max(last, std::min(planeBytes, (unit + 1u) * unitBytes)); + } + return {target.address + first, target.address + std::max(first, last)}; + } + + void NoteStored(const std::vector& units, std::uint64_t generation) { + for (std::size_t unit = 0; unit < units.size() && unit < unitGeneration.size(); ++unit) { + if (units[unit]) unitGeneration[unit] = generation; + } + } + + std::vector WrittenUnits(const GuestTextureResource& resource, std::uint64_t& generation) { const auto bytes = static_cast(DescribeSurface(resource).guestBytes); + SizeUnits(bytes); GuestMemory::CollectWrites(resource.baseAddress, bytes); - if (syncedGeneration == 0 || (pendingClear & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 || !GuestMemory::WrittenSince(resource.baseAddress, bytes, syncedGeneration)) { - syncedGeneration = GuestMemory::TrackerGeneration(); - return false; + generation = GuestMemory::TrackerGeneration(); + std::vector changed(unitGeneration.size(), false); + if (unitGeneration.size() == 1u) { + changed[0] = GuestMemory::WrittenSince(resource.baseAddress, bytes, unitGeneration[0]); + return changed; } - memoryStale = false; - if (mirrorPlane.storage == nullptr) { - mirrorPlane.storage = StorageTexture::Create(context, *context.detiler, resource, 0); - ++mirrorEpoch; + const auto oldest = *std::min_element(unitGeneration.begin(), unitGeneration.end()); + if (oldest != 0u && !GuestMemory::WrittenSince(resource.baseAddress, bytes, oldest)) return changed; + std::vector stamped(unitGeneration.size(), GuestMemory::BlockUnchanged); + if (!GuestMemory::ChangedBlocks(resource.baseAddress, bytes, unitGeneration, stamped)) return changed; + for (std::size_t unit = 0; unit < changed.size(); ++unit) changed[unit] = unitGeneration[unit] != 0u && stamped[unit] == GuestMemory::BlockWritten; + return changed; + } + + void SyncFromMemory(const GuestTextureResource& resource) { + std::uint64_t generation = 0; + const auto changed = WrittenUnits(resource, generation); + if ((pendingClear & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 || !anyUnit(changed)) { + unitGeneration.assign(unitGeneration.size(), generation); + return; + } + ClearStale(changed); + { + struct Syncing { + bool& flag; + ~Syncing() { flag = false; } + } guard{syncing}; + syncing = true; + if (mirrorPlane.storage == nullptr) { + mirrorPlane.storage = StorageTexture::Create(context, *context.detiler, resource, 0); + ++mirrorEpoch; + } else { + static_cast(mirrorPlane.storage->Refresh()); + } + } + unitGeneration.assign(unitGeneration.size(), generation); + const auto mirror = mirrorPlane.storage; + if (StoresUnits(*mirror)) { + const auto rects = mirror->UnitRects(changed); + copyPlane(mirrorPlane, mirror, false, false, true, &rects); + } else { + ClearStale(); + copyPlane(mirrorPlane, mirror, false, false, true); } - else static_cast(mirrorPlane.storage->Refresh()); - syncedGeneration = GuestMemory::TrackerGeneration(); - copyPlane(mirrorPlane, mirrorPlane.storage, false, false, true); written = ++depthWrites; - return true; } void DropMirror() { mirrorPlane = {}; } - bool memoryStale = false; + std::vector staleUnits; + std::size_t staleCount = 0; + std::vector unitGeneration; + std::uint64_t unitBytes = 0; + bool syncing = false; std::uint64_t scannedEpoch = 0; - std::uint64_t syncedGeneration = 0; const Context context; const DepthTarget target; @@ -943,33 +1083,52 @@ private: return ResolveTextureFormat(resource.format) == (stencil ? VK_FORMAT_R8_UINT : d16 ? VK_FORMAT_R16_UNORM : VK_FORMAT_R32_SFLOAT); } - void copyPlane(PlaneStorage& plane, const std::shared_ptr& storage, bool stencil, bool toStorage, bool ownBatch = false) { + void copyPlane(PlaneStorage& plane, const std::shared_ptr& storage, bool stencil, bool toStorage, bool ownBatch = false, const std::vector* rects = nullptr) { + if (rects != nullptr && rects->empty()) return; auto* recorder = Recorder::Active(); Require(recorder != nullptr || ownBatch, "depth surface storage copies need an active recorder"); std::unique_ptr batch; if (recorder == nullptr) batch = std::make_unique(context); const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT; - const auto bytes = static_cast(target.extent.width) * target.extent.height * (stencil ? 1u : d16 ? 2u : 4u); + const std::uint32_t texelBytes = stencil ? 1u : d16 ? 2u : 4u; + const auto bytes = static_cast(target.extent.width) * target.extent.height * texelBytes; if (plane.staging == nullptr) plane.staging = std::make_unique(context, bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); const auto commands = recorder != nullptr ? recorder->Commands() : batch->Handle(); if (recorder != nullptr) recorder->Keep(storage); const std::uint32_t timing = recorder != nullptr ? recorder->BeginGpuTiming(Recorder::CommandClass::Copy) : 0u; - VkBufferImageCopy planeRegion{0, 0, 0, {stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1}, {0, 0, 0}, {target.extent.width, target.extent.height, 1}}; - VkBufferImageCopy colorRegion = planeRegion; - colorRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + const VkBufferImageCopy planeRegion{0, 0, 0, {stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1}, {0, 0, 0}, {target.extent.width, target.extent.height, 1}}; + std::vector planeRegions; + std::size_t moved = bytes; + if (rects == nullptr) { + planeRegions.push_back(planeRegion); + } else { + moved = 0; + for (const auto& rect : *rects) { + auto region = planeRegion; + region.bufferOffset = (static_cast(rect.offset.y) * target.extent.width + static_cast(rect.offset.x)) * texelBytes; + region.bufferRowLength = target.extent.width; + region.imageOffset = {rect.offset.x, rect.offset.y, 0}; + region.imageExtent = {rect.extent.width, rect.extent.height, 1}; + planeRegions.push_back(region); + moved += static_cast(rect.extent.width) * rect.extent.height * texelBytes; + } + } + auto colorRegions = planeRegions; + for (auto& region : colorRegions) region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + const auto count = static_cast(planeRegions.size()); const auto sourceImage = toStorage ? image : storage->Image(); const auto destinationImage = toStorage ? storage->Image() : image; RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - context.Function("vkCmdCopyImageToBuffer")(commands, sourceImage, VK_IMAGE_LAYOUT_GENERAL, plane.staging->Handle(), 1, toStorage ? &planeRegion : &colorRegion); + context.Function("vkCmdCopyImageToBuffer")(commands, sourceImage, VK_IMAGE_LAYOUT_GENERAL, plane.staging->Handle(), count, toStorage ? planeRegions.data() : colorRegions.data()); RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); - context.Function("vkCmdCopyBufferToImage")(commands, plane.staging->Handle(), destinationImage, VK_IMAGE_LAYOUT_GENERAL, 1, toStorage ? &colorRegion : &planeRegion); + context.Function("vkCmdCopyBufferToImage")(commands, plane.staging->Handle(), destinationImage, VK_IMAGE_LAYOUT_GENERAL, count, toStorage ? colorRegions.data() : planeRegions.data()); RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT); if (batch) { batch->SubmitAndWait(); return; } Recorder::CountBarriers(Recorder::CommandClass::Copy, 3); - recorder->EndGpuTiming(timing, bytes); + recorder->EndGpuTiming(timing, moved); } void release() noexcept { @@ -1578,15 +1737,36 @@ void retire(DepthSurface& surface) { for (auto* owners : {&depthOwners(), &stencilOwners()}) { std::erase_if(*owners, [&](const auto& entry) { return entry.second.surface == &surface; }); } - if (!surface.memoryStale) return; - surface.memoryStale = false; + if (!surface.AnyStale()) return; + surface.ClearStale(); const auto plane = stalePlaneRange(surface); - if (std::any_of(surfaces().begin(), surfaces().end(), [&](const auto& other) { return other->memoryStale && stalePlaneRange(*other) == plane; })) return; + if (std::any_of(surfaces().begin(), surfaces().end(), [&](const auto& other) { return other->AnyStale() && stalePlaneRange(*other) == plane; })) return; auto& planes = stalePlanes(); std::lock_guard lock(planes.mutex); std::erase(planes.ranges, plane); publishStaleHull(planes); } + +void retireStoringStale(DepthSurface& surface) { + if (!surface.AnyStale() || !mirrorable(surface.target)) { + retire(surface); + return; + } + const auto resource = mirrorResource(surface.target); + const auto planeBytes = DescribeSurface(resource).guestBytes; + std::uint64_t generation = 0; + const auto written = surface.WrittenUnits(resource, generation); + auto units = surface.staleUnits; + for (std::size_t unit = 0; unit < units.size() && unit < written.size(); ++unit) { + if (written[unit]) units[unit] = false; + } + std::shared_ptr mirror; + if (anyUnit(units) && (!anyUnit(written) || surface.StoresUnits(*surface.Mirror(resource)))) mirror = surface.StoreDepthPlane(resource, units); + retire(surface); + if (mirror == nullptr) return; + const auto [begin, end] = surface.UnitHull(units, planeBytes); + if (StorageTexture::FlushPending(begin, static_cast(end - begin), nullptr, "depth plane store")) surface.NoteStored(units, GuestMemory::TrackerGeneration()); +} } std::uint64_t DepthSurfacesSerial() { @@ -1749,7 +1929,7 @@ VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target, if (!created) surface->NoteHtileClear(VK_IMAGE_ASPECT_DEPTH_BIT); } if (!created) surface->NoteHtileClear(htileCleared); - if (mirrorable(target)) static_cast(surface->SyncFromMemory(mirrorResource(target))); + if (mirrorable(target)) surface->SyncFromMemory(mirrorResource(target)); surface->ApplyFastClear(); if (drawnDepth != nullptr && drawnDepth != surface) drawnDepth->written = ++depthWrites; surface->written = ++depthWrites; @@ -1761,14 +1941,15 @@ VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target, if (other.get() == surface || other->context.device != context.device || other->written >= surface->written || !mirrorable(other->target)) continue; const auto otherPlane = stalePlaneRange(*other); if (otherPlane == plane || otherPlane.first >= plane.second || plane.first >= otherPlane.second) continue; - other->memoryStale = false; + other->ClearStale(); other->DropMirror(); } } if ((depthWritten || cleared) && mirrorable(target)) { - surface->memoryStale = true; const auto plane = target.address; - noteStalePlane(plane, plane + DescribeSurface(mirrorResource(target)).guestBytes); + const auto planeBytes = DescribeSurface(mirrorResource(target)).guestBytes; + surface->MarkAllStale(planeBytes); + noteStalePlane(plane, plane + planeBytes); } drawnDepth = surface; return surface->view; @@ -1805,52 +1986,78 @@ bool DepthPlanesStale() { return planes.begin.load(std::memory_order_acquire) < planes.end.load(std::memory_order_acquire); } -std::vector> FlushDepthPlanes(std::uint64_t address, std::size_t bytes) { +std::vector> FlushDepthPlanes(std::uint64_t address, std::size_t bytes, DepthPlaneScope scope) { static thread_local bool flushing = false; const auto end = bytes > std::numeric_limits::max() - address ? std::numeric_limits::max() : address + bytes; if (flushing || !overlapsStalePlane(address, end)) return {}; + if (scope == DepthPlaneScope::Overlapped) { + std::lock_guard lock(surfacesMutex()); + const auto staleInRange = [&](const auto& surface) { return surface->AnyStale() && !surface->syncing && ((surface->pendingClear & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 || anyUnit(surface->StaleUnitsIn(DescribeSurface(mirrorResource(surface->target)).guestBytes, address, end))); }; + if (std::none_of(surfaces().begin(), surfaces().end(), staleInRange)) return {}; + } flushing = true; struct Reset { ~Reset() { flushing = false; } } reset; - std::vector>> stored; + struct Stored { + DepthSurface* surface; + std::shared_ptr mirror; + std::vector units; + std::uint64_t begin; + std::uint64_t end; + bool flushed; + }; + std::vector stored; std::vector> ranges; { std::lock_guard gpu(GuestMemory::GpuMutex()); std::lock_guard lock(surfacesMutex()); std::vector> remaining; for (const auto& surface : surfaces()) { - if (!surface->memoryStale) continue; + if (!surface->AnyStale()) continue; const auto resource = mirrorResource(surface->target); const auto plane = surface->target.address; const auto planeBytes = DescribeSurface(resource).guestBytes; - if (plane >= end || address >= plane + planeBytes) { - remaining.emplace_back(plane, plane + planeBytes); - continue; + const bool overlapped = !surface->syncing && plane < end && address < plane + planeBytes; + if (overlapped && (surface->pendingClear & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) surface->MarkAllStale(planeBytes); + if (overlapped && (scope == DepthPlaneScope::Whole || anyUnit(surface->StaleUnitsIn(planeBytes, address, end)))) { + surface->SyncFromMemory(resource); + auto units = scope == DepthPlaneScope::Whole ? surface->staleUnits : surface->StaleUnitsIn(planeBytes, address, end); + if (anyUnit(units)) { + auto mirror = surface->StoreDepthPlane(resource, units); + surface->ClearStale(units); + const auto hull = surface->UnitHull(units, planeBytes); + ranges.emplace_back(hull.first, hull.second - hull.first); + stored.push_back({surface.get(), std::move(mirror), std::move(units), hull.first, hull.second, false}); + } } - surface->memoryStale = false; - if (surface->SyncFromMemory(resource)) continue; - stored.emplace_back(surface.get(), surface->StoreDepthPlane(resource)); - ranges.emplace_back(plane, planeBytes); + if (surface->AnyStale()) remaining.emplace_back(plane, plane + planeBytes); } auto& planes = stalePlanes(); std::lock_guard staleLock(planes.mutex); planes.ranges = std::move(remaining); publishStaleHull(planes); } - for (const auto& entry : stored) entry.second->MarkWholeWritten(); - for (const auto& [plane, planeBytes] : ranges) StorageTexture::FlushPending(plane, static_cast(planeBytes), nullptr, "depth plane store"); + for (auto& entry : stored) entry.flushed = StorageTexture::FlushPending(entry.begin, static_cast(entry.end - entry.begin), nullptr, "depth plane store"); const auto generation = GuestMemory::TrackerGeneration(); std::lock_guard lock(surfacesMutex()); for (const auto& entry : stored) { - if (std::any_of(surfaces().begin(), surfaces().end(), [&](const auto& candidate) { return candidate.get() == entry.first; })) { - entry.first->syncedGeneration = generation; - entry.first->NoteMemory(); - } + if (!entry.flushed || std::none_of(surfaces().begin(), surfaces().end(), [&](const auto& candidate) { return candidate.get() == entry.surface; })) continue; + entry.surface->NoteStored(entry.units, generation); + entry.surface->NoteMemory(); } return ranges; } +DepthPlaneStoreStatistics DepthPlaneStoreCounts() { + const auto& counters = depthPlaneStoreCounters(); + DepthPlaneStoreStatistics statistics; + statistics.stores = counters.stores.load(); + statistics.units = counters.units.load(); + statistics.bytes = counters.bytes.load(); + return statistics; +} + std::uint64_t HtileDepthClearAddress(std::span code, std::span userData, const std::array& numThreads) { static constexpr std::array htileMaskKernel{0xd7460000u, 0x04010c06u, 0x34000084u, 0xdc388000u, 0x04020000u, 0xbf8c3f70u, 0xd7710008u, 0x00120805u, 0xd7710009u, 0x00120a05u, 0xd771000au, 0x00120c05u, 0xd771000bu, 0x00120e05u, 0xdc788000u, 0x00020800u, 0xbf810000u}; if (userData.size() < 6 || numThreads[0] != 64 || numThreads[1] != 1 || numThreads[2] != 1) return 0; @@ -2027,7 +2234,7 @@ std::shared_ptr DepthSurfaceTexture(const Context& context, std::span resource.depthOrLastArray)) && !(cube && resource.depthOrLastArray != 0 && resource.depthOrLastArray != 5) && (resource.dimension == TextureDimension::k2D || resource.dimension == TextureDimension::k2DArray || cube); if (!(sampled ? found->SampledAccepts(words, resource) : planes) && found->OverwrittenInMemory()) { - retire(*found); + retireStoringStale(*found); return nullptr; } found->ApplyFastClear(); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index 2438c18aa2..cb4c5b1136 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -2239,7 +2239,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters& } APS5_LOG_OUT_DEBUG("Creating color target %zu address=0x%llx bytes=%llu extent=%ux%u", index, static_cast(color.address), static_cast(color.bytes), color.extent.width, color.extent.height); const ColorTargetLayout colorLayout(color.extent.width, color.extent.height, color.tileMode, color.elementBytes); - static_cast(FlushDepthPlanes(color.address, colorLayout.Bytes())); + static_cast(FlushDepthPlanes(color.address, colorLayout.Bytes(), DepthPlaneScope::Whole)); RetireDepthSurfaces(context.device, color.address, colorLayout.Bytes()); constexpr VkBufferUsageFlags copies = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; timer.phase(PhaseSetup); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index 277eea93c5..fd39bcc21a 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -1123,7 +1123,7 @@ std::shared_ptr cachedStorageTexture(const Context& context, std reportGuestAllocation(viewed, viewedBytes); } } - if (DepthPlanesStale() || written) static_cast(FlushDepthPlanes(viewed.baseAddress, static_cast(viewedBytes))); + if (DepthPlanesStale() || written) static_cast(FlushDepthPlanes(viewed.baseAddress, static_cast(viewedBytes), written ? DepthPlaneScope::Whole : DepthPlaneScope::Overlapped)); if (written && DepthSurfaceOverlaps(context.device, viewed.baseAddress, viewedBytes)) { const auto storage = WrittenStorageBytes(viewed, mip); RetireCoveredDepthSurfaces(context.device, storage.bytes, storage.extents); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp index a70da26def..91970938a8 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp @@ -2837,6 +2837,16 @@ std::vector StorageTexture::sliceWindows(std::span< return windows; } +std::vector StorageTexture::UnitRects(const std::vector& units) const { + std::vector rects; + if (units.size() != trackedLayers) return rects; + const auto runs = unitRuns(units); + for (const auto& window : sliceWindows(runs)) { + for (const auto& region : window.regions) rects.push_back({{region.imageOffset.x, region.imageOffset.y}, {region.imageExtent.width, region.imageExtent.height}}); + } + return rects; +} + std::vector> StorageTexture::uncoveredBytes(std::span> runs) const { std::vector> covered; if (!geometry.thick) { @@ -4814,6 +4824,38 @@ void StorageTexture::MarkWholeWritten() { MarkDirty(); } +void StorageTexture::MarkUnitsWritten(const std::vector& units) { + if (!blockUnits || descriptor.dccAddress != 0 || units.size() != trackedLayers || std::all_of(units.begin(), units.end(), [](bool unit) { return unit; })) { + MarkWholeWritten(); + return; + } + const auto runs = unitRuns(units); + if (runs.empty()) return; + if (GuestMemory::Watched(descriptor.baseAddress, static_cast(guestBytes))) { + for (std::uint32_t unit = 0; unit < trackedLayers; ++unit) { + if (!units[unit]) continue; + layerOriginal[unit] = false; + cancelStoredSave(unit); + } + } else { + captureGuestBytes(&units); + } + uploadedKeys = DccKeys::Uncompressed; + for (const auto& [begin, end] : runs) { + const auto first = static_cast(begin / trackedLayerBytes); + const auto count = static_cast((end - begin + trackedLayerBytes - 1) / trackedLayerBytes); + const auto collected = GuestMemory::CollectWritesUncached(descriptor.baseAddress + begin, static_cast(end - begin)); + for (auto unit = first; unit < first + count; ++unit) layerGeneration[unit] = collected; + } + refreshGeneration(); + for (const auto& [begin, end] : runs) { + const auto first = static_cast(begin / trackedLayerBytes); + const auto count = static_cast((end - begin + trackedLayerBytes - 1) / trackedLayerBytes); + forgetBorrowed(first, count); + markLayersPending(first, count); + } +} + // See AdjacentGenerationEnabled above. std::vector StorageTexture::adjacentPendingUnchanged(std::uint64_t firstBlock, std::uint64_t lastBlock) const { std::vector adjacent; diff --git a/docs/dev/ENVIRONMENT.md b/docs/dev/ENVIRONMENT.md index 7d6f4d537b..dd75c7f25f 100644 --- a/docs/dev/ENVIRONMENT.md +++ b/docs/dev/ENVIRONMENT.md @@ -107,6 +107,7 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti | `APS5_CPU_STORES` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketMemory.cpp` | | writes packet stores on the CPU instead of recording them on the GPU | | `APS5_DCC_KEYS_EACH` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp` | | records every DCC key store at once instead of batching them | | `APS5_DEPTH_COPY_MIN_KIB` | integer | `core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp` | 1024 | minimum surface size in KiB for a depth target to take the depth-copy path | +| `APS5_DEPTH_PLANE_WHOLE_STORE` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp` | | stores a stale depth plane whole when memory over it is read, instead of only the 64 KiB units the read overlaps | | `APS5_DISPATCH_CACHE_BYTES` | integer | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/DispatchCache.cpp` | 268435456 | size of the dispatch cache in bytes (0 keeps the default of 256 MiB) | | `APS5_DISPATCH_CACHE_ENTRIES` | integer | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/DispatchCache.cpp` | 8192 | number of dispatch cache entries; 0 picks a default from the variant count | | `APS5_DISPATCH_STAMP_VALIDATE` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/DispatchCache.cpp` | | validates dispatch cache hits by write stamps, forcing one variant and a 4096-entry cache | diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index 05682afa75..0e12b5ca8f 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -73,7 +73,7 @@ - [Reused depth storage](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp): a storage view with another extent or a format that cannot hold the cached depth plane uses guest memory. Depth rendering is not written back to those guest bytes; this admission rule does not prove allocation reuse or preserve a depth writer through an incompatible view. A 2D storage view, or a one-layer 2D array view of layer 0, of level 0 with the plane's extent and format (32-bit float or the 32-bit depth-bits layout for the depth plane, 16-bit unorm for D16, 8-bit uint for the stencil plane) is a storage image that is [filled](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) from the depth image before every use and, when the shader writes it, copied back after the work: a whole-plane copy through a staging buffer each way. A dispatch of the 15-word 8x8x1 image clear kernel ([`MatchStorageImageClear`](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp): one colour read from the V# in `user[8..11]` stored to every texel of the T# in `user[0..7]`) that covers the whole plane and stores 0 or 1 through a 16-bit unorm view, 0 or 0xffff through a 16-bit uint view, or 0 or a normal float in (0, 1] through a 32-bit float or integer view leaves out the copy-in and replaces the copy-out by a clear of the depth image to that value; the dispatch still writes the storage image. The colour is read again when the copy-out is replaced, and a changed value keeps the copy-out. Indirect dispatches, partial last groups, dispatches that queue on the draw pipeline, other values and formats, a plane the kernel covers only partly and stencil planes keep both copies. A written view is also left pending like any other storage image, so the plane's content, drawn depth included, reaches guest memory on the next flush of that range. A 2D array view of more than one slice of the depth plane, or of a later slice, is a storage image over the slices from 0 to its last (`LayeredDepthStorage`); only the slices from its base slice to its last are copied: a slice that has a depth surface at the plane's address plus a whole depth slice per layer (`DepthSliceBytes`, as for the depth plane copies below) is copied from that surface before every use and back to it after the work, every bound slice each time, through a staging buffer; the other slices hold guest memory like any storage image. These images are kept per address, extent, slice count, base slice, format and tile mode until the device goes away, outside the storage cache's budget. Such a view needs a layer pitch equal to that slice size, and another view of the same memory (the 2D view of its first slice, a view from another base slice, or a sampled copy) is a separate image, copied in before and out after each use, so views that are written before a flush and share a slice do not merge: the later copy-out wins, so their results are not merged. A matching view still throws when it is a cube, a 2D array of more than one layer of the stencil plane or from a later slice, or another level, when the surface is multisampled, when it names the depth plane of a 16-bit depth with stencil surface the device stores in a wider format, and when it was written after another view of the same plane (another format, tile mode, address or dimension) replaced it, as two such views bound in one dispatch are. Stored depth values outside 0..1 are not clamped, and Vulkan leaves that copy undefined without `VK_EXT_depth_range_unrestricted`. `agc_driver_depth_storage_dispatch` pins these paths; none is measured on the console. - [Collapsed color array views](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) preserve the selected slice in the XOR address equation when the view is represented as a shifted 2D surface. Native Vulkan coverage checks R_X draws, array reads, publication and re-upload for slices 1-3; Lost Soul Aside PPSA26358 validates the RGBA16F cube mip chain on Windows/NVIDIA. Other devices and console comparisons remain unverified. Full-array views retain their layer addressing. - A [depth surface](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver) is retired when a color target or a written storage image it does not serve overlaps it, or when it is sampled through a view it cannot serve and guest memory over it was written since it was last bound; lookups then skip it, so sampling reads the memory like any texture. Bound again, it keeps its own depth instead of the bytes written over it, except for the depth planes the next entry keeps in guest memory. Without write watching, a view it cannot serve still throws. A retired surface also gives up its depth and stencil planes, so another surface sharing them no longer copies from it. -- [Depth planes in guest memory](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver): only single-sample `D32_SFLOAT` and `D32_SFLOAT_S8_UINT` planes with the 64 KiB Z swizzle (`SW_MODE` 24) reach guest memory; D16, other swizzles, stencil and multisampled planes keep depth in the host image only. A plane drawn since its last store is stored, uncompressed, when a buffer upload, a `GuestBuffers` V#, an image lookup or a pending-store flush overlaps it; CPU reads and reads through address-based heaps are not caught. Guest writes are ordered when collected (bind, flush, collect epoch), not when made, at 64 KiB granularity: a collected write reloads the whole plane unless an HTILE clear is pending. A store outside a recorder waits for the GPU. The console keeps HTILE-compressed tiles compressed in memory until a decompress (Mesa `ac_descriptors.h`). +- [Depth planes in guest memory](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver): only single-sample `D32_SFLOAT` and `D32_SFLOAT_S8_UINT` planes with the 64 KiB Z swizzle (`SW_MODE` 24) reach guest memory; D16, other swizzles, stencil and multisampled planes keep depth in the host image only. A plane drawn since its last store is stored, uncompressed, in 64 KiB units (128x128 tiles): a buffer upload, a `GuestBuffers` V#, an image lookup or a pending-store flush stores the units it overlaps, a color target or written storage image over the plane all of them. CPU reads and reads through address-based heaps are not caught. Guest writes are ordered when collected (bind, flush, collect epoch), not when made: a collected write reloads the units it touched unless an HTILE clear is pending. A store outside a recorder waits for the GPU. The console keeps HTILE-compressed tiles compressed in memory until a decompress (Mesa `ac_descriptors.h`). - [Depth plane copies](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver): a depth plane sampled as a 2D array or a cube, or a D16 plane read as `R16_UINT`, is copied into an image of its own (`DepthPlaneCopy`), kept per base address, extent, format and slice count; a slice comes from the live depth surface when one is bound at the slice's address and from guest memory (detiled) otherwise. A draw or dispatch that binds the plane copies again only the slices that changed and records nothing when none did: a surface slice when its image or write serial differs from the one copied (the serial is bumped when the surface is created, when a draw binds it as its depth target, when a pending fast clear is applied and when a written storage view of the plane is copied back), a guest-memory slice when its bytes were written since they were last read (`GuestMemory::UnchangedSince`), a snapshot write overlaps them, or its address, size or tile mode changed. The surface of the latest depth draw is copied at every use until another depth target is bound, since that draw may still be recorded after the copy. A slice's address is the base plus a whole 64 KiB-block depth slice per layer (`DepthSliceBytes`), and the cube face order is the array order; both are inferred from the titles' use, not measured on hardware. A 2D-array T# whose `BASE_ARRAY` is past slice 0 reads slices `BASE_ARRAY` to `LAST_ARRAY` of the copy, which holds slices 0 to `LAST_ARRAY`, and a 2D binding of it reads slice `BASE_ARRAY`; that follows the colour 2D-array semantics and the titles that hit it (PPSA01556, PPSA03912), and was not measured on hardware for depth surfaces. Planes with mips, a cube or a 2D T# with a base slice other than 0, a `BASE_ARRAY` past `LAST_ARRAY`, a cube with other than 1 or 6 slices, and other views of the planes still throw, and so do these copies of a 16-bit plane with stencil that the device stores as D24 or D32 (16-bit depth with stencil, below). - [Repacked format-43 copies](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp) (libSceAgcDriver): a sampled texture of format 43 (10-bit R, 11-bit G and B unsigned floats from bit 0) is uploaded into a `B10G11R11_UFLOAT` image with its R and B fields swapped (`RepackTextureWord`) and viewed with R and B swapped back. A texture whose results a storage image still holds is copied from it again, whole, whenever the image changed (`RefreshDepthCopy`); a 3D one is flushed to guest memory and uploaded instead. Storage reads and writes of format 43 keep the packed `R32_UINT` path. - [SW_64KB_Z_X color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ColorTargetLayout.cpp) (libSceAgcDriver) use the texture equations and 64 KiB block geometry of `SW_64KB_Z_X`, whose addresses are checked against [AMD addrlib GFX10](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp) in the texture tiling tests. Any 256-byte pipe/bank XOR below the block size is accepted, as for `SW_64KB_R_X` targets and Z_X textures; which of those bits the console honours, and the render results, are not measured. From 7bf7be589d68688bdb74e41a240b4a3ce1881f0f Mon Sep 17 00:00:00 2001 From: milescoviello Date: Wed, 21 Oct 2026 08:32:53 +0000 Subject: [PATCH 119/523] test(agc): cover range-limited depth plane stores DepthPlanePartialStore draws depth 0.5 into a 384x200 D32 Z_X plane (3x2 64 KiB tiles, the last row 72 texels high) over memory filled with 1.0 and checks the plane's memory unit by unit: - a read ending 3 KiB into the plane stores unit 0 alone (one store, one unit, 64 KiB counted) and leaves the plane stale; the same read again stores nothing; - a read of the whole plane stores the other five units, the edge tiles holding 128x72 depth values with their padding left as it was; - a guest write into unit 4 after unit 0 was stored wins in unit 4 only: a later whole read stores the four other stale units over 1.0, and a LESS draw at 0.6 passes exactly over unit 4 (the plane-wide generation synced every unit from memory instead); - the retire of a surface the guest wrote into, through a view with two levels, stores its stale units the guest did not write; - a color target over unit 5 retires the surface after storing units 1-4; - over unwatched memory a partial store leaves the other units on the GPU, so a LESS draw at 0.6 passes nowhere. TextureTiling checks that the 64KB Z_X equation of 4-byte elements reads only x and y bits below 7, so each 64 KiB unit of a 3840x2160 D32 plane is exactly one 128x128 tile. Co-Authored-By: Claude Opus 5.5 --- .../libSceAgcDriver/tests/TextureTiling.cpp | 23 ++ .../execution/DepthPlanePartialStore.cpp | 234 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/DepthPlanePartialStore.cpp diff --git a/core/libs/prx/libSceAgcDriver/tests/TextureTiling.cpp b/core/libs/prx/libSceAgcDriver/tests/TextureTiling.cpp index cb439c6ffe..48b7f6fa98 100644 --- a/core/libs/prx/libSceAgcDriver/tests/TextureTiling.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/TextureTiling.cpp @@ -386,6 +386,29 @@ void RunTextureTilingTests() { Require(dense.pitchBytes == 1024 && CoveredMipBytes(TextureTileMode::kLinear, 4, dense) == std::vector>{{0, 3072}}, "linear rows without padding are not one range"); } + { + const auto* equation = FindTextureSwizzleEquation(XorSwizzleMode(TextureTileMode::kZ64KBX), 4u); + Require(equation != nullptr, "missing the 64KB Z_X swizzle equation of 4-byte elements"); + for (const auto mask : equation->bits) Require((mask & 0x00f80f80u) == 0u, "a 64KB Z_X address bit of a 4-byte element reads x or y bits past one 128x128 tile"); + GuestTextureResource plane{}; + plane.width = 3840; + plane.height = 2160; + plane.mipCount = 1; + plane.tileMode = TextureTileMode::kZ64KBX; + plane.dimension = TextureDimension::k2D; + plane.format = 22; + const auto geometry = DescribeSurface(plane); + const auto& mip = geometry.mips.front(); + const auto block = ThinBlockLayout(TextureTileMode::kZ64KBX, 4u); + Require(block[0] == 65536u && block[1] == 128u && block[2] == 128u, "a 64KB Z_X tile of 4-byte elements is not 128x128"); + Require(!mip.tail && mip.tiledOffset == 0u && mip.blocksPerRow == 30u && geometry.guestBytes == 510ull * 65536ull, "a 3840x2160 D32 Z_X plane is not 30x17 tiles"); + const std::array, 8> samples{{{0, 0}, {127, 127}, {128, 0}, {3839, 0}, {0, 128}, {1000, 700}, {2047, 1151}, {3839, 2159}}}; + for (const auto& [x, y] : samples) { + const auto offset = mip.tiledOffset + (static_cast(y / block[2]) * mip.blocksPerRow + x / block[1]) * block[0] + equationOffset(*equation, x, y, 0); + Require(offset / 65536u == static_cast(y >> 7u) * 30u + (x >> 7u), "element (" + std::to_string(x) + ", " + std::to_string(y) + ") of a 3840x2160 D32 Z_X plane lies outside the 64 KiB unit of its 128x128 tile"); + } + } + reject([] { ComputeSurfaceSize({}, 1); }, "empty mip chain"); reject([] { ComputeSurfaceSize(ComputeMipLayout(TextureTileMode::kLinear, 1, 4, 4, 1), 0); }, "zero array layers"); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/DepthPlanePartialStore.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/DepthPlanePartialStore.cpp new file mode 100644 index 0000000000..43b8815b56 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/DepthPlanePartialStore.cpp @@ -0,0 +1,234 @@ +#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp" +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp" +#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp" +#include "prx/libc/include/GuestAllocations.hpp" +#include "prx/libc/include/GuestArena.hpp" +#include "prx/libc/include/GuestWriteWatch.hpp" +#include "DepthFastClearHarness.hpp" +#include "VulkanTestDevice.hpp" +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace AgcDriver { + +struct VulkanDeviceTestAccess { + static Graphics::Context ContextOf(const VulkanDevice& device) { return device.graphicsContext(); } +}; + +} + +namespace { + +using AgcDriver::Graphics::DepthPlaneStoreStatistics; +using DepthFastClearHarness::Compile; +using DepthFastClearHarness::Draw; +using DepthFastClearHarness::DrawOptions; +using DepthFastClearHarness::Require; +using DepthFastClearHarness::Surface; + +constexpr std::uint32_t PlaneWidth = 384; +constexpr std::uint32_t PlaneHeight = 200; +constexpr VkExtent2D PlaneExtent{PlaneWidth, PlaneHeight}; +constexpr std::size_t Block = 65536; +constexpr std::size_t Units = 6; +constexpr std::size_t PlaneBytes = Units * Block; +constexpr std::size_t UnitTexels = Block / sizeof(float); +constexpr std::size_t EdgeTexels = 128u * (PlaneHeight - 128u); +constexpr std::size_t PlaneTexels = static_cast(PlaneWidth) * PlaneHeight; +constexpr VkExtent2D SmallExtent{64, 32}; +constexpr std::size_t SmallColorBytes = static_cast(SmallExtent.width) * SmallExtent.height * 4u; +constexpr std::uint64_t ReadBefore = 4096; +constexpr std::size_t ReadBytes = 7u * 1024u; +alignas(256) std::array Colors{}; + +void* AllocateBlocks(std::size_t bytes, bool watched) { +#ifdef _WIN32 + void* block = nullptr; + if (watched) { + block = GuestArena::GuestArenaAllocate_nid_postfix(bytes, Block); + if (block != nullptr) GuestArena::GuestArenaCommit_nid_postfix(block, bytes, PAGE_READWRITE, bytes); + } else { + block = VirtualAlloc(nullptr, bytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + } + Require(block != nullptr, "cannot allocate the depth plane blocks"); +#else + void* raw = mmap(nullptr, bytes + Block, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Require(raw != MAP_FAILED, "cannot map the depth plane blocks"); + const auto begin = reinterpret_cast(raw); + const auto aligned = (begin + Block - 1) & ~static_cast(Block - 1); + if (aligned != begin) munmap(raw, aligned - begin); + if (aligned + bytes != begin + bytes + Block) munmap(reinterpret_cast(aligned + bytes), begin + Block - aligned); + void* block = reinterpret_cast(aligned); + if (watched) GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block, bytes); +#endif + GuestAllocations::Mutation().Add(block, bytes, true, true, true); + return block; +} + +void Fill(float* plane, float value) { + std::fill(plane, plane + PlaneBytes / sizeof(float), value); + AgcDriver::GuestMemory::BumpCollectEpoch(); +} + +void FillUnit(float* plane, std::size_t unit, float value) { + std::fill(plane + unit * UnitTexels, plane + (unit + 1u) * UnitTexels, value); + AgcDriver::GuestMemory::BumpCollectEpoch(); +} + +std::size_t Texels(std::size_t unit) { + return unit < 3u ? UnitTexels : EdgeTexels; +} + +void RequireUnit(const float* plane, std::size_t unit, float depth, std::size_t count, const std::string& what) { + const auto* first = plane + unit * UnitTexels; + const auto held = static_cast(std::count(first, first + UnitTexels, depth)); + Require(held == count, what + ": unit " + std::to_string(unit) + " holds " + std::to_string(held) + " texels of " + std::to_string(depth) + " instead of " + std::to_string(count)); +} + +DepthPlaneStoreStatistics Since(const DepthPlaneStoreStatistics& before) { + const auto now = AgcDriver::Graphics::DepthPlaneStoreCounts(); + DepthPlaneStoreStatistics difference; + difference.stores = now.stores - before.stores; + difference.units = now.units - before.units; + difference.bytes = now.bytes - before.bytes; + return difference; +} + +std::vector> ReadStart(AgcDriver::VulkanDevice& device, std::uint64_t base) { + auto stored = AgcDriver::Graphics::FlushDepthPlanes(base - ReadBefore, ReadBytes); + device.WaitIdle(); + return stored; +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + Require(AgcDriver::Graphics::DepthSliceBytes(PlaneExtent, 4u) == PlaneBytes, "the 384x200 depth plane is not six 64 KiB tiles"); + auto* blocks = static_cast(AllocateBlocks(Block + PlaneBytes, true)); + auto* plane = reinterpret_cast(blocks + Block); + const auto base = reinterpret_cast(plane); + const auto shaders = Compile(*device); + const auto swizzle = AgcDriver::Graphics::XorSwizzleMode(AgcDriver::Graphics::TextureTileMode::kZ64KBX); + const Surface depth{base, 0, 0, false, VK_FORMAT_D32_SFLOAT, 0, 0, swizzle}; + const auto draw = [&](const Surface& surface, const DrawOptions& options) { return Draw(*device, shaders, surface, options, PlaneExtent, Colors); }; + const DrawOptions writeHalf{.depthWrite = true, .depthCompare = VK_COMPARE_OP_ALWAYS}; + + Fill(plane, 1.0f); + Require(draw(depth, writeHalf) == PlaneTexels, "the draw writing depth 0.5 did not cover the plane"); + Require(AgcDriver::Graphics::DepthPlanesStale(), "the draw writing depth left the plane unregistered"); + auto before = AgcDriver::Graphics::DepthPlaneStoreCounts(); + const auto first = ReadStart(*device, base); + auto stored = Since(before); + Require(first.size() == 1 && first[0].first == base && first[0].second == Block, "a read ending 3 KiB into the stale plane did not store exactly its first 64 KiB unit"); + Require(stored.stores == 1 && stored.units == 1 && stored.bytes == Block, "a read ending 3 KiB into the stale plane did not count one store of one unit"); + RequireUnit(plane, 0, 0.5f, UnitTexels, "after a read of the first unit"); + for (std::size_t unit = 1; unit < Units; ++unit) RequireUnit(plane, unit, 1.0f, UnitTexels, "after a read of the first unit, a unit it does not overlap"); + Require(AgcDriver::Graphics::DepthPlanesStale(), "a store of one unit left no stale plane"); + + before = AgcDriver::Graphics::DepthPlaneStoreCounts(); + Require(ReadStart(*device, base).empty(), "the same read again stored the first unit again"); + stored = Since(before); + Require(stored.stores == 0 && stored.units == 0 && stored.bytes == 0, "the same read again counted a store"); + + before = AgcDriver::Graphics::DepthPlaneStoreCounts(); + const auto rest = AgcDriver::Graphics::FlushDepthPlanes(base, PlaneBytes); + device->WaitIdle(); + stored = Since(before); + Require(rest.size() == 1 && rest[0].first == base + Block && rest[0].second == PlaneBytes - Block, "a read of the whole plane did not store the five units still stale"); + Require(stored.stores == 1 && stored.units == Units - 1u && stored.bytes == PlaneBytes - Block, "a read of the whole plane did not count one store of five units"); + for (std::size_t unit = 0; unit < Units; ++unit) RequireUnit(plane, unit, 0.5f, Texels(unit), "after a read of the whole plane"); + for (std::size_t unit = 3; unit < Units; ++unit) RequireUnit(plane, unit, 1.0f, UnitTexels - EdgeTexels, "after a read of the whole plane, the padding below the last row"); + Require(!AgcDriver::Graphics::DepthPlanesStale(), "a read of the whole plane left it stale"); + + if (!AgcDriver::GuestMemory::WriteWatched() || !AgcDriver::GuestMemory::Watched(base, PlaneBytes)) { + std::puts("guest memory is not write-watched here: guest writes into a partly stored plane are not exercised"); + } else { + Fill(plane, 1.0f); + Require(draw(depth, writeHalf) == PlaneTexels, "the draw writing depth 0.5 over the reloaded plane did not cover it"); + Require(!ReadStart(*device, base).empty(), "a read of the first unit of the drawn plane stored nothing"); + FillUnit(plane, 4, 0.75f); + draw(depth, {.depthTest = false}); + before = AgcDriver::Graphics::DepthPlaneStoreCounts(); + Require(!AgcDriver::Graphics::FlushDepthPlanes(base, PlaneBytes).empty(), "a read of the whole plane after a guest write into unit 4 stored nothing"); + device->WaitIdle(); + stored = Since(before); + Require(stored.units == 4 && stored.bytes == 4u * Block, "a read of the whole plane after a guest write into unit 4 did not store the four units still stale"); + for (const std::size_t unit : {0u, 1u, 2u, 3u, 5u}) RequireUnit(plane, unit, 0.5f, Texels(unit), "a guest write into unit 4 after unit 0 was stored, then a read of the whole plane"); + RequireUnit(plane, 4, 0.75f, UnitTexels, "a guest write into unit 4 after unit 0 was stored, then a read of the whole plane, the written unit"); + Require(draw(depth, {.minDepth = 0.6f, .maxDepth = 0.6f}) == EdgeTexels, "a draw at depth 0.6 did not pass LESS exactly over the unit the guest wrote 0.75 into"); + + Fill(plane, 1.0f); + Require(draw(depth, writeHalf) == PlaneTexels, "the draw writing depth 0.5 before a guest write and an unmatched view did not cover the plane"); + Require(!ReadStart(*device, base).empty(), "a read of the first unit before a guest write and an unmatched view stored nothing"); + FillUnit(plane, 2, 0.75f); + const std::array words{0, 0, 0, 0, PlaneWidth, PlaneHeight, 1, 0}; + AgcDriver::Graphics::GuestTextureResource levels{}; + levels.baseAddress = base; + levels.width = PlaneWidth; + levels.height = PlaneHeight; + levels.mipCount = 2; + levels.lastLevel = 1; + levels.dimension = AgcDriver::Graphics::TextureDimension::k2D; + levels.tileMode = AgcDriver::Graphics::TextureTileMode::kZ64KBX; + levels.format = 22; + Require(AgcDriver::Graphics::DepthSurfaceTexture(AgcDriver::VulkanDeviceTestAccess::ContextOf(*device), words, levels, {}) == nullptr, "a view with two levels of a plane the guest wrote was served by the depth surface"); + device->WaitIdle(); + Require(!AgcDriver::Graphics::DepthSurfaceAt(base), "a view with two levels of a plane the guest wrote left the depth surface live"); + for (const std::size_t unit : {0u, 1u, 3u, 4u, 5u}) RequireUnit(plane, unit, 0.5f, Texels(unit), "the retire of a partly stored plane the guest wrote into"); + RequireUnit(plane, 2, 0.75f, UnitTexels, "the retire of a partly stored plane the guest wrote into, the written unit"); + } + + Fill(plane, 1.0f); + Require(draw(depth, writeHalf) == PlaneTexels, "the draw writing depth 0.5 under a later color target did not cover the plane"); + Require(!ReadStart(*device, base).empty(), "a read of the first unit before a color target stored nothing"); + auto* smallPlane = AllocateBlocks(Block, true); + const Surface smallDepth{reinterpret_cast(smallPlane), 0, 0, false, VK_FORMAT_D32_SFLOAT, 0, 0, swizzle}; + const std::span overLastUnit(reinterpret_cast(plane + 5u * UnitTexels), SmallColorBytes); + Draw(*device, shaders, smallDepth, {.depthTest = false}, SmallExtent, overLastUnit); + Require(!AgcDriver::Graphics::DepthSurfaceAt(base), "a color target over the last unit left the depth surface live"); + for (std::size_t unit = 0; unit < 5u; ++unit) RequireUnit(plane, unit, 0.5f, Texels(unit), "a color target over the last unit of a partly stored plane"); + + auto* looseBlocks = static_cast(AllocateBlocks(Block + PlaneBytes, false)); + auto* loose = reinterpret_cast(looseBlocks + Block); + const auto looseBase = reinterpret_cast(loose); + if (AgcDriver::GuestMemory::Watched(looseBase, PlaneBytes)) { + std::puts("memory outside the guest arena is write-watched here: a partial store over unwatched memory is not exercised"); + } else { + const Surface unwatched{looseBase, 0, 0, false, VK_FORMAT_D32_SFLOAT, 0, 0, swizzle}; + Fill(loose, 1.0f); + Require(draw(unwatched, writeHalf) == PlaneTexels, "the draw writing depth 0.5 into an unwatched plane did not cover it"); + before = AgcDriver::Graphics::DepthPlaneStoreCounts(); + Require(ReadStart(*device, looseBase).size() == 1, "a read of the first unit of an unwatched plane did not store it"); + stored = Since(before); + Require(stored.units == 1 && stored.bytes == Block, "a read of the first unit of an unwatched plane did not count one unit"); + RequireUnit(loose, 0, 0.5f, UnitTexels, "unwatched plane after a read of the first unit"); + for (std::size_t unit = 1; unit < Units; ++unit) RequireUnit(loose, unit, 1.0f, UnitTexels, "unwatched plane after a read of the first unit, a unit it does not overlap"); + Require(draw(unwatched, {.minDepth = 0.6f, .maxDepth = 0.6f}) == 0u, "a draw at depth 0.6 passed LESS over unwatched units still holding depth 0.5 on the GPU"); + } + std::puts("depth plane partial store tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} From 0a059a9365afd06d39fe4502860801dc1ebffe3b Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Tue, 20 Oct 2026 14:22:05 +0530 Subject: [PATCH 120/523] feat(libSceFreeType): load glyphs by character code --- core/libs/prx/libSceFreeType/Export.cpp | 7 ++++ core/libs/tests/GuestFreeType.cpp | 43 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/core/libs/prx/libSceFreeType/Export.cpp b/core/libs/prx/libSceFreeType/Export.cpp index 0a098e6024..b86e37b86b 100644 --- a/core/libs/prx/libSceFreeType/Export.cpp +++ b/core/libs/prx/libSceFreeType/Export.cpp @@ -103,6 +103,13 @@ int APS5_VABI FT_Load_Glyph_nid_postfix(GuestFaceRec* face, std::uint32_t glyph_ return error; } +int APS5_VABI FT_Load_Char_nid_postfix(GuestFaceRec* face, std::uint64_t char_code, std::int32_t load_flags) { + GuestFace* owner = FaceOf(face); + const FT_Error error = FT_Load_Char(owner ? owner->host : nullptr, static_cast(static_cast(char_code)), load_flags); + if (owner) SyncSlot(*owner); + return error; +} + int APS5_VABI FT_Render_Glyph_nid_postfix(GuestGlyphSlotRec* slot, std::int32_t render_mode) { GuestFace* owner = FaceOf(slot); if (owner) WriteBackOutline(*owner); diff --git a/core/libs/tests/GuestFreeType.cpp b/core/libs/tests/GuestFreeType.cpp index bfb51cb340..b288ece217 100644 --- a/core/libs/tests/GuestFreeType.cpp +++ b/core/libs/tests/GuestFreeType.cpp @@ -6,6 +6,7 @@ #include FT_FREETYPE_H #include FT_OUTLINE_H +#include #include #include #include @@ -27,6 +28,7 @@ int APS5_VABI FT_New_Face_nid_postfix(void*, const char*, std::int64_t, GuestFac int APS5_VABI FT_Set_Pixel_Sizes_nid_postfix(GuestFaceRec*, std::uint32_t, std::uint32_t); std::uint32_t APS5_VABI FT_Get_Char_Index_nid_postfix(GuestFaceRec*, std::uint64_t); int APS5_VABI FT_Load_Glyph_nid_postfix(GuestFaceRec*, std::uint32_t, std::int32_t); +int APS5_VABI FT_Load_Char_nid_postfix(GuestFaceRec*, std::uint64_t, std::int32_t); int APS5_VABI FT_Render_Glyph_nid_postfix(GuestGlyphSlotRec*, std::int32_t); } @@ -131,6 +133,47 @@ int main() { Require(FT_Get_Char_Index_nid_postfix(face, 0x100000041ull) == 1); Require(FT_Get_Char_Index_nid_postfix(nullptr, 'A') == 0); + Require(FT_Load_Char_nid_postfix(nullptr, 'A', FT_LOAD_DEFAULT) == FT_Err_Invalid_Face_Handle); + for (const std::uint64_t code : std::array{'A', 'B', 0x100000041ull, 0xFFFFFFFFFFFFFFFFull}) { + for (const std::int32_t flags : std::array{FT_LOAD_DEFAULT, FT_LOAD_NO_SCALE, FT_LOAD_NO_HINTING, FT_LOAD_RENDER}) { + Require(FT_Load_Char_nid_postfix(face, code, flags) == FT_Load_Char(host, static_cast(code), flags)); + SlotMatches(face->glyph, host->glyph); + Require(face->glyph->glyph_index == (static_cast(code) == 'A' ? 1u : 0u)); + } + } + { + auto noMap = font; + const std::size_t tableCount = (static_cast(noMap[4]) << 8) | noMap[5]; + bool renamed = false; + for (std::size_t offset = 12; offset < 12 + tableCount * 16; offset += 16) { + if (std::memcmp(noMap.data() + offset, "cmap", 4) != 0) continue; + std::memcpy(noMap.data() + offset, "none", 4); + renamed = true; + break; + } + Require(renamed); + { + std::ofstream file(root / "app0" / "no-map.ttf", std::ios::binary); + file.write(reinterpret_cast(noMap.data()), static_cast(noMap.size())); + Require(file.good()); + } + GuestFaceRec* guestNoMap = nullptr; + FT_Face nativeNoMap = nullptr; + Require(FT_New_Face_nid_postfix(library, "/app0/no-map.ttf", 0, &guestNoMap) == 0); + Require(FT_New_Memory_Face(hostLibrary, noMap.data(), static_cast(noMap.size()), 0, &nativeNoMap) == 0); + Require(guestNoMap->charmap == nullptr && nativeNoMap->charmap == nullptr); + Require(FT_Set_Pixel_Sizes_nid_postfix(guestNoMap, 0, 100) == 0 && FT_Set_Pixel_Sizes(nativeNoMap, 0, 100) == 0); + for (const std::uint64_t code : {1ull, 2ull}) { + for (const std::int32_t flags : std::array{FT_LOAD_DEFAULT, FT_LOAD_RENDER}) { + const FT_Error expected = FT_Load_Char(nativeNoMap, static_cast(code), flags); + Require((code == 1) == (expected == 0)); + Require(FT_Load_Char_nid_postfix(guestNoMap, code, flags) == expected); + SlotMatches(guestNoMap->glyph, nativeNoMap->glyph); + } + } + Require(FT_Done_Face(nativeNoMap) == 0); + } + Require(FT_Load_Glyph_nid_postfix(nullptr, 1, FT_LOAD_DEFAULT) == FT_Err_Invalid_Face_Handle); Require(FT_Load_Glyph_nid_postfix(face, 1, FT_LOAD_DEFAULT) == 0 && FT_Load_Glyph(host, 1, FT_LOAD_DEFAULT) == 0); GuestGlyphSlotRec* slot = face->glyph; From f43de1fe5d38d7bb191bc81e971f4052420593a1 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Tue, 20 Oct 2026 14:34:14 +0530 Subject: [PATCH 121/523] style(libSceFreeType): use camel case for character arguments --- core/libs/prx/libSceFreeType/Export.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceFreeType/Export.cpp b/core/libs/prx/libSceFreeType/Export.cpp index b86e37b86b..e87fa466f8 100644 --- a/core/libs/prx/libSceFreeType/Export.cpp +++ b/core/libs/prx/libSceFreeType/Export.cpp @@ -103,9 +103,9 @@ int APS5_VABI FT_Load_Glyph_nid_postfix(GuestFaceRec* face, std::uint32_t glyph_ return error; } -int APS5_VABI FT_Load_Char_nid_postfix(GuestFaceRec* face, std::uint64_t char_code, std::int32_t load_flags) { +int APS5_VABI FT_Load_Char_nid_postfix(GuestFaceRec* face, std::uint64_t charCode, std::int32_t loadFlags) { GuestFace* owner = FaceOf(face); - const FT_Error error = FT_Load_Char(owner ? owner->host : nullptr, static_cast(static_cast(char_code)), load_flags); + const FT_Error error = FT_Load_Char(owner ? owner->host : nullptr, static_cast(static_cast(charCode)), loadFlags); if (owner) SyncSlot(*owner); return error; } From db3af409704a595bb22e06bc43583d652c88632b Mon Sep 17 00:00:00 2001 From: Joey Fisher Date: Mon, 19 Oct 2026 10:53:16 -0600 Subject: [PATCH 122/523] fix(agc): wait for the idle flush's unit-shadow publish before returning The idle flush (VulkanDevice::StoreIdleResults) publishes the unit shadows over guest memory into the host import and only submits the copies. publishUnits picks the units to store on the CPU, and the slab -> import copy lands when the GPU runs it, so a guest store made between the two is overwritten with the older shadow bytes. The memory-access hook waits through SyncThrough right after its flush; the idle flush has no reader that waits. FlushPending and PublishShadowsOnly now sync the recorder under the GPU mutex after an idle publish that stored units. Other flush reasons are unchanged. Test: agc_idle_publish_wait (a CPU store made after the idle flush returns survives; a label flush's publish stays recorded). --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 4 +- .../libSceAgcDriver/Graphics/src/Texture.cpp | 17 +++- .../prx/libSceAgcDriver/tests/Recorder.cpp | 81 +++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 198d4a0865..350d4e703e 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -1542,6 +1542,8 @@ if(BUILD_TESTING) set_tests_properties(agc_sparse_run_import_copied PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30) add_test(NAME agc_unit_shadow COMMAND agc_driver_recorder_tests --unit-shadow-only) set_tests_properties(agc_unit_shadow PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000") + add_test(NAME agc_idle_publish_wait COMMAND agc_driver_recorder_tests --idle-publish-wait-only) + set_tests_properties(agc_idle_publish_wait PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000;APS5_WRITE_WATCH_IMPORTS=watch") if(WIN32) add_test(NAME agc_storage_cpu_snapshot COMMAND agc_driver_recorder_tests --storage-cpu-snapshot-only) set_tests_properties(agc_storage_cpu_snapshot PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 30 SKIP_RETURN_CODE 77 @@ -1575,7 +1577,7 @@ if(BUILD_TESTING) add_test(NAME agc_metadata_pass COMMAND agc_driver_recorder_tests --metadata-pass-only) set_tests_properties(agc_metadata_pass PROPERTIES SKIP_RETURN_CODE 77) if(WIN32) - set_tests_properties(agc_recorder_read_list agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_sparse_run_import agc_sparse_run_import_copied agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_draw_inputs_copied agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass agc_tiled_resolve PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(agc_recorder_read_list agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_sparse_run_import agc_sparse_run_import_copied agc_unit_shadow agc_idle_publish_wait agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_draw_snapshot_epoch agc_multisample_guest_access agc_draw_inputs_in_place agc_draw_inputs_copied agc_resident_present agc_slice_dirty agc_queued_write_groups agc_metadata_pass agc_tiled_resolve PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") add_test(NAME agc_host_import_second_mapping COMMAND agc_driver_recorder_tests --host-import-second-mapping-only) set_tests_properties(agc_host_import_second_mapping PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp index a70da26def..53c21f2c17 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp @@ -3674,6 +3674,18 @@ bool StorageTexture::pendingUnitInside(std::uint64_t address, std::size_t bytes) return false; } +namespace { + +void waitForIdlePublish(const char* reason) { + if (reason == nullptr || std::strcmp(reason, "idle") != 0 || Recorder::InCompletion()) return; + if (auto* recorder = Recorder::Active()) { + Recorder::CountSync(0); + recorder->Sync(); + } +} + +} + bool StorageTexture::FlushPending(std::uint64_t address, std::size_t bytes, const StorageTexture* except, const char* reason, PublishScope scope, bool* published) { static_cast(FlushDepthPlanes(address, bytes)); if (published != nullptr) *published = false; @@ -3767,6 +3779,7 @@ bool StorageTexture::FlushPending(std::uint64_t address, std::size_t bytes, cons // import, so they are published under the same hold, after every store. if (scope != PublishScope::None) { const auto units = PublishShadow(address, bytes, scope, PublishReasonFor(reason)); + if (units != 0) waitForIdlePublish(reason); if (published != nullptr) *published = units != 0; } return true; @@ -3932,7 +3945,9 @@ void StorageTexture::awaitStoredSave(const std::vector* layers) { bool StorageTexture::PublishShadowsOnly(std::uint64_t address, std::size_t bytes, PublishScope scope, const char* reason) { if (scope == PublishScope::None || !AnyShadowedOverlaps(address, bytes)) return false; std::lock_guard gpu(GuestMemory::GpuMutex()); - return PublishShadow(address, bytes, scope, PublishReasonFor(reason)) != 0; + if (PublishShadow(address, bytes, scope, PublishReasonFor(reason)) == 0) return false; + waitForIdlePublish(reason); + return true; } namespace { diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 497adb234f..6dbf0f5d32 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -4677,6 +4677,79 @@ bool unitShadowTests(const Device& device, Recorder& recorder) { return true; } +void idlePublishWaitTests(const Device& device, Recorder& recorder) { + using namespace AgcDriver::GuestMemory; + const auto& context = device.GetContext(); + constexpr std::uint64_t unit = 65536; + constexpr std::size_t bytes = 8 * unit; + void* block = AllocateWatched(bytes, 65536); + Require(block != nullptr, "unit shadows are on without write watching"); + struct Release { + void* block; + ~Release() { ReleaseWatched(block, bytes); } + } release{block}; + std::memset(block, 0x11, bytes); + const auto address = reinterpret_cast(block); + { + GuestAllocations::Mutation mutation; + mutation.Add(block, bytes, true, true, true); + } + struct Unregister { + const Context& context; + void* block; + std::uint64_t address; + ~Unregister() { + { + GuestAllocations::Mutation mutation; + mutation.Remove(block); + } + HostImportFor(context, address, bytes); + } + } unregister{context, block, address}; + const auto* import = HostImportFor(context, address, bytes); + if (import == nullptr || !Watched(address, bytes)) { + std::cout << "host import of the idle publish test block refused or not watched: idle publish wait not tested\n"; + return; + } + recorder.Sync(); + CollectWritesUncached(address, bytes); + auto* words = static_cast(block); + const auto copyBuffer = context.Function("vkCmdCopyBuffer"); + const auto retile = [&](std::uint64_t begin, std::uint8_t value) { + auto destination = ShadowDestinationFor(context, *import, begin, begin + unit); + Require(destination.has_value() && destination->seedUnits.empty(), "a whole-unit retile piece has no destination"); + auto pattern = std::make_shared(context, static_cast(unit), VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + std::memset(pattern->Bytes().data(), value, pattern->Bytes().size()); + const auto commands = recorder.Commands(); + recorder.Keep(pattern); + recorder.Keep(destination->slab); + RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + const VkBufferCopy piece{0, destination->offset, unit}; + copyBuffer(commands, pattern->Handle(), destination->buffer, 1, &piece); + RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + recorder.MarkCovered(VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + const ShadowedRange range{begin, begin + unit, destination->slab}; + MarkShadowed(*import, std::span(&range, 1), TrackerGeneration()); + Require(AnyShadowedOverlaps(begin, unit), "the retiled unit is not shadowed"); + }; + retile(address + 2 * unit, 0x22); + retile(address + 3 * unit, 0x33); + StorageTexture::FlushAllPending("idle"); + Require(!AnyShadowedOverlaps(address + 2 * unit, static_cast(2 * unit)), "the idle flush did not publish the shadowed units"); + Require(!recorder.PendingWriteOverlaps(address + 2 * unit, static_cast(2 * unit)), "the idle flush returned before its publish's copies completed"); + words[2 * unit] = 0x44; + words[4 * unit - 1] = 0x45; + recorder.Sync(); + Require(words[2 * unit] == 0x44 && words[4 * unit - 1] == 0x45, "a CPU store made after the idle flush returned was overwritten by its publish"); + Require(words[2 * unit + 1] == 0x22 && words[3 * unit - 1] == 0x22 && words[3 * unit] == 0x33 && words[1 * unit] == 0x11 && words[4 * unit] == 0x11, "the idle publish's bytes are wrong"); + retile(address + 5 * unit, 0x55); + StorageTexture::FlushAllPending("label"); + Require(recorder.PendingWriteOverlaps(address + 5 * unit, unit), "a publish of a flush other than the idle one was waited"); + recorder.Sync(); + Require(words[5 * unit] == 0x55 && words[6 * unit - 1] == 0x55, "the label flush's publish did not land"); + std::cout << "idle publish wait verified\n"; +} + void storageRefreshTests(const Device& device, Recorder& recorder, bool watched) { const auto& base = device.GetContext(); if (base.hostImportAlignment == 0) { @@ -10164,6 +10237,14 @@ int main(int argc, char** argv) { parkedImportUnmapTests(device); return 0; } + if (argc == 2 && std::string_view(argv[1]) == "--idle-publish-wait-only") { + if (!AgcDriver::Graphics::UnitShadowEnabled() || device.GetContext().hostImportAlignment == 0) { + std::cout << "skipped, unit shadows or host imports are unavailable\n"; + return 77; + } + idlePublishWaitTests(device, recorder); + return 0; + } if (argc == 2 && std::string_view(argv[1]) == "--unit-shadow-only") { if (!AgcDriver::Graphics::UnitShadowEnabled() || device.GetContext().hostImportAlignment == 0) { std::cout << "skipped, unit shadows or host imports are unavailable\n"; From ed921e225fae906ca55d540413bf56cc41a382fe Mon Sep 17 00:00:00 2001 From: Joey Fisher Date: Sun, 18 Oct 2026 22:13:20 -0600 Subject: [PATCH 123/523] fix(agc): hash the pages of an import window instead of copying them ImportWatched kept a copy of every non-zero page of the range being imported, so that a page the write watch reported during the import could be told from one that still held its bytes. The copy had the range's footprint: a first import of a 0.5-3 GiB guest heap held that much host memory while it ran. The window now keeps a 64-bit digest per 4 KiB page (four xxHash64 lanes) and compares digests: 8 bytes per page, 1/512 of the range. Same decision for every page, same reads of guest memory. CheckImportWindowFootprint imports a 64 MiB watched range and requires the heap held during the import to stay under 1 MiB. It failed with "held 64 MiB" before this change. --- .../Execution/src/GuestMemory.cpp | 42 +++++++++++-------- .../libSceAgcDriver/tests/WriteTracking.cpp | 36 ++++++++++++++++ 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp index 6e7a43a575..bdd6f4f160 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp @@ -813,18 +813,33 @@ constexpr std::size_t WritePagesPerBlock = WriteBlockBytes / WritePageBytes; enum class StampKind : std::uint8_t { Cpu, Driver, ImportWindow, Fresh }; -constexpr std::array ZeroPage{}; +std::uint64_t HashPage(const std::byte* page) { + constexpr std::uint64_t prime1 = 0x9E3779B185EBCA87ull; + constexpr std::uint64_t prime2 = 0xC2B2AE3D27D4EB4Full; + constexpr std::uint64_t prime3 = 0x165667B19E3779F9ull; + constexpr std::uint64_t prime4 = 0x85EBCA77C2B2AE63ull; + const auto round = [](std::uint64_t accumulator, std::uint64_t word) { return std::rotl(accumulator + word * prime2, 31) * prime1; }; + std::array lanes{prime1 + prime2, prime2, 0, 0 - prime1}; + for (std::size_t offset = 0; offset < WritePageBytes; offset += 4 * sizeof(std::uint64_t)) { + std::array words; + std::memcpy(words.data(), page + offset, sizeof(words)); + for (std::size_t lane = 0; lane < 4; ++lane) lanes[lane] = round(lanes[lane], words[lane]); + } + std::uint64_t hash = std::rotl(lanes[0], 1) + std::rotl(lanes[1], 7) + std::rotl(lanes[2], 12) + std::rotl(lanes[3], 18); + for (const auto lane : lanes) hash = (hash ^ round(0, lane)) * prime1 + prime4; + hash ^= hash >> 33; + hash *= prime2; + hash ^= hash >> 29; + hash *= prime3; + return hash ^ (hash >> 32); +} struct WindowCopy { - static constexpr std::uint32_t Zero = std::numeric_limits::max(); std::uint64_t first; - std::span bytes; - std::span slots; + std::span hashes; bool Holds(std::uintptr_t page) const { - const auto slot = slots[(page - first) / WritePageBytes]; - const auto* expected = slot == Zero ? ZeroPage.data() : bytes.data() + static_cast(slot) * WritePageBytes; - return std::memcmp(reinterpret_cast(page), expected, WritePageBytes) == 0; + return HashPage(reinterpret_cast(page)) == hashes[(page - first) / WritePageBytes]; } }; @@ -1567,23 +1582,16 @@ bool ImportWatched(std::uint64_t address, std::size_t bytes, const std::function begin = end; } } - std::vector slots(static_cast((stop - first) / page), WindowCopy::Zero); - std::size_t kept = 0; + std::vector hashes(static_cast((stop - first) / page), 0); for (const auto& [begin, end] : spans) { CheckRange(reinterpret_cast(begin), static_cast(end - begin), 1, false); for (auto cursor = begin; cursor < end; cursor += page) { - if (std::memcmp(reinterpret_cast(cursor), ZeroPage.data(), page) != 0) slots[static_cast((cursor - first) / page)] = static_cast(kept++); + hashes[static_cast((cursor - first) / page)] = HashPage(reinterpret_cast(cursor)); } } - std::vector saved; - saved.reserve(kept * page); - const auto* source = reinterpret_cast(first); - for (std::size_t index = 0; index < slots.size(); ++index) { - if (slots[index] != WindowCopy::Zero) saved.insert(saved.end(), source + index * page, source + (index + 1) * page); - } std::erase_if(spans, [&](const auto& span) { return !walkWrites(tracker, span.first, span.second, StampKind::Cpu, true); }); if (!runImport()) return false; - const WindowCopy window{first, saved, slots}; + const WindowCopy window{first, hashes}; auto unwatchedFrom = first; for (const auto& [begin, end] : spans) { if (!walkWrites(tracker, begin, end, StampKind::Cpu, true, &window)) continue; diff --git a/core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp b/core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp index 700455ddd0..e7ad973bba 100644 --- a/core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp @@ -12,6 +12,9 @@ #include #include #endif +#ifdef __GLIBC__ +#include +#endif #include #include #include @@ -1153,6 +1156,38 @@ void CheckImportWindowOverUnwatchedBlock() { Require(after != 0 && !UnchangedSince(base, Block, after), "a store after an import window over an unwatched block was missed"); } +void CheckImportWindowFootprint() { +#ifdef __GLIBC__ + constexpr std::size_t bytes = std::size_t{64} << 20; + void* memory = AllocateWatched(bytes); + const auto base = reinterpret_cast(memory); + std::memset(memory, 0x5a, bytes); + Require(CollectWritesUncached(base, bytes) != 0, "the footprint import window block is not collected"); + const auto heapBytes = [] { + const auto info = mallinfo2(); + return info.uordblks + info.hblkhd; + }; + const auto before = heapBytes(); + std::size_t during = 0; + Require(ImportWatched(base, bytes, [&] { + during = heapBytes(); + return true; + }), "the footprint import window import failed"); + Require(Watched(base, bytes), "the footprint import window left its range unwatched"); + const auto held = during > before ? during - before : 0; + Require(held <= bytes / 64, "an import window over " + std::to_string(bytes >> 20) + " MiB held " + std::to_string(held >> 20) + " MiB of host memory during the import: it copies the range instead of hashing its pages"); + auto* plain = static_cast(memory); + const auto synced = CollectWritesUncached(base, bytes); + Require(ImportWatched(base, bytes, [&] { + plain[0x100] = 0x5a; + plain[Block + 0x100] = 0x5b; + return true; + }), "the footprint import window re-import failed"); + Require(UnchangedSince(base, Block, synced), "a page a hashed import window resolved without changing its bytes reads as written"); + Require(WrittenSince(base + Block, Block, synced), "a store of new bytes within a hashed import window was missed"); +#endif +} + void ProtectPage(std::uint64_t address, bool readable) { #ifdef _WIN32 DWORD previous = 0; @@ -1442,6 +1477,7 @@ int main() { CheckImportWindowOtherThread(); CheckImportWindowOverUnwatchedBlock(); CheckStoreAcrossImports(); + CheckImportWindowFootprint(); CheckUnwatchedImport(); CheckCommittedRanges(); CheckAdoptLatestEpoch(); From 67cea1c314d87ed64d795d0354ad704cc9550908 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Wed, 21 Oct 2026 10:27:24 +0000 Subject: [PATCH 124/523] test(agc): refuse a DB depth copy from a depth surface the device cannot sample On a device that cannot sample two-sample depth, as lavapipe, the DB depth copy case now also creates a two-sample surface and expects the copy's sampling refusal, so that refusal is covered where it applies. Devices that sample two-sample depth skip it. Co-authored-by: simonbbby <159568960+simonbbby@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- .../prx/libSceAgcDriver/tests/execution/DepthStorage.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp index 4aba6f4a27..27236f4199 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/DepthStorage.cpp @@ -355,6 +355,12 @@ void DepthCopyCase(Context context, std::uint8_t* block) { ExpectFailure([&] { copy(samples, 0, {Side / 2, Side / 2}); }, "of another size", "a DB depth copy of another extent than the depth surface was accepted"); ClearDepthSurfaces(context.device); } + if ((context.limits.sampledImageDepthSampleCounts & VK_SAMPLE_COUNT_2_BIT) == 0) { + Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.375f, 0, 0, false, 2}) != VK_NULL_HANDLE, "the unsampleable two-sample depth surface was not created"); + ExpectFailure([&] { copy(2, 0, {Side, Side}); }, "cannot sample at its sample count", "a DB depth copy from an unsampleable two-sample depth surface was accepted"); + ClearDepthSurfaces(context.device); + std::puts("DB depth copy: unsupported two-sample source refused cleanly"); + } Require(DepthSurfaceView(context, DepthTarget{depthAddress, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 0.625f, 0, 0, false, 1}) != VK_NULL_HANDLE, "the one-sample depth surface was not created"); std::fill(block, block + BlockBytes, std::uint8_t{0}); copy(1, 0, {Side, Side}); From eb2aae7f8b58d7a38230230e6707f2157829917d Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Wed, 21 Oct 2026 23:40:11 +1300 Subject: [PATCH 125/523] fix(agc): refresh every layer a layered array color draw attaches A slice of a 2D array color target was refreshed one layer wide while its attachment view spans color.layers, so a layered draw refreshed only its first slice. Pass the attached layer count. --- core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index 94c47bf27a..8f8a1dd2bf 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -2235,7 +2235,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters& const auto geometry = DescribeSurface(array); for (std::uint32_t slice = 0; slice <= color.arrayLastSlice; ++slice) Require(geometry.GuestLayerOffset(slice) == slice * sliceBytes, "the array image's layer pitch differs from the color view's slice offset"); Require(color.surfaceAddress == color.arrayAddress + color.arraySlice * sliceBytes, "the array image's layer pitch differs from the color view's slice offset"); - auto resident = CachedStorageLayers(context, array, color.arraySlice, 1); + auto resident = CachedStorageLayers(context, array, color.arraySlice, color.layers); Require(resident->Attachable(), "storage format cannot be a color attachment"); return resident; } From dc7be1b0729b62903180ea3cab4a4c1ce0dd5c5d Mon Sep 17 00:00:00 2001 From: jacob6769 Date: Mon, 19 Oct 2026 21:19:58 +0900 Subject: [PATCH 126/523] feat(libSceCesCs): convert EUC-KR, UHC and Big5 through the CP949 and CP950 tables Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceCesCs/CMakeLists.txt | 10 +- core/libs/prx/libSceCesCs/Cp949.hpp | 136 +++++++++++++++++ core/libs/prx/libSceCesCs/Cp950.hpp | 99 ++++++++++++ core/libs/prx/libSceCesCs/Export.cpp | 182 +++++++++++++++++++---- core/libs/tests/GuestCesDbcs.cpp | 179 ++++++++++++++++++++++ docs/dev/debt/libSceCesCs.md | 1 + 6 files changed, 580 insertions(+), 27 deletions(-) create mode 100644 core/libs/prx/libSceCesCs/Cp949.hpp create mode 100644 core/libs/prx/libSceCesCs/Cp950.hpp create mode 100644 core/libs/tests/GuestCesDbcs.cpp diff --git a/core/libs/prx/libSceCesCs/CMakeLists.txt b/core/libs/prx/libSceCesCs/CMakeLists.txt index 2601651d05..c9d9076534 100644 --- a/core/libs/prx/libSceCesCs/CMakeLists.txt +++ b/core/libs/prx/libSceCesCs/CMakeLists.txt @@ -10,8 +10,16 @@ if(BUILD_TESTING) target_include_directories(guest_ces_module_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_ces_module_tests PRIVATE libSceCesCs-module) add_test(NAME guest_ces_module COMMAND guest_ces_module_tests) + add_test_executable(guest_ces_dbcs_tests ${LIBS_INCLUDE_DIR}/tests/GuestCesDbcs.cpp) + target_include_directories(guest_ces_dbcs_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_ces_dbcs_tests PRIVATE libSceCesCs) + add_test(NAME guest_ces_dbcs COMMAND guest_ces_dbcs_tests) + add_test_executable(guest_ces_dbcs_module_tests ${LIBS_INCLUDE_DIR}/tests/GuestCesDbcs.cpp) + target_include_directories(guest_ces_dbcs_module_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_ces_dbcs_module_tests PRIVATE libSceCesCs-module) + add_test(NAME guest_ces_dbcs_module COMMAND guest_ces_dbcs_module_tests) if(WIN32) - set_tests_properties(guest_ces guest_ces_module PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(guest_ces guest_ces_module guest_ces_dbcs guest_ces_dbcs_module PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/prx/libSceCesCs/Cp949.hpp b/core/libs/prx/libSceCesCs/Cp949.hpp new file mode 100644 index 0000000000..ec0166ce13 --- /dev/null +++ b/core/libs/prx/libSceCesCs/Cp949.hpp @@ -0,0 +1,136 @@ +#pragma once + +#include + +namespace Cp949 { + +inline constexpr uint16_t TO_UCS2[126 * 190] = { + 0xAC02, 0xAC03, 0xAC05, 0xAC06, 0xAC0B, 0xAC0C, 0xAC0D, 0xAC0E, 0xAC0F, 0xAC18, 0xAC1E, 0xAC1F, 0xAC21, 0xAC22, 0xAC23, 0xAC25, 0xAC26, 0xAC27, 0xAC28, 0xAC29, 0xAC2A, 0xAC2B, 0xAC2E, 0xAC32, 0xAC33, 0xAC34, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAC35, 0xAC36, 0xAC37, 0xAC3A, 0xAC3B, 0xAC3D, 0xAC3E, 0xAC3F, 0xAC41, 0xAC42, 0xAC43, 0xAC44, 0xAC45, 0xAC46, 0xAC47, 0xAC48, 0xAC49, 0xAC4A, 0xAC4C, 0xAC4E, 0xAC4F, 0xAC50, 0xAC51, 0xAC52, 0xAC53, 0xAC55, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAC56, 0xAC57, 0xAC59, 0xAC5A, 0xAC5B, 0xAC5D, 0xAC5E, 0xAC5F, 0xAC60, 0xAC61, 0xAC62, 0xAC63, 0xAC64, 0xAC65, 0xAC66, 0xAC67, 0xAC68, 0xAC69, 0xAC6A, 0xAC6B, 0xAC6C, 0xAC6D, 0xAC6E, 0xAC6F, 0xAC72, 0xAC73, 0xAC75, 0xAC76, 0xAC79, 0xAC7B, 0xAC7C, 0xAC7D, 0xAC7E, 0xAC7F, 0xAC82, 0xAC87, 0xAC88, 0xAC8D, 0xAC8E, 0xAC8F, 0xAC91, 0xAC92, 0xAC93, 0xAC95, 0xAC96, 0xAC97, 0xAC98, 0xAC99, 0xAC9A, 0xAC9B, 0xAC9E, 0xACA2, 0xACA3, 0xACA4, 0xACA5, 0xACA6, 0xACA7, 0xACAB, 0xACAD, 0xACAE, 0xACB1, 0xACB2, 0xACB3, 0xACB4, 0xACB5, 0xACB6, 0xACB7, 0xACBA, 0xACBE, 0xACBF, 0xACC0, 0xACC2, 0xACC3, 0xACC5, 0xACC6, 0xACC7, 0xACC9, 0xACCA, 0xACCB, 0xACCD, 0xACCE, 0xACCF, 0xACD0, 0xACD1, 0xACD2, 0xACD3, 0xACD4, 0xACD6, 0xACD8, 0xACD9, 0xACDA, 0xACDB, 0xACDC, 0xACDD, 0xACDE, 0xACDF, 0xACE2, 0xACE3, 0xACE5, 0xACE6, 0xACE9, 0xACEB, 0xACED, 0xACEE, 0xACF2, 0xACF4, 0xACF7, 0xACF8, 0xACF9, 0xACFA, 0xACFB, 0xACFE, 0xACFF, 0xAD01, 0xAD02, 0xAD03, 0xAD05, 0xAD07, 0xAD08, 0xAD09, 0xAD0A, 0xAD0B, 0xAD0E, 0xAD10, 0xAD12, 0xAD13, + 0xAD14, 0xAD15, 0xAD16, 0xAD17, 0xAD19, 0xAD1A, 0xAD1B, 0xAD1D, 0xAD1E, 0xAD1F, 0xAD21, 0xAD22, 0xAD23, 0xAD24, 0xAD25, 0xAD26, 0xAD27, 0xAD28, 0xAD2A, 0xAD2B, 0xAD2E, 0xAD2F, 0xAD30, 0xAD31, 0xAD32, 0xAD33, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAD36, 0xAD37, 0xAD39, 0xAD3A, 0xAD3B, 0xAD3D, 0xAD3E, 0xAD3F, 0xAD40, 0xAD41, 0xAD42, 0xAD43, 0xAD46, 0xAD48, 0xAD4A, 0xAD4B, 0xAD4C, 0xAD4D, 0xAD4E, 0xAD4F, 0xAD51, 0xAD52, 0xAD53, 0xAD55, 0xAD56, 0xAD57, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAD59, 0xAD5A, 0xAD5B, 0xAD5C, 0xAD5D, 0xAD5E, 0xAD5F, 0xAD60, 0xAD62, 0xAD64, 0xAD65, 0xAD66, 0xAD67, 0xAD68, 0xAD69, 0xAD6A, 0xAD6B, 0xAD6E, 0xAD6F, 0xAD71, 0xAD72, 0xAD77, 0xAD78, 0xAD79, 0xAD7A, 0xAD7E, 0xAD80, 0xAD83, 0xAD84, 0xAD85, 0xAD86, 0xAD87, 0xAD8A, 0xAD8B, 0xAD8D, 0xAD8E, 0xAD8F, 0xAD91, 0xAD92, 0xAD93, 0xAD94, 0xAD95, 0xAD96, 0xAD97, 0xAD98, 0xAD99, 0xAD9A, 0xAD9B, 0xAD9E, 0xAD9F, 0xADA0, 0xADA1, 0xADA2, 0xADA3, 0xADA5, 0xADA6, 0xADA7, 0xADA8, 0xADA9, 0xADAA, 0xADAB, 0xADAC, 0xADAD, 0xADAE, 0xADAF, 0xADB0, 0xADB1, 0xADB2, 0xADB3, 0xADB4, 0xADB5, 0xADB6, 0xADB8, 0xADB9, 0xADBA, 0xADBB, 0xADBC, 0xADBD, 0xADBE, 0xADBF, 0xADC2, 0xADC3, 0xADC5, 0xADC6, 0xADC7, 0xADC9, 0xADCA, 0xADCB, 0xADCC, 0xADCD, 0xADCE, 0xADCF, 0xADD2, 0xADD4, 0xADD5, 0xADD6, 0xADD7, 0xADD8, 0xADD9, 0xADDA, 0xADDB, 0xADDD, 0xADDE, 0xADDF, 0xADE1, 0xADE2, 0xADE3, 0xADE5, 0xADE6, 0xADE7, 0xADE8, 0xADE9, 0xADEA, 0xADEB, 0xADEC, 0xADED, 0xADEE, 0xADEF, 0xADF0, 0xADF1, 0xADF2, 0xADF3, 0xADF4, 0xADF5, 0xADF6, 0xADF7, + 0xADFA, 0xADFB, 0xADFD, 0xADFE, 0xAE02, 0xAE03, 0xAE04, 0xAE05, 0xAE06, 0xAE07, 0xAE0A, 0xAE0C, 0xAE0E, 0xAE0F, 0xAE10, 0xAE11, 0xAE12, 0xAE13, 0xAE15, 0xAE16, 0xAE17, 0xAE18, 0xAE19, 0xAE1A, 0xAE1B, 0xAE1C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAE1D, 0xAE1E, 0xAE1F, 0xAE20, 0xAE21, 0xAE22, 0xAE23, 0xAE24, 0xAE25, 0xAE26, 0xAE27, 0xAE28, 0xAE29, 0xAE2A, 0xAE2B, 0xAE2C, 0xAE2D, 0xAE2E, 0xAE2F, 0xAE32, 0xAE33, 0xAE35, 0xAE36, 0xAE39, 0xAE3B, 0xAE3C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAE3D, 0xAE3E, 0xAE3F, 0xAE42, 0xAE44, 0xAE47, 0xAE48, 0xAE49, 0xAE4B, 0xAE4F, 0xAE51, 0xAE52, 0xAE53, 0xAE55, 0xAE57, 0xAE58, 0xAE59, 0xAE5A, 0xAE5B, 0xAE5E, 0xAE62, 0xAE63, 0xAE64, 0xAE66, 0xAE67, 0xAE6A, 0xAE6B, 0xAE6D, 0xAE6E, 0xAE6F, 0xAE71, 0xAE72, 0xAE73, 0xAE74, 0xAE75, 0xAE76, 0xAE77, 0xAE7A, 0xAE7E, 0xAE7F, 0xAE80, 0xAE81, 0xAE82, 0xAE83, 0xAE86, 0xAE87, 0xAE88, 0xAE89, 0xAE8A, 0xAE8B, 0xAE8D, 0xAE8E, 0xAE8F, 0xAE90, 0xAE91, 0xAE92, 0xAE93, 0xAE94, 0xAE95, 0xAE96, 0xAE97, 0xAE98, 0xAE99, 0xAE9A, 0xAE9B, 0xAE9C, 0xAE9D, 0xAE9E, 0xAE9F, 0xAEA0, 0xAEA1, 0xAEA2, 0xAEA3, 0xAEA4, 0xAEA5, 0xAEA6, 0xAEA7, 0xAEA8, 0xAEA9, 0xAEAA, 0xAEAB, 0xAEAC, 0xAEAD, 0xAEAE, 0xAEAF, 0xAEB0, 0xAEB1, 0xAEB2, 0xAEB3, 0xAEB4, 0xAEB5, 0xAEB6, 0xAEB7, 0xAEB8, 0xAEB9, 0xAEBA, 0xAEBB, 0xAEBF, 0xAEC1, 0xAEC2, 0xAEC3, 0xAEC5, 0xAEC6, 0xAEC7, 0xAEC8, 0xAEC9, 0xAECA, 0xAECB, 0xAECE, 0xAED2, 0xAED3, 0xAED4, 0xAED5, 0xAED6, 0xAED7, 0xAEDA, 0xAEDB, 0xAEDD, 0xAEDE, 0xAEDF, 0xAEE0, 0xAEE1, 0xAEE2, 0xAEE3, 0xAEE4, 0xAEE5, + 0xAEE6, 0xAEE7, 0xAEE9, 0xAEEA, 0xAEEC, 0xAEEE, 0xAEEF, 0xAEF0, 0xAEF1, 0xAEF2, 0xAEF3, 0xAEF5, 0xAEF6, 0xAEF7, 0xAEF9, 0xAEFA, 0xAEFB, 0xAEFD, 0xAEFE, 0xAEFF, 0xAF00, 0xAF01, 0xAF02, 0xAF03, 0xAF04, 0xAF05, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAF06, 0xAF09, 0xAF0A, 0xAF0B, 0xAF0C, 0xAF0E, 0xAF0F, 0xAF11, 0xAF12, 0xAF13, 0xAF14, 0xAF15, 0xAF16, 0xAF17, 0xAF18, 0xAF19, 0xAF1A, 0xAF1B, 0xAF1C, 0xAF1D, 0xAF1E, 0xAF1F, 0xAF20, 0xAF21, 0xAF22, 0xAF23, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAF24, 0xAF25, 0xAF26, 0xAF27, 0xAF28, 0xAF29, 0xAF2A, 0xAF2B, 0xAF2E, 0xAF2F, 0xAF31, 0xAF33, 0xAF35, 0xAF36, 0xAF37, 0xAF38, 0xAF39, 0xAF3A, 0xAF3B, 0xAF3E, 0xAF40, 0xAF44, 0xAF45, 0xAF46, 0xAF47, 0xAF4A, 0xAF4B, 0xAF4C, 0xAF4D, 0xAF4E, 0xAF4F, 0xAF51, 0xAF52, 0xAF53, 0xAF54, 0xAF55, 0xAF56, 0xAF57, 0xAF58, 0xAF59, 0xAF5A, 0xAF5B, 0xAF5E, 0xAF5F, 0xAF60, 0xAF61, 0xAF62, 0xAF63, 0xAF66, 0xAF67, 0xAF68, 0xAF69, 0xAF6A, 0xAF6B, 0xAF6C, 0xAF6D, 0xAF6E, 0xAF6F, 0xAF70, 0xAF71, 0xAF72, 0xAF73, 0xAF74, 0xAF75, 0xAF76, 0xAF77, 0xAF78, 0xAF7A, 0xAF7B, 0xAF7C, 0xAF7D, 0xAF7E, 0xAF7F, 0xAF81, 0xAF82, 0xAF83, 0xAF85, 0xAF86, 0xAF87, 0xAF89, 0xAF8A, 0xAF8B, 0xAF8C, 0xAF8D, 0xAF8E, 0xAF8F, 0xAF92, 0xAF93, 0xAF94, 0xAF96, 0xAF97, 0xAF98, 0xAF99, 0xAF9A, 0xAF9B, 0xAF9D, 0xAF9E, 0xAF9F, 0xAFA0, 0xAFA1, 0xAFA2, 0xAFA3, 0xAFA4, 0xAFA5, 0xAFA6, 0xAFA7, 0xAFA8, 0xAFA9, 0xAFAA, 0xAFAB, 0xAFAC, 0xAFAD, 0xAFAE, 0xAFAF, 0xAFB0, 0xAFB1, 0xAFB2, 0xAFB3, 0xAFB4, 0xAFB5, 0xAFB6, 0xAFB7, 0xAFBA, 0xAFBB, 0xAFBD, 0xAFBE, + 0xAFBF, 0xAFC1, 0xAFC2, 0xAFC3, 0xAFC4, 0xAFC5, 0xAFC6, 0xAFCA, 0xAFCC, 0xAFCF, 0xAFD0, 0xAFD1, 0xAFD2, 0xAFD3, 0xAFD5, 0xAFD6, 0xAFD7, 0xAFD8, 0xAFD9, 0xAFDA, 0xAFDB, 0xAFDD, 0xAFDE, 0xAFDF, 0xAFE0, 0xAFE1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xAFE2, 0xAFE3, 0xAFE4, 0xAFE5, 0xAFE6, 0xAFE7, 0xAFEA, 0xAFEB, 0xAFEC, 0xAFED, 0xAFEE, 0xAFEF, 0xAFF2, 0xAFF3, 0xAFF5, 0xAFF6, 0xAFF7, 0xAFF9, 0xAFFA, 0xAFFB, 0xAFFC, 0xAFFD, 0xAFFE, 0xAFFF, 0xB002, 0xB003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB005, 0xB006, 0xB007, 0xB008, 0xB009, 0xB00A, 0xB00B, 0xB00D, 0xB00E, 0xB00F, 0xB011, 0xB012, 0xB013, 0xB015, 0xB016, 0xB017, 0xB018, 0xB019, 0xB01A, 0xB01B, 0xB01E, 0xB01F, 0xB020, 0xB021, 0xB022, 0xB023, 0xB024, 0xB025, 0xB026, 0xB027, 0xB029, 0xB02A, 0xB02B, 0xB02C, 0xB02D, 0xB02E, 0xB02F, 0xB030, 0xB031, 0xB032, 0xB033, 0xB034, 0xB035, 0xB036, 0xB037, 0xB038, 0xB039, 0xB03A, 0xB03B, 0xB03C, 0xB03D, 0xB03E, 0xB03F, 0xB040, 0xB041, 0xB042, 0xB043, 0xB046, 0xB047, 0xB049, 0xB04B, 0xB04D, 0xB04F, 0xB050, 0xB051, 0xB052, 0xB056, 0xB058, 0xB05A, 0xB05B, 0xB05C, 0xB05E, 0xB05F, 0xB060, 0xB061, 0xB062, 0xB063, 0xB064, 0xB065, 0xB066, 0xB067, 0xB068, 0xB069, 0xB06A, 0xB06B, 0xB06C, 0xB06D, 0xB06E, 0xB06F, 0xB070, 0xB071, 0xB072, 0xB073, 0xB074, 0xB075, 0xB076, 0xB077, 0xB078, 0xB079, 0xB07A, 0xB07B, 0xB07E, 0xB07F, 0xB081, 0xB082, 0xB083, 0xB085, 0xB086, 0xB087, 0xB088, 0xB089, 0xB08A, 0xB08B, 0xB08E, 0xB090, 0xB092, 0xB093, 0xB094, 0xB095, 0xB096, 0xB097, 0xB09B, 0xB09D, 0xB09E, 0xB0A3, 0xB0A4, + 0xB0A5, 0xB0A6, 0xB0A7, 0xB0AA, 0xB0B0, 0xB0B2, 0xB0B6, 0xB0B7, 0xB0B9, 0xB0BA, 0xB0BB, 0xB0BD, 0xB0BE, 0xB0BF, 0xB0C0, 0xB0C1, 0xB0C2, 0xB0C3, 0xB0C6, 0xB0CA, 0xB0CB, 0xB0CC, 0xB0CD, 0xB0CE, 0xB0CF, 0xB0D2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB0D3, 0xB0D5, 0xB0D6, 0xB0D7, 0xB0D9, 0xB0DA, 0xB0DB, 0xB0DC, 0xB0DD, 0xB0DE, 0xB0DF, 0xB0E1, 0xB0E2, 0xB0E3, 0xB0E4, 0xB0E6, 0xB0E7, 0xB0E8, 0xB0E9, 0xB0EA, 0xB0EB, 0xB0EC, 0xB0ED, 0xB0EE, 0xB0EF, 0xB0F0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB0F1, 0xB0F2, 0xB0F3, 0xB0F4, 0xB0F5, 0xB0F6, 0xB0F7, 0xB0F8, 0xB0F9, 0xB0FA, 0xB0FB, 0xB0FC, 0xB0FD, 0xB0FE, 0xB0FF, 0xB100, 0xB101, 0xB102, 0xB103, 0xB104, 0xB105, 0xB106, 0xB107, 0xB10A, 0xB10D, 0xB10E, 0xB10F, 0xB111, 0xB114, 0xB115, 0xB116, 0xB117, 0xB11A, 0xB11E, 0xB11F, 0xB120, 0xB121, 0xB122, 0xB126, 0xB127, 0xB129, 0xB12A, 0xB12B, 0xB12D, 0xB12E, 0xB12F, 0xB130, 0xB131, 0xB132, 0xB133, 0xB136, 0xB13A, 0xB13B, 0xB13C, 0xB13D, 0xB13E, 0xB13F, 0xB142, 0xB143, 0xB145, 0xB146, 0xB147, 0xB149, 0xB14A, 0xB14B, 0xB14C, 0xB14D, 0xB14E, 0xB14F, 0xB152, 0xB153, 0xB156, 0xB157, 0xB159, 0xB15A, 0xB15B, 0xB15D, 0xB15E, 0xB15F, 0xB161, 0xB162, 0xB163, 0xB164, 0xB165, 0xB166, 0xB167, 0xB168, 0xB169, 0xB16A, 0xB16B, 0xB16C, 0xB16D, 0xB16E, 0xB16F, 0xB170, 0xB171, 0xB172, 0xB173, 0xB174, 0xB175, 0xB176, 0xB177, 0xB17A, 0xB17B, 0xB17D, 0xB17E, 0xB17F, 0xB181, 0xB183, 0xB184, 0xB185, 0xB186, 0xB187, 0xB18A, 0xB18C, 0xB18E, 0xB18F, 0xB190, 0xB191, 0xB195, 0xB196, 0xB197, 0xB199, 0xB19A, 0xB19B, 0xB19D, + 0xB19E, 0xB19F, 0xB1A0, 0xB1A1, 0xB1A2, 0xB1A3, 0xB1A4, 0xB1A5, 0xB1A6, 0xB1A7, 0xB1A9, 0xB1AA, 0xB1AB, 0xB1AC, 0xB1AD, 0xB1AE, 0xB1AF, 0xB1B0, 0xB1B1, 0xB1B2, 0xB1B3, 0xB1B4, 0xB1B5, 0xB1B6, 0xB1B7, 0xB1B8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB1B9, 0xB1BA, 0xB1BB, 0xB1BC, 0xB1BD, 0xB1BE, 0xB1BF, 0xB1C0, 0xB1C1, 0xB1C2, 0xB1C3, 0xB1C4, 0xB1C5, 0xB1C6, 0xB1C7, 0xB1C8, 0xB1C9, 0xB1CA, 0xB1CB, 0xB1CD, 0xB1CE, 0xB1CF, 0xB1D1, 0xB1D2, 0xB1D3, 0xB1D5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB1D6, 0xB1D7, 0xB1D8, 0xB1D9, 0xB1DA, 0xB1DB, 0xB1DE, 0xB1E0, 0xB1E1, 0xB1E2, 0xB1E3, 0xB1E4, 0xB1E5, 0xB1E6, 0xB1E7, 0xB1EA, 0xB1EB, 0xB1ED, 0xB1EE, 0xB1EF, 0xB1F1, 0xB1F2, 0xB1F3, 0xB1F4, 0xB1F5, 0xB1F6, 0xB1F7, 0xB1F8, 0xB1FA, 0xB1FC, 0xB1FE, 0xB1FF, 0xB200, 0xB201, 0xB202, 0xB203, 0xB206, 0xB207, 0xB209, 0xB20A, 0xB20D, 0xB20E, 0xB20F, 0xB210, 0xB211, 0xB212, 0xB213, 0xB216, 0xB218, 0xB21A, 0xB21B, 0xB21C, 0xB21D, 0xB21E, 0xB21F, 0xB221, 0xB222, 0xB223, 0xB224, 0xB225, 0xB226, 0xB227, 0xB228, 0xB229, 0xB22A, 0xB22B, 0xB22C, 0xB22D, 0xB22E, 0xB22F, 0xB230, 0xB231, 0xB232, 0xB233, 0xB235, 0xB236, 0xB237, 0xB238, 0xB239, 0xB23A, 0xB23B, 0xB23D, 0xB23E, 0xB23F, 0xB240, 0xB241, 0xB242, 0xB243, 0xB244, 0xB245, 0xB246, 0xB247, 0xB248, 0xB249, 0xB24A, 0xB24B, 0xB24C, 0xB24D, 0xB24E, 0xB24F, 0xB250, 0xB251, 0xB252, 0xB253, 0xB254, 0xB255, 0xB256, 0xB257, 0xB259, 0xB25A, 0xB25B, 0xB25D, 0xB25E, 0xB25F, 0xB261, 0xB262, 0xB263, 0xB264, 0xB265, 0xB266, 0xB267, 0xB26A, 0xB26B, 0xB26C, 0xB26D, 0xB26E, + 0xB26F, 0xB270, 0xB271, 0xB272, 0xB273, 0xB276, 0xB277, 0xB278, 0xB279, 0xB27A, 0xB27B, 0xB27D, 0xB27E, 0xB27F, 0xB280, 0xB281, 0xB282, 0xB283, 0xB286, 0xB287, 0xB288, 0xB28A, 0xB28B, 0xB28C, 0xB28D, 0xB28E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB28F, 0xB292, 0xB293, 0xB295, 0xB296, 0xB297, 0xB29B, 0xB29C, 0xB29D, 0xB29E, 0xB29F, 0xB2A2, 0xB2A4, 0xB2A7, 0xB2A8, 0xB2A9, 0xB2AB, 0xB2AD, 0xB2AE, 0xB2AF, 0xB2B1, 0xB2B2, 0xB2B3, 0xB2B5, 0xB2B6, 0xB2B7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB2B8, 0xB2B9, 0xB2BA, 0xB2BB, 0xB2BC, 0xB2BD, 0xB2BE, 0xB2BF, 0xB2C0, 0xB2C1, 0xB2C2, 0xB2C3, 0xB2C4, 0xB2C5, 0xB2C6, 0xB2C7, 0xB2CA, 0xB2CB, 0xB2CD, 0xB2CE, 0xB2CF, 0xB2D1, 0xB2D3, 0xB2D4, 0xB2D5, 0xB2D6, 0xB2D7, 0xB2DA, 0xB2DC, 0xB2DE, 0xB2DF, 0xB2E0, 0xB2E1, 0xB2E3, 0xB2E7, 0xB2E9, 0xB2EA, 0xB2F0, 0xB2F1, 0xB2F2, 0xB2F6, 0xB2FC, 0xB2FD, 0xB2FE, 0xB302, 0xB303, 0xB305, 0xB306, 0xB307, 0xB309, 0xB30A, 0xB30B, 0xB30C, 0xB30D, 0xB30E, 0xB30F, 0xB312, 0xB316, 0xB317, 0xB318, 0xB319, 0xB31A, 0xB31B, 0xB31D, 0xB31E, 0xB31F, 0xB320, 0xB321, 0xB322, 0xB323, 0xB324, 0xB325, 0xB326, 0xB327, 0xB328, 0xB329, 0xB32A, 0xB32B, 0xB32C, 0xB32D, 0xB32E, 0xB32F, 0xB330, 0xB331, 0xB332, 0xB333, 0xB334, 0xB335, 0xB336, 0xB337, 0xB338, 0xB339, 0xB33A, 0xB33B, 0xB33C, 0xB33D, 0xB33E, 0xB33F, 0xB340, 0xB341, 0xB342, 0xB343, 0xB344, 0xB345, 0xB346, 0xB347, 0xB348, 0xB349, 0xB34A, 0xB34B, 0xB34C, 0xB34D, 0xB34E, 0xB34F, 0xB350, 0xB351, 0xB352, 0xB353, 0xB357, 0xB359, 0xB35A, 0xB35D, 0xB360, 0xB361, 0xB362, 0xB363, + 0xB366, 0xB368, 0xB36A, 0xB36C, 0xB36D, 0xB36F, 0xB372, 0xB373, 0xB375, 0xB376, 0xB377, 0xB379, 0xB37A, 0xB37B, 0xB37C, 0xB37D, 0xB37E, 0xB37F, 0xB382, 0xB386, 0xB387, 0xB388, 0xB389, 0xB38A, 0xB38B, 0xB38D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB38E, 0xB38F, 0xB391, 0xB392, 0xB393, 0xB395, 0xB396, 0xB397, 0xB398, 0xB399, 0xB39A, 0xB39B, 0xB39C, 0xB39D, 0xB39E, 0xB39F, 0xB3A2, 0xB3A3, 0xB3A4, 0xB3A5, 0xB3A6, 0xB3A7, 0xB3A9, 0xB3AA, 0xB3AB, 0xB3AD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB3AE, 0xB3AF, 0xB3B0, 0xB3B1, 0xB3B2, 0xB3B3, 0xB3B4, 0xB3B5, 0xB3B6, 0xB3B7, 0xB3B8, 0xB3B9, 0xB3BA, 0xB3BB, 0xB3BC, 0xB3BD, 0xB3BE, 0xB3BF, 0xB3C0, 0xB3C1, 0xB3C2, 0xB3C3, 0xB3C6, 0xB3C7, 0xB3C9, 0xB3CA, 0xB3CD, 0xB3CF, 0xB3D1, 0xB3D2, 0xB3D3, 0xB3D6, 0xB3D8, 0xB3DA, 0xB3DC, 0xB3DE, 0xB3DF, 0xB3E1, 0xB3E2, 0xB3E3, 0xB3E5, 0xB3E6, 0xB3E7, 0xB3E9, 0xB3EA, 0xB3EB, 0xB3EC, 0xB3ED, 0xB3EE, 0xB3EF, 0xB3F0, 0xB3F1, 0xB3F2, 0xB3F3, 0xB3F4, 0xB3F5, 0xB3F6, 0xB3F7, 0xB3F8, 0xB3F9, 0xB3FA, 0xB3FB, 0xB3FD, 0xB3FE, 0xB3FF, 0xB400, 0xB401, 0xB402, 0xB403, 0xB404, 0xB405, 0xB406, 0xB407, 0xB408, 0xB409, 0xB40A, 0xB40B, 0xB40C, 0xB40D, 0xB40E, 0xB40F, 0xB411, 0xB412, 0xB413, 0xB414, 0xB415, 0xB416, 0xB417, 0xB419, 0xB41A, 0xB41B, 0xB41D, 0xB41E, 0xB41F, 0xB421, 0xB422, 0xB423, 0xB424, 0xB425, 0xB426, 0xB427, 0xB42A, 0xB42C, 0xB42D, 0xB42E, 0xB42F, 0xB430, 0xB431, 0xB432, 0xB433, 0xB435, 0xB436, 0xB437, 0xB438, 0xB439, 0xB43A, 0xB43B, 0xB43C, 0xB43D, 0xB43E, 0xB43F, 0xB440, 0xB441, 0xB442, 0xB443, 0xB444, + 0xB445, 0xB446, 0xB447, 0xB448, 0xB449, 0xB44A, 0xB44B, 0xB44C, 0xB44D, 0xB44E, 0xB44F, 0xB452, 0xB453, 0xB455, 0xB456, 0xB457, 0xB459, 0xB45A, 0xB45B, 0xB45C, 0xB45D, 0xB45E, 0xB45F, 0xB462, 0xB464, 0xB466, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB467, 0xB468, 0xB469, 0xB46A, 0xB46B, 0xB46D, 0xB46E, 0xB46F, 0xB470, 0xB471, 0xB472, 0xB473, 0xB474, 0xB475, 0xB476, 0xB477, 0xB478, 0xB479, 0xB47A, 0xB47B, 0xB47C, 0xB47D, 0xB47E, 0xB47F, 0xB481, 0xB482, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB483, 0xB484, 0xB485, 0xB486, 0xB487, 0xB489, 0xB48A, 0xB48B, 0xB48C, 0xB48D, 0xB48E, 0xB48F, 0xB490, 0xB491, 0xB492, 0xB493, 0xB494, 0xB495, 0xB496, 0xB497, 0xB498, 0xB499, 0xB49A, 0xB49B, 0xB49C, 0xB49E, 0xB49F, 0xB4A0, 0xB4A1, 0xB4A2, 0xB4A3, 0xB4A5, 0xB4A6, 0xB4A7, 0xB4A9, 0xB4AA, 0xB4AB, 0xB4AD, 0xB4AE, 0xB4AF, 0xB4B0, 0xB4B1, 0xB4B2, 0xB4B3, 0xB4B4, 0xB4B6, 0xB4B8, 0xB4BA, 0xB4BB, 0xB4BC, 0xB4BD, 0xB4BE, 0xB4BF, 0xB4C1, 0xB4C2, 0xB4C3, 0xB4C5, 0xB4C6, 0xB4C7, 0xB4C9, 0xB4CA, 0xB4CB, 0xB4CC, 0xB4CD, 0xB4CE, 0xB4CF, 0xB4D1, 0xB4D2, 0xB4D3, 0xB4D4, 0xB4D6, 0xB4D7, 0xB4D8, 0xB4D9, 0xB4DA, 0xB4DB, 0xB4DE, 0xB4DF, 0xB4E1, 0xB4E2, 0xB4E5, 0xB4E7, 0xB4E8, 0xB4E9, 0xB4EA, 0xB4EB, 0xB4EE, 0xB4F0, 0xB4F2, 0xB4F3, 0xB4F4, 0xB4F5, 0xB4F6, 0xB4F7, 0xB4F9, 0xB4FA, 0xB4FB, 0xB4FC, 0xB4FD, 0xB4FE, 0xB4FF, 0xB500, 0xB501, 0xB502, 0xB503, 0xB504, 0xB505, 0xB506, 0xB507, 0xB508, 0xB509, 0xB50A, 0xB50B, 0xB50C, 0xB50D, 0xB50E, 0xB50F, 0xB510, 0xB511, 0xB512, 0xB513, 0xB516, 0xB517, 0xB519, 0xB51A, 0xB51D, + 0xB51E, 0xB51F, 0xB520, 0xB521, 0xB522, 0xB523, 0xB526, 0xB52B, 0xB52C, 0xB52D, 0xB52E, 0xB52F, 0xB532, 0xB533, 0xB535, 0xB536, 0xB537, 0xB539, 0xB53A, 0xB53B, 0xB53C, 0xB53D, 0xB53E, 0xB53F, 0xB542, 0xB546, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB547, 0xB548, 0xB549, 0xB54A, 0xB54E, 0xB54F, 0xB551, 0xB552, 0xB553, 0xB555, 0xB556, 0xB557, 0xB558, 0xB559, 0xB55A, 0xB55B, 0xB55E, 0xB562, 0xB563, 0xB564, 0xB565, 0xB566, 0xB567, 0xB568, 0xB569, 0xB56A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB56B, 0xB56C, 0xB56D, 0xB56E, 0xB56F, 0xB570, 0xB571, 0xB572, 0xB573, 0xB574, 0xB575, 0xB576, 0xB577, 0xB578, 0xB579, 0xB57A, 0xB57B, 0xB57C, 0xB57D, 0xB57E, 0xB57F, 0xB580, 0xB581, 0xB582, 0xB583, 0xB584, 0xB585, 0xB586, 0xB587, 0xB588, 0xB589, 0xB58A, 0xB58B, 0xB58C, 0xB58D, 0xB58E, 0xB58F, 0xB590, 0xB591, 0xB592, 0xB593, 0xB594, 0xB595, 0xB596, 0xB597, 0xB598, 0xB599, 0xB59A, 0xB59B, 0xB59C, 0xB59D, 0xB59E, 0xB59F, 0xB5A2, 0xB5A3, 0xB5A5, 0xB5A6, 0xB5A7, 0xB5A9, 0xB5AC, 0xB5AD, 0xB5AE, 0xB5AF, 0xB5B2, 0xB5B6, 0xB5B7, 0xB5B8, 0xB5B9, 0xB5BA, 0xB5BE, 0xB5BF, 0xB5C1, 0xB5C2, 0xB5C3, 0xB5C5, 0xB5C6, 0xB5C7, 0xB5C8, 0xB5C9, 0xB5CA, 0xB5CB, 0xB5CE, 0xB5D2, 0xB5D3, 0xB5D4, 0xB5D5, 0xB5D6, 0xB5D7, 0xB5D9, 0xB5DA, 0xB5DB, 0xB5DC, 0xB5DD, 0xB5DE, 0xB5DF, 0xB5E0, 0xB5E1, 0xB5E2, 0xB5E3, 0xB5E4, 0xB5E5, 0xB5E6, 0xB5E7, 0xB5E8, 0xB5E9, 0xB5EA, 0xB5EB, 0xB5ED, 0xB5EE, 0xB5EF, 0xB5F0, 0xB5F1, 0xB5F2, 0xB5F3, 0xB5F4, 0xB5F5, 0xB5F6, 0xB5F7, 0xB5F8, 0xB5F9, 0xB5FA, 0xB5FB, 0xB5FC, 0xB5FD, 0xB5FE, 0xB5FF, + 0xB600, 0xB601, 0xB602, 0xB603, 0xB604, 0xB605, 0xB606, 0xB607, 0xB608, 0xB609, 0xB60A, 0xB60B, 0xB60C, 0xB60D, 0xB60E, 0xB60F, 0xB612, 0xB613, 0xB615, 0xB616, 0xB617, 0xB619, 0xB61A, 0xB61B, 0xB61C, 0xB61D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB61E, 0xB61F, 0xB620, 0xB621, 0xB622, 0xB623, 0xB624, 0xB626, 0xB627, 0xB628, 0xB629, 0xB62A, 0xB62B, 0xB62D, 0xB62E, 0xB62F, 0xB630, 0xB631, 0xB632, 0xB633, 0xB635, 0xB636, 0xB637, 0xB638, 0xB639, 0xB63A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB63B, 0xB63C, 0xB63D, 0xB63E, 0xB63F, 0xB640, 0xB641, 0xB642, 0xB643, 0xB644, 0xB645, 0xB646, 0xB647, 0xB649, 0xB64A, 0xB64B, 0xB64C, 0xB64D, 0xB64E, 0xB64F, 0xB650, 0xB651, 0xB652, 0xB653, 0xB654, 0xB655, 0xB656, 0xB657, 0xB658, 0xB659, 0xB65A, 0xB65B, 0xB65C, 0xB65D, 0xB65E, 0xB65F, 0xB660, 0xB661, 0xB662, 0xB663, 0xB665, 0xB666, 0xB667, 0xB669, 0xB66A, 0xB66B, 0xB66C, 0xB66D, 0xB66E, 0xB66F, 0xB670, 0xB671, 0xB672, 0xB673, 0xB674, 0xB675, 0xB676, 0xB677, 0xB678, 0xB679, 0xB67A, 0xB67B, 0xB67C, 0xB67D, 0xB67E, 0xB67F, 0xB680, 0xB681, 0xB682, 0xB683, 0xB684, 0xB685, 0xB686, 0xB687, 0xB688, 0xB689, 0xB68A, 0xB68B, 0xB68C, 0xB68D, 0xB68E, 0xB68F, 0xB690, 0xB691, 0xB692, 0xB693, 0xB694, 0xB695, 0xB696, 0xB697, 0xB698, 0xB699, 0xB69A, 0xB69B, 0xB69E, 0xB69F, 0xB6A1, 0xB6A2, 0xB6A3, 0xB6A5, 0xB6A6, 0xB6A7, 0xB6A8, 0xB6A9, 0xB6AA, 0xB6AD, 0xB6AE, 0xB6AF, 0xB6B0, 0xB6B2, 0xB6B3, 0xB6B4, 0xB6B5, 0xB6B6, 0xB6B7, 0xB6B8, 0xB6B9, 0xB6BA, 0xB6BB, 0xB6BC, 0xB6BD, 0xB6BE, 0xB6BF, 0xB6C0, 0xB6C1, 0xB6C2, + 0xB6C3, 0xB6C4, 0xB6C5, 0xB6C6, 0xB6C7, 0xB6C8, 0xB6C9, 0xB6CA, 0xB6CB, 0xB6CC, 0xB6CD, 0xB6CE, 0xB6CF, 0xB6D0, 0xB6D1, 0xB6D2, 0xB6D3, 0xB6D5, 0xB6D6, 0xB6D7, 0xB6D8, 0xB6D9, 0xB6DA, 0xB6DB, 0xB6DC, 0xB6DD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB6DE, 0xB6DF, 0xB6E0, 0xB6E1, 0xB6E2, 0xB6E3, 0xB6E4, 0xB6E5, 0xB6E6, 0xB6E7, 0xB6E8, 0xB6E9, 0xB6EA, 0xB6EB, 0xB6EC, 0xB6ED, 0xB6EE, 0xB6EF, 0xB6F1, 0xB6F2, 0xB6F3, 0xB6F5, 0xB6F6, 0xB6F7, 0xB6F9, 0xB6FA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB6FB, 0xB6FC, 0xB6FD, 0xB6FE, 0xB6FF, 0xB702, 0xB703, 0xB704, 0xB706, 0xB707, 0xB708, 0xB709, 0xB70A, 0xB70B, 0xB70C, 0xB70D, 0xB70E, 0xB70F, 0xB710, 0xB711, 0xB712, 0xB713, 0xB714, 0xB715, 0xB716, 0xB717, 0xB718, 0xB719, 0xB71A, 0xB71B, 0xB71C, 0xB71D, 0xB71E, 0xB71F, 0xB720, 0xB721, 0xB722, 0xB723, 0xB724, 0xB725, 0xB726, 0xB727, 0xB72A, 0xB72B, 0xB72D, 0xB72E, 0xB731, 0xB732, 0xB733, 0xB734, 0xB735, 0xB736, 0xB737, 0xB73A, 0xB73C, 0xB73D, 0xB73E, 0xB73F, 0xB740, 0xB741, 0xB742, 0xB743, 0xB745, 0xB746, 0xB747, 0xB749, 0xB74A, 0xB74B, 0xB74D, 0xB74E, 0xB74F, 0xB750, 0xB751, 0xB752, 0xB753, 0xB756, 0xB757, 0xB758, 0xB759, 0xB75A, 0xB75B, 0xB75C, 0xB75D, 0xB75E, 0xB75F, 0xB761, 0xB762, 0xB763, 0xB765, 0xB766, 0xB767, 0xB769, 0xB76A, 0xB76B, 0xB76C, 0xB76D, 0xB76E, 0xB76F, 0xB772, 0xB774, 0xB776, 0xB777, 0xB778, 0xB779, 0xB77A, 0xB77B, 0xB77E, 0xB77F, 0xB781, 0xB782, 0xB783, 0xB785, 0xB786, 0xB787, 0xB788, 0xB789, 0xB78A, 0xB78B, 0xB78E, 0xB793, 0xB794, 0xB795, 0xB79A, 0xB79B, 0xB79D, 0xB79E, + 0xB79F, 0xB7A1, 0xB7A2, 0xB7A3, 0xB7A4, 0xB7A5, 0xB7A6, 0xB7A7, 0xB7AA, 0xB7AE, 0xB7AF, 0xB7B0, 0xB7B1, 0xB7B2, 0xB7B3, 0xB7B6, 0xB7B7, 0xB7B9, 0xB7BA, 0xB7BB, 0xB7BC, 0xB7BD, 0xB7BE, 0xB7BF, 0xB7C0, 0xB7C1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB7C2, 0xB7C3, 0xB7C4, 0xB7C5, 0xB7C6, 0xB7C8, 0xB7CA, 0xB7CB, 0xB7CC, 0xB7CD, 0xB7CE, 0xB7CF, 0xB7D0, 0xB7D1, 0xB7D2, 0xB7D3, 0xB7D4, 0xB7D5, 0xB7D6, 0xB7D7, 0xB7D8, 0xB7D9, 0xB7DA, 0xB7DB, 0xB7DC, 0xB7DD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB7DE, 0xB7DF, 0xB7E0, 0xB7E1, 0xB7E2, 0xB7E3, 0xB7E4, 0xB7E5, 0xB7E6, 0xB7E7, 0xB7E8, 0xB7E9, 0xB7EA, 0xB7EB, 0xB7EE, 0xB7EF, 0xB7F1, 0xB7F2, 0xB7F3, 0xB7F5, 0xB7F6, 0xB7F7, 0xB7F8, 0xB7F9, 0xB7FA, 0xB7FB, 0xB7FE, 0xB802, 0xB803, 0xB804, 0xB805, 0xB806, 0xB80A, 0xB80B, 0xB80D, 0xB80E, 0xB80F, 0xB811, 0xB812, 0xB813, 0xB814, 0xB815, 0xB816, 0xB817, 0xB81A, 0xB81C, 0xB81E, 0xB81F, 0xB820, 0xB821, 0xB822, 0xB823, 0xB826, 0xB827, 0xB829, 0xB82A, 0xB82B, 0xB82D, 0xB82E, 0xB82F, 0xB830, 0xB831, 0xB832, 0xB833, 0xB836, 0xB83A, 0xB83B, 0xB83C, 0xB83D, 0xB83E, 0xB83F, 0xB841, 0xB842, 0xB843, 0xB845, 0xB846, 0xB847, 0xB848, 0xB849, 0xB84A, 0xB84B, 0xB84C, 0xB84D, 0xB84E, 0xB84F, 0xB850, 0xB852, 0xB854, 0xB855, 0xB856, 0xB857, 0xB858, 0xB859, 0xB85A, 0xB85B, 0xB85E, 0xB85F, 0xB861, 0xB862, 0xB863, 0xB865, 0xB866, 0xB867, 0xB868, 0xB869, 0xB86A, 0xB86B, 0xB86E, 0xB870, 0xB872, 0xB873, 0xB874, 0xB875, 0xB876, 0xB877, 0xB879, 0xB87A, 0xB87B, 0xB87D, 0xB87E, 0xB87F, 0xB880, 0xB881, 0xB882, 0xB883, 0xB884, + 0xB885, 0xB886, 0xB887, 0xB888, 0xB889, 0xB88A, 0xB88B, 0xB88C, 0xB88E, 0xB88F, 0xB890, 0xB891, 0xB892, 0xB893, 0xB894, 0xB895, 0xB896, 0xB897, 0xB898, 0xB899, 0xB89A, 0xB89B, 0xB89C, 0xB89D, 0xB89E, 0xB89F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB8A0, 0xB8A1, 0xB8A2, 0xB8A3, 0xB8A4, 0xB8A5, 0xB8A6, 0xB8A7, 0xB8A9, 0xB8AA, 0xB8AB, 0xB8AC, 0xB8AD, 0xB8AE, 0xB8AF, 0xB8B1, 0xB8B2, 0xB8B3, 0xB8B5, 0xB8B6, 0xB8B7, 0xB8B9, 0xB8BA, 0xB8BB, 0xB8BC, 0xB8BD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB8BE, 0xB8BF, 0xB8C2, 0xB8C4, 0xB8C6, 0xB8C7, 0xB8C8, 0xB8C9, 0xB8CA, 0xB8CB, 0xB8CD, 0xB8CE, 0xB8CF, 0xB8D1, 0xB8D2, 0xB8D3, 0xB8D5, 0xB8D6, 0xB8D7, 0xB8D8, 0xB8D9, 0xB8DA, 0xB8DB, 0xB8DC, 0xB8DE, 0xB8E0, 0xB8E2, 0xB8E3, 0xB8E4, 0xB8E5, 0xB8E6, 0xB8E7, 0xB8EA, 0xB8EB, 0xB8ED, 0xB8EE, 0xB8EF, 0xB8F1, 0xB8F2, 0xB8F3, 0xB8F4, 0xB8F5, 0xB8F6, 0xB8F7, 0xB8FA, 0xB8FC, 0xB8FE, 0xB8FF, 0xB900, 0xB901, 0xB902, 0xB903, 0xB905, 0xB906, 0xB907, 0xB908, 0xB909, 0xB90A, 0xB90B, 0xB90C, 0xB90D, 0xB90E, 0xB90F, 0xB910, 0xB911, 0xB912, 0xB913, 0xB914, 0xB915, 0xB916, 0xB917, 0xB919, 0xB91A, 0xB91B, 0xB91C, 0xB91D, 0xB91E, 0xB91F, 0xB921, 0xB922, 0xB923, 0xB924, 0xB925, 0xB926, 0xB927, 0xB928, 0xB929, 0xB92A, 0xB92B, 0xB92C, 0xB92D, 0xB92E, 0xB92F, 0xB930, 0xB931, 0xB932, 0xB933, 0xB934, 0xB935, 0xB936, 0xB937, 0xB938, 0xB939, 0xB93A, 0xB93B, 0xB93E, 0xB93F, 0xB941, 0xB942, 0xB943, 0xB945, 0xB946, 0xB947, 0xB948, 0xB949, 0xB94A, 0xB94B, 0xB94D, 0xB94E, 0xB950, 0xB952, 0xB953, 0xB954, 0xB955, 0xB956, 0xB957, + 0xB95A, 0xB95B, 0xB95D, 0xB95E, 0xB95F, 0xB961, 0xB962, 0xB963, 0xB964, 0xB965, 0xB966, 0xB967, 0xB96A, 0xB96C, 0xB96E, 0xB96F, 0xB970, 0xB971, 0xB972, 0xB973, 0xB976, 0xB977, 0xB979, 0xB97A, 0xB97B, 0xB97D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB97E, 0xB97F, 0xB980, 0xB981, 0xB982, 0xB983, 0xB986, 0xB988, 0xB98B, 0xB98C, 0xB98F, 0xB990, 0xB991, 0xB992, 0xB993, 0xB994, 0xB995, 0xB996, 0xB997, 0xB998, 0xB999, 0xB99A, 0xB99B, 0xB99C, 0xB99D, 0xB99E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB99F, 0xB9A0, 0xB9A1, 0xB9A2, 0xB9A3, 0xB9A4, 0xB9A5, 0xB9A6, 0xB9A7, 0xB9A8, 0xB9A9, 0xB9AA, 0xB9AB, 0xB9AE, 0xB9AF, 0xB9B1, 0xB9B2, 0xB9B3, 0xB9B5, 0xB9B6, 0xB9B7, 0xB9B8, 0xB9B9, 0xB9BA, 0xB9BB, 0xB9BE, 0xB9C0, 0xB9C2, 0xB9C3, 0xB9C4, 0xB9C5, 0xB9C6, 0xB9C7, 0xB9CA, 0xB9CB, 0xB9CD, 0xB9D3, 0xB9D4, 0xB9D5, 0xB9D6, 0xB9D7, 0xB9DA, 0xB9DC, 0xB9DF, 0xB9E0, 0xB9E2, 0xB9E6, 0xB9E7, 0xB9E9, 0xB9EA, 0xB9EB, 0xB9ED, 0xB9EE, 0xB9EF, 0xB9F0, 0xB9F1, 0xB9F2, 0xB9F3, 0xB9F6, 0xB9FB, 0xB9FC, 0xB9FD, 0xB9FE, 0xB9FF, 0xBA02, 0xBA03, 0xBA04, 0xBA05, 0xBA06, 0xBA07, 0xBA09, 0xBA0A, 0xBA0B, 0xBA0C, 0xBA0D, 0xBA0E, 0xBA0F, 0xBA10, 0xBA11, 0xBA12, 0xBA13, 0xBA14, 0xBA16, 0xBA17, 0xBA18, 0xBA19, 0xBA1A, 0xBA1B, 0xBA1C, 0xBA1D, 0xBA1E, 0xBA1F, 0xBA20, 0xBA21, 0xBA22, 0xBA23, 0xBA24, 0xBA25, 0xBA26, 0xBA27, 0xBA28, 0xBA29, 0xBA2A, 0xBA2B, 0xBA2C, 0xBA2D, 0xBA2E, 0xBA2F, 0xBA30, 0xBA31, 0xBA32, 0xBA33, 0xBA34, 0xBA35, 0xBA36, 0xBA37, 0xBA3A, 0xBA3B, 0xBA3D, 0xBA3E, 0xBA3F, 0xBA41, 0xBA43, 0xBA44, 0xBA45, 0xBA46, + 0xBA47, 0xBA4A, 0xBA4C, 0xBA4F, 0xBA50, 0xBA51, 0xBA52, 0xBA56, 0xBA57, 0xBA59, 0xBA5A, 0xBA5B, 0xBA5D, 0xBA5E, 0xBA5F, 0xBA60, 0xBA61, 0xBA62, 0xBA63, 0xBA66, 0xBA6A, 0xBA6B, 0xBA6C, 0xBA6D, 0xBA6E, 0xBA6F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBA72, 0xBA73, 0xBA75, 0xBA76, 0xBA77, 0xBA79, 0xBA7A, 0xBA7B, 0xBA7C, 0xBA7D, 0xBA7E, 0xBA7F, 0xBA80, 0xBA81, 0xBA82, 0xBA86, 0xBA88, 0xBA89, 0xBA8A, 0xBA8B, 0xBA8D, 0xBA8E, 0xBA8F, 0xBA90, 0xBA91, 0xBA92, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBA93, 0xBA94, 0xBA95, 0xBA96, 0xBA97, 0xBA98, 0xBA99, 0xBA9A, 0xBA9B, 0xBA9C, 0xBA9D, 0xBA9E, 0xBA9F, 0xBAA0, 0xBAA1, 0xBAA2, 0xBAA3, 0xBAA4, 0xBAA5, 0xBAA6, 0xBAA7, 0xBAAA, 0xBAAD, 0xBAAE, 0xBAAF, 0xBAB1, 0xBAB3, 0xBAB4, 0xBAB5, 0xBAB6, 0xBAB7, 0xBABA, 0xBABC, 0xBABE, 0xBABF, 0xBAC0, 0xBAC1, 0xBAC2, 0xBAC3, 0xBAC5, 0xBAC6, 0xBAC7, 0xBAC9, 0xBACA, 0xBACB, 0xBACC, 0xBACD, 0xBACE, 0xBACF, 0xBAD0, 0xBAD1, 0xBAD2, 0xBAD3, 0xBAD4, 0xBAD5, 0xBAD6, 0xBAD7, 0xBADA, 0xBADB, 0xBADC, 0xBADD, 0xBADE, 0xBADF, 0xBAE0, 0xBAE1, 0xBAE2, 0xBAE3, 0xBAE4, 0xBAE5, 0xBAE6, 0xBAE7, 0xBAE8, 0xBAE9, 0xBAEA, 0xBAEB, 0xBAEC, 0xBAED, 0xBAEE, 0xBAEF, 0xBAF0, 0xBAF1, 0xBAF2, 0xBAF3, 0xBAF4, 0xBAF5, 0xBAF6, 0xBAF7, 0xBAF8, 0xBAF9, 0xBAFA, 0xBAFB, 0xBAFD, 0xBAFE, 0xBAFF, 0xBB01, 0xBB02, 0xBB03, 0xBB05, 0xBB06, 0xBB07, 0xBB08, 0xBB09, 0xBB0A, 0xBB0B, 0xBB0C, 0xBB0E, 0xBB10, 0xBB12, 0xBB13, 0xBB14, 0xBB15, 0xBB16, 0xBB17, 0xBB19, 0xBB1A, 0xBB1B, 0xBB1D, 0xBB1E, 0xBB1F, 0xBB21, 0xBB22, 0xBB23, 0xBB24, 0xBB25, 0xBB26, 0xBB27, + 0xBB28, 0xBB2A, 0xBB2C, 0xBB2D, 0xBB2E, 0xBB2F, 0xBB30, 0xBB31, 0xBB32, 0xBB33, 0xBB37, 0xBB39, 0xBB3A, 0xBB3F, 0xBB40, 0xBB41, 0xBB42, 0xBB43, 0xBB46, 0xBB48, 0xBB4A, 0xBB4B, 0xBB4C, 0xBB4E, 0xBB51, 0xBB52, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBB53, 0xBB55, 0xBB56, 0xBB57, 0xBB59, 0xBB5A, 0xBB5B, 0xBB5C, 0xBB5D, 0xBB5E, 0xBB5F, 0xBB60, 0xBB62, 0xBB64, 0xBB65, 0xBB66, 0xBB67, 0xBB68, 0xBB69, 0xBB6A, 0xBB6B, 0xBB6D, 0xBB6E, 0xBB6F, 0xBB70, 0xBB71, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBB72, 0xBB73, 0xBB74, 0xBB75, 0xBB76, 0xBB77, 0xBB78, 0xBB79, 0xBB7A, 0xBB7B, 0xBB7C, 0xBB7D, 0xBB7E, 0xBB7F, 0xBB80, 0xBB81, 0xBB82, 0xBB83, 0xBB84, 0xBB85, 0xBB86, 0xBB87, 0xBB89, 0xBB8A, 0xBB8B, 0xBB8D, 0xBB8E, 0xBB8F, 0xBB91, 0xBB92, 0xBB93, 0xBB94, 0xBB95, 0xBB96, 0xBB97, 0xBB98, 0xBB99, 0xBB9A, 0xBB9B, 0xBB9C, 0xBB9D, 0xBB9E, 0xBB9F, 0xBBA0, 0xBBA1, 0xBBA2, 0xBBA3, 0xBBA5, 0xBBA6, 0xBBA7, 0xBBA9, 0xBBAA, 0xBBAB, 0xBBAD, 0xBBAE, 0xBBAF, 0xBBB0, 0xBBB1, 0xBBB2, 0xBBB3, 0xBBB5, 0xBBB6, 0xBBB8, 0xBBB9, 0xBBBA, 0xBBBB, 0xBBBC, 0xBBBD, 0xBBBE, 0xBBBF, 0xBBC1, 0xBBC2, 0xBBC3, 0xBBC5, 0xBBC6, 0xBBC7, 0xBBC9, 0xBBCA, 0xBBCB, 0xBBCC, 0xBBCD, 0xBBCE, 0xBBCF, 0xBBD1, 0xBBD2, 0xBBD4, 0xBBD5, 0xBBD6, 0xBBD7, 0xBBD8, 0xBBD9, 0xBBDA, 0xBBDB, 0xBBDC, 0xBBDD, 0xBBDE, 0xBBDF, 0xBBE0, 0xBBE1, 0xBBE2, 0xBBE3, 0xBBE4, 0xBBE5, 0xBBE6, 0xBBE7, 0xBBE8, 0xBBE9, 0xBBEA, 0xBBEB, 0xBBEC, 0xBBED, 0xBBEE, 0xBBEF, 0xBBF0, 0xBBF1, 0xBBF2, 0xBBF3, 0xBBF4, 0xBBF5, 0xBBF6, 0xBBF7, 0xBBFA, 0xBBFB, 0xBBFD, 0xBBFE, 0xBC01, + 0xBC03, 0xBC04, 0xBC05, 0xBC06, 0xBC07, 0xBC0A, 0xBC0E, 0xBC10, 0xBC12, 0xBC13, 0xBC19, 0xBC1A, 0xBC20, 0xBC21, 0xBC22, 0xBC23, 0xBC26, 0xBC28, 0xBC2A, 0xBC2B, 0xBC2C, 0xBC2E, 0xBC2F, 0xBC32, 0xBC33, 0xBC35, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBC36, 0xBC37, 0xBC39, 0xBC3A, 0xBC3B, 0xBC3C, 0xBC3D, 0xBC3E, 0xBC3F, 0xBC42, 0xBC46, 0xBC47, 0xBC48, 0xBC4A, 0xBC4B, 0xBC4E, 0xBC4F, 0xBC51, 0xBC52, 0xBC53, 0xBC54, 0xBC55, 0xBC56, 0xBC57, 0xBC58, 0xBC59, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBC5A, 0xBC5B, 0xBC5C, 0xBC5E, 0xBC5F, 0xBC60, 0xBC61, 0xBC62, 0xBC63, 0xBC64, 0xBC65, 0xBC66, 0xBC67, 0xBC68, 0xBC69, 0xBC6A, 0xBC6B, 0xBC6C, 0xBC6D, 0xBC6E, 0xBC6F, 0xBC70, 0xBC71, 0xBC72, 0xBC73, 0xBC74, 0xBC75, 0xBC76, 0xBC77, 0xBC78, 0xBC79, 0xBC7A, 0xBC7B, 0xBC7C, 0xBC7D, 0xBC7E, 0xBC7F, 0xBC80, 0xBC81, 0xBC82, 0xBC83, 0xBC86, 0xBC87, 0xBC89, 0xBC8A, 0xBC8D, 0xBC8F, 0xBC90, 0xBC91, 0xBC92, 0xBC93, 0xBC96, 0xBC98, 0xBC9B, 0xBC9C, 0xBC9D, 0xBC9E, 0xBC9F, 0xBCA2, 0xBCA3, 0xBCA5, 0xBCA6, 0xBCA9, 0xBCAA, 0xBCAB, 0xBCAC, 0xBCAD, 0xBCAE, 0xBCAF, 0xBCB2, 0xBCB6, 0xBCB7, 0xBCB8, 0xBCB9, 0xBCBA, 0xBCBB, 0xBCBE, 0xBCBF, 0xBCC1, 0xBCC2, 0xBCC3, 0xBCC5, 0xBCC6, 0xBCC7, 0xBCC8, 0xBCC9, 0xBCCA, 0xBCCB, 0xBCCC, 0xBCCE, 0xBCD2, 0xBCD3, 0xBCD4, 0xBCD6, 0xBCD7, 0xBCD9, 0xBCDA, 0xBCDB, 0xBCDD, 0xBCDE, 0xBCDF, 0xBCE0, 0xBCE1, 0xBCE2, 0xBCE3, 0xBCE4, 0xBCE5, 0xBCE6, 0xBCE7, 0xBCE8, 0xBCE9, 0xBCEA, 0xBCEB, 0xBCEC, 0xBCED, 0xBCEE, 0xBCEF, 0xBCF0, 0xBCF1, 0xBCF2, 0xBCF3, 0xBCF7, 0xBCF9, 0xBCFA, 0xBCFB, 0xBCFD, + 0xBCFE, 0xBCFF, 0xBD00, 0xBD01, 0xBD02, 0xBD03, 0xBD06, 0xBD08, 0xBD0A, 0xBD0B, 0xBD0C, 0xBD0D, 0xBD0E, 0xBD0F, 0xBD11, 0xBD12, 0xBD13, 0xBD15, 0xBD16, 0xBD17, 0xBD18, 0xBD19, 0xBD1A, 0xBD1B, 0xBD1C, 0xBD1D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBD1E, 0xBD1F, 0xBD20, 0xBD21, 0xBD22, 0xBD23, 0xBD25, 0xBD26, 0xBD27, 0xBD28, 0xBD29, 0xBD2A, 0xBD2B, 0xBD2D, 0xBD2E, 0xBD2F, 0xBD30, 0xBD31, 0xBD32, 0xBD33, 0xBD34, 0xBD35, 0xBD36, 0xBD37, 0xBD38, 0xBD39, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBD3A, 0xBD3B, 0xBD3C, 0xBD3D, 0xBD3E, 0xBD3F, 0xBD41, 0xBD42, 0xBD43, 0xBD44, 0xBD45, 0xBD46, 0xBD47, 0xBD4A, 0xBD4B, 0xBD4D, 0xBD4E, 0xBD4F, 0xBD51, 0xBD52, 0xBD53, 0xBD54, 0xBD55, 0xBD56, 0xBD57, 0xBD5A, 0xBD5B, 0xBD5C, 0xBD5D, 0xBD5E, 0xBD5F, 0xBD60, 0xBD61, 0xBD62, 0xBD63, 0xBD65, 0xBD66, 0xBD67, 0xBD69, 0xBD6A, 0xBD6B, 0xBD6C, 0xBD6D, 0xBD6E, 0xBD6F, 0xBD70, 0xBD71, 0xBD72, 0xBD73, 0xBD74, 0xBD75, 0xBD76, 0xBD77, 0xBD78, 0xBD79, 0xBD7A, 0xBD7B, 0xBD7C, 0xBD7D, 0xBD7E, 0xBD7F, 0xBD82, 0xBD83, 0xBD85, 0xBD86, 0xBD8B, 0xBD8C, 0xBD8D, 0xBD8E, 0xBD8F, 0xBD92, 0xBD94, 0xBD96, 0xBD97, 0xBD98, 0xBD9B, 0xBD9D, 0xBD9E, 0xBD9F, 0xBDA0, 0xBDA1, 0xBDA2, 0xBDA3, 0xBDA5, 0xBDA6, 0xBDA7, 0xBDA8, 0xBDA9, 0xBDAA, 0xBDAB, 0xBDAC, 0xBDAD, 0xBDAE, 0xBDAF, 0xBDB1, 0xBDB2, 0xBDB3, 0xBDB4, 0xBDB5, 0xBDB6, 0xBDB7, 0xBDB9, 0xBDBA, 0xBDBB, 0xBDBC, 0xBDBD, 0xBDBE, 0xBDBF, 0xBDC0, 0xBDC1, 0xBDC2, 0xBDC3, 0xBDC4, 0xBDC5, 0xBDC6, 0xBDC7, 0xBDC8, 0xBDC9, 0xBDCA, 0xBDCB, 0xBDCC, 0xBDCD, 0xBDCE, 0xBDCF, 0xBDD0, 0xBDD1, + 0xBDD2, 0xBDD3, 0xBDD6, 0xBDD7, 0xBDD9, 0xBDDA, 0xBDDB, 0xBDDD, 0xBDDE, 0xBDDF, 0xBDE0, 0xBDE1, 0xBDE2, 0xBDE3, 0xBDE4, 0xBDE5, 0xBDE6, 0xBDE7, 0xBDE8, 0xBDEA, 0xBDEB, 0xBDEC, 0xBDED, 0xBDEE, 0xBDEF, 0xBDF1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBDF2, 0xBDF3, 0xBDF5, 0xBDF6, 0xBDF7, 0xBDF9, 0xBDFA, 0xBDFB, 0xBDFC, 0xBDFD, 0xBDFE, 0xBDFF, 0xBE01, 0xBE02, 0xBE04, 0xBE06, 0xBE07, 0xBE08, 0xBE09, 0xBE0A, 0xBE0B, 0xBE0E, 0xBE0F, 0xBE11, 0xBE12, 0xBE13, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBE15, 0xBE16, 0xBE17, 0xBE18, 0xBE19, 0xBE1A, 0xBE1B, 0xBE1E, 0xBE20, 0xBE21, 0xBE22, 0xBE23, 0xBE24, 0xBE25, 0xBE26, 0xBE27, 0xBE28, 0xBE29, 0xBE2A, 0xBE2B, 0xBE2C, 0xBE2D, 0xBE2E, 0xBE2F, 0xBE30, 0xBE31, 0xBE32, 0xBE33, 0xBE34, 0xBE35, 0xBE36, 0xBE37, 0xBE38, 0xBE39, 0xBE3A, 0xBE3B, 0xBE3C, 0xBE3D, 0xBE3E, 0xBE3F, 0xBE40, 0xBE41, 0xBE42, 0xBE43, 0xBE46, 0xBE47, 0xBE49, 0xBE4A, 0xBE4B, 0xBE4D, 0xBE4F, 0xBE50, 0xBE51, 0xBE52, 0xBE53, 0xBE56, 0xBE58, 0xBE5C, 0xBE5D, 0xBE5E, 0xBE5F, 0xBE62, 0xBE63, 0xBE65, 0xBE66, 0xBE67, 0xBE69, 0xBE6B, 0xBE6C, 0xBE6D, 0xBE6E, 0xBE6F, 0xBE72, 0xBE76, 0xBE77, 0xBE78, 0xBE79, 0xBE7A, 0xBE7E, 0xBE7F, 0xBE81, 0xBE82, 0xBE83, 0xBE85, 0xBE86, 0xBE87, 0xBE88, 0xBE89, 0xBE8A, 0xBE8B, 0xBE8E, 0xBE92, 0xBE93, 0xBE94, 0xBE95, 0xBE96, 0xBE97, 0xBE9A, 0xBE9B, 0xBE9C, 0xBE9D, 0xBE9E, 0xBE9F, 0xBEA0, 0xBEA1, 0xBEA2, 0xBEA3, 0xBEA4, 0xBEA5, 0xBEA6, 0xBEA7, 0xBEA9, 0xBEAA, 0xBEAB, 0xBEAC, 0xBEAD, 0xBEAE, 0xBEAF, 0xBEB0, 0xBEB1, 0xBEB2, 0xBEB3, 0xBEB4, 0xBEB5, 0xBEB6, 0xBEB7, + 0xBEB8, 0xBEB9, 0xBEBA, 0xBEBB, 0xBEBC, 0xBEBD, 0xBEBE, 0xBEBF, 0xBEC0, 0xBEC1, 0xBEC2, 0xBEC3, 0xBEC4, 0xBEC5, 0xBEC6, 0xBEC7, 0xBEC8, 0xBEC9, 0xBECA, 0xBECB, 0xBECC, 0xBECD, 0xBECE, 0xBECF, 0xBED2, 0xBED3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBED5, 0xBED6, 0xBED9, 0xBEDA, 0xBEDB, 0xBEDC, 0xBEDD, 0xBEDE, 0xBEDF, 0xBEE1, 0xBEE2, 0xBEE6, 0xBEE7, 0xBEE8, 0xBEE9, 0xBEEA, 0xBEEB, 0xBEED, 0xBEEE, 0xBEEF, 0xBEF0, 0xBEF1, 0xBEF2, 0xBEF3, 0xBEF4, 0xBEF5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBEF6, 0xBEF7, 0xBEF8, 0xBEF9, 0xBEFA, 0xBEFB, 0xBEFC, 0xBEFD, 0xBEFE, 0xBEFF, 0xBF00, 0xBF02, 0xBF03, 0xBF04, 0xBF05, 0xBF06, 0xBF07, 0xBF0A, 0xBF0B, 0xBF0C, 0xBF0D, 0xBF0E, 0xBF0F, 0xBF10, 0xBF11, 0xBF12, 0xBF13, 0xBF14, 0xBF15, 0xBF16, 0xBF17, 0xBF1A, 0xBF1E, 0xBF1F, 0xBF20, 0xBF21, 0xBF22, 0xBF23, 0xBF24, 0xBF25, 0xBF26, 0xBF27, 0xBF28, 0xBF29, 0xBF2A, 0xBF2B, 0xBF2C, 0xBF2D, 0xBF2E, 0xBF2F, 0xBF30, 0xBF31, 0xBF32, 0xBF33, 0xBF34, 0xBF35, 0xBF36, 0xBF37, 0xBF38, 0xBF39, 0xBF3A, 0xBF3B, 0xBF3C, 0xBF3D, 0xBF3E, 0xBF3F, 0xBF42, 0xBF43, 0xBF45, 0xBF46, 0xBF47, 0xBF49, 0xBF4A, 0xBF4B, 0xBF4C, 0xBF4D, 0xBF4E, 0xBF4F, 0xBF52, 0xBF53, 0xBF54, 0xBF56, 0xBF57, 0xBF58, 0xBF59, 0xBF5A, 0xBF5B, 0xBF5C, 0xBF5D, 0xBF5E, 0xBF5F, 0xBF60, 0xBF61, 0xBF62, 0xBF63, 0xBF64, 0xBF65, 0xBF66, 0xBF67, 0xBF68, 0xBF69, 0xBF6A, 0xBF6B, 0xBF6C, 0xBF6D, 0xBF6E, 0xBF6F, 0xBF70, 0xBF71, 0xBF72, 0xBF73, 0xBF74, 0xBF75, 0xBF76, 0xBF77, 0xBF78, 0xBF79, 0xBF7A, 0xBF7B, 0xBF7C, 0xBF7D, 0xBF7E, 0xBF7F, 0xBF80, 0xBF81, 0xBF82, + 0xBF83, 0xBF84, 0xBF85, 0xBF86, 0xBF87, 0xBF88, 0xBF89, 0xBF8A, 0xBF8B, 0xBF8C, 0xBF8D, 0xBF8E, 0xBF8F, 0xBF90, 0xBF91, 0xBF92, 0xBF93, 0xBF95, 0xBF96, 0xBF97, 0xBF98, 0xBF99, 0xBF9A, 0xBF9B, 0xBF9C, 0xBF9D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBF9E, 0xBF9F, 0xBFA0, 0xBFA1, 0xBFA2, 0xBFA3, 0xBFA4, 0xBFA5, 0xBFA6, 0xBFA7, 0xBFA8, 0xBFA9, 0xBFAA, 0xBFAB, 0xBFAC, 0xBFAD, 0xBFAE, 0xBFAF, 0xBFB1, 0xBFB2, 0xBFB3, 0xBFB4, 0xBFB5, 0xBFB6, 0xBFB7, 0xBFB8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBFB9, 0xBFBA, 0xBFBB, 0xBFBC, 0xBFBD, 0xBFBE, 0xBFBF, 0xBFC0, 0xBFC1, 0xBFC2, 0xBFC3, 0xBFC4, 0xBFC6, 0xBFC7, 0xBFC8, 0xBFC9, 0xBFCA, 0xBFCB, 0xBFCE, 0xBFCF, 0xBFD1, 0xBFD2, 0xBFD3, 0xBFD5, 0xBFD6, 0xBFD7, 0xBFD8, 0xBFD9, 0xBFDA, 0xBFDB, 0xBFDD, 0xBFDE, 0xBFE0, 0xBFE2, 0xBFE3, 0xBFE4, 0xBFE5, 0xBFE6, 0xBFE7, 0xBFE8, 0xBFE9, 0xBFEA, 0xBFEB, 0xBFEC, 0xBFED, 0xBFEE, 0xBFEF, 0xBFF0, 0xBFF1, 0xBFF2, 0xBFF3, 0xBFF4, 0xBFF5, 0xBFF6, 0xBFF7, 0xBFF8, 0xBFF9, 0xBFFA, 0xBFFB, 0xBFFC, 0xBFFD, 0xBFFE, 0xBFFF, 0xC000, 0xC001, 0xC002, 0xC003, 0xC004, 0xC005, 0xC006, 0xC007, 0xC008, 0xC009, 0xC00A, 0xC00B, 0xC00C, 0xC00D, 0xC00E, 0xC00F, 0xC010, 0xC011, 0xC012, 0xC013, 0xC014, 0xC015, 0xC016, 0xC017, 0xC018, 0xC019, 0xC01A, 0xC01B, 0xC01C, 0xC01D, 0xC01E, 0xC01F, 0xC020, 0xC021, 0xC022, 0xC023, 0xC024, 0xC025, 0xC026, 0xC027, 0xC028, 0xC029, 0xC02A, 0xC02B, 0xC02C, 0xC02D, 0xC02E, 0xC02F, 0xC030, 0xC031, 0xC032, 0xC033, 0xC034, 0xC035, 0xC036, 0xC037, 0xC038, 0xC039, 0xC03A, 0xC03B, 0xC03D, 0xC03E, 0xC03F, + 0xC040, 0xC041, 0xC042, 0xC043, 0xC044, 0xC045, 0xC046, 0xC047, 0xC048, 0xC049, 0xC04A, 0xC04B, 0xC04C, 0xC04D, 0xC04E, 0xC04F, 0xC050, 0xC052, 0xC053, 0xC054, 0xC055, 0xC056, 0xC057, 0xC059, 0xC05A, 0xC05B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC05D, 0xC05E, 0xC05F, 0xC061, 0xC062, 0xC063, 0xC064, 0xC065, 0xC066, 0xC067, 0xC06A, 0xC06B, 0xC06C, 0xC06D, 0xC06E, 0xC06F, 0xC070, 0xC071, 0xC072, 0xC073, 0xC074, 0xC075, 0xC076, 0xC077, 0xC078, 0xC079, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC07A, 0xC07B, 0xC07C, 0xC07D, 0xC07E, 0xC07F, 0xC080, 0xC081, 0xC082, 0xC083, 0xC084, 0xC085, 0xC086, 0xC087, 0xC088, 0xC089, 0xC08A, 0xC08B, 0xC08C, 0xC08D, 0xC08E, 0xC08F, 0xC092, 0xC093, 0xC095, 0xC096, 0xC097, 0xC099, 0xC09A, 0xC09B, 0xC09C, 0xC09D, 0xC09E, 0xC09F, 0xC0A2, 0xC0A4, 0xC0A6, 0xC0A7, 0xC0A8, 0xC0A9, 0xC0AA, 0xC0AB, 0xC0AE, 0xC0B1, 0xC0B2, 0xC0B7, 0xC0B8, 0xC0B9, 0xC0BA, 0xC0BB, 0xC0BE, 0xC0C2, 0xC0C3, 0xC0C4, 0xC0C6, 0xC0C7, 0xC0CA, 0xC0CB, 0xC0CD, 0xC0CE, 0xC0CF, 0xC0D1, 0xC0D2, 0xC0D3, 0xC0D4, 0xC0D5, 0xC0D6, 0xC0D7, 0xC0DA, 0xC0DE, 0xC0DF, 0xC0E0, 0xC0E1, 0xC0E2, 0xC0E3, 0xC0E6, 0xC0E7, 0xC0E9, 0xC0EA, 0xC0EB, 0xC0ED, 0xC0EE, 0xC0EF, 0xC0F0, 0xC0F1, 0xC0F2, 0xC0F3, 0xC0F6, 0xC0F8, 0xC0FA, 0xC0FB, 0xC0FC, 0xC0FD, 0xC0FE, 0xC0FF, 0xC101, 0xC102, 0xC103, 0xC105, 0xC106, 0xC107, 0xC109, 0xC10A, 0xC10B, 0xC10C, 0xC10D, 0xC10E, 0xC10F, 0xC111, 0xC112, 0xC113, 0xC114, 0xC116, 0xC117, 0xC118, 0xC119, 0xC11A, 0xC11B, 0xC121, 0xC122, 0xC125, 0xC128, 0xC129, 0xC12A, 0xC12B, 0xC12E, + 0xC132, 0xC133, 0xC134, 0xC135, 0xC137, 0xC13A, 0xC13B, 0xC13D, 0xC13E, 0xC13F, 0xC141, 0xC142, 0xC143, 0xC144, 0xC145, 0xC146, 0xC147, 0xC14A, 0xC14E, 0xC14F, 0xC150, 0xC151, 0xC152, 0xC153, 0xC156, 0xC157, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC159, 0xC15A, 0xC15B, 0xC15D, 0xC15E, 0xC15F, 0xC160, 0xC161, 0xC162, 0xC163, 0xC166, 0xC16A, 0xC16B, 0xC16C, 0xC16D, 0xC16E, 0xC16F, 0xC171, 0xC172, 0xC173, 0xC175, 0xC176, 0xC177, 0xC179, 0xC17A, 0xC17B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC17C, 0xC17D, 0xC17E, 0xC17F, 0xC180, 0xC181, 0xC182, 0xC183, 0xC184, 0xC186, 0xC187, 0xC188, 0xC189, 0xC18A, 0xC18B, 0xC18F, 0xC191, 0xC192, 0xC193, 0xC195, 0xC197, 0xC198, 0xC199, 0xC19A, 0xC19B, 0xC19E, 0xC1A0, 0xC1A2, 0xC1A3, 0xC1A4, 0xC1A6, 0xC1A7, 0xC1AA, 0xC1AB, 0xC1AD, 0xC1AE, 0xC1AF, 0xC1B1, 0xC1B2, 0xC1B3, 0xC1B4, 0xC1B5, 0xC1B6, 0xC1B7, 0xC1B8, 0xC1B9, 0xC1BA, 0xC1BB, 0xC1BC, 0xC1BE, 0xC1BF, 0xC1C0, 0xC1C1, 0xC1C2, 0xC1C3, 0xC1C5, 0xC1C6, 0xC1C7, 0xC1C9, 0xC1CA, 0xC1CB, 0xC1CD, 0xC1CE, 0xC1CF, 0xC1D0, 0xC1D1, 0xC1D2, 0xC1D3, 0xC1D5, 0xC1D6, 0xC1D9, 0xC1DA, 0xC1DB, 0xC1DC, 0xC1DD, 0xC1DE, 0xC1DF, 0xC1E1, 0xC1E2, 0xC1E3, 0xC1E5, 0xC1E6, 0xC1E7, 0xC1E9, 0xC1EA, 0xC1EB, 0xC1EC, 0xC1ED, 0xC1EE, 0xC1EF, 0xC1F2, 0xC1F4, 0xC1F5, 0xC1F6, 0xC1F7, 0xC1F8, 0xC1F9, 0xC1FA, 0xC1FB, 0xC1FE, 0xC1FF, 0xC201, 0xC202, 0xC203, 0xC205, 0xC206, 0xC207, 0xC208, 0xC209, 0xC20A, 0xC20B, 0xC20E, 0xC210, 0xC212, 0xC213, 0xC214, 0xC215, 0xC216, 0xC217, 0xC21A, 0xC21B, 0xC21D, 0xC21E, 0xC221, 0xC222, 0xC223, + 0xC224, 0xC225, 0xC226, 0xC227, 0xC22A, 0xC22C, 0xC22E, 0xC230, 0xC233, 0xC235, 0xC236, 0xC237, 0xC238, 0xC239, 0xC23A, 0xC23B, 0xC23C, 0xC23D, 0xC23E, 0xC23F, 0xC240, 0xC241, 0xC242, 0xC243, 0xC244, 0xC245, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC246, 0xC247, 0xC249, 0xC24A, 0xC24B, 0xC24C, 0xC24D, 0xC24E, 0xC24F, 0xC252, 0xC253, 0xC255, 0xC256, 0xC257, 0xC259, 0xC25A, 0xC25B, 0xC25C, 0xC25D, 0xC25E, 0xC25F, 0xC261, 0xC262, 0xC263, 0xC264, 0xC266, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC267, 0xC268, 0xC269, 0xC26A, 0xC26B, 0xC26E, 0xC26F, 0xC271, 0xC272, 0xC273, 0xC275, 0xC276, 0xC277, 0xC278, 0xC279, 0xC27A, 0xC27B, 0xC27E, 0xC280, 0xC282, 0xC283, 0xC284, 0xC285, 0xC286, 0xC287, 0xC28A, 0xC28B, 0xC28C, 0xC28D, 0xC28E, 0xC28F, 0xC291, 0xC292, 0xC293, 0xC294, 0xC295, 0xC296, 0xC297, 0xC299, 0xC29A, 0xC29C, 0xC29E, 0xC29F, 0xC2A0, 0xC2A1, 0xC2A2, 0xC2A3, 0xC2A6, 0xC2A7, 0xC2A9, 0xC2AA, 0xC2AB, 0xC2AE, 0xC2AF, 0xC2B0, 0xC2B1, 0xC2B2, 0xC2B3, 0xC2B6, 0xC2B8, 0xC2BA, 0xC2BB, 0xC2BC, 0xC2BD, 0xC2BE, 0xC2BF, 0xC2C0, 0xC2C1, 0xC2C2, 0xC2C3, 0xC2C4, 0xC2C5, 0xC2C6, 0xC2C7, 0xC2C8, 0xC2C9, 0xC2CA, 0xC2CB, 0xC2CC, 0xC2CD, 0xC2CE, 0xC2CF, 0xC2D0, 0xC2D1, 0xC2D2, 0xC2D3, 0xC2D4, 0xC2D5, 0xC2D6, 0xC2D7, 0xC2D8, 0xC2D9, 0xC2DA, 0xC2DB, 0xC2DE, 0xC2DF, 0xC2E1, 0xC2E2, 0xC2E5, 0xC2E6, 0xC2E7, 0xC2E8, 0xC2E9, 0xC2EA, 0xC2EE, 0xC2F0, 0xC2F2, 0xC2F3, 0xC2F4, 0xC2F5, 0xC2F7, 0xC2FA, 0xC2FD, 0xC2FE, 0xC2FF, 0xC301, 0xC302, 0xC303, 0xC304, 0xC305, 0xC306, 0xC307, 0xC30A, 0xC30B, 0xC30E, 0xC30F, + 0xC310, 0xC311, 0xC312, 0xC316, 0xC317, 0xC319, 0xC31A, 0xC31B, 0xC31D, 0xC31E, 0xC31F, 0xC320, 0xC321, 0xC322, 0xC323, 0xC326, 0xC327, 0xC32A, 0xC32B, 0xC32C, 0xC32D, 0xC32E, 0xC32F, 0xC330, 0xC331, 0xC332, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC333, 0xC334, 0xC335, 0xC336, 0xC337, 0xC338, 0xC339, 0xC33A, 0xC33B, 0xC33C, 0xC33D, 0xC33E, 0xC33F, 0xC340, 0xC341, 0xC342, 0xC343, 0xC344, 0xC346, 0xC347, 0xC348, 0xC349, 0xC34A, 0xC34B, 0xC34C, 0xC34D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC34E, 0xC34F, 0xC350, 0xC351, 0xC352, 0xC353, 0xC354, 0xC355, 0xC356, 0xC357, 0xC358, 0xC359, 0xC35A, 0xC35B, 0xC35C, 0xC35D, 0xC35E, 0xC35F, 0xC360, 0xC361, 0xC362, 0xC363, 0xC364, 0xC365, 0xC366, 0xC367, 0xC36A, 0xC36B, 0xC36D, 0xC36E, 0xC36F, 0xC371, 0xC373, 0xC374, 0xC375, 0xC376, 0xC377, 0xC37A, 0xC37B, 0xC37E, 0xC37F, 0xC380, 0xC381, 0xC382, 0xC383, 0xC385, 0xC386, 0xC387, 0xC389, 0xC38A, 0xC38B, 0xC38D, 0xC38E, 0xC38F, 0xC390, 0xC391, 0xC392, 0xC393, 0xC394, 0xC395, 0xC396, 0xC397, 0xC398, 0xC399, 0xC39A, 0xC39B, 0xC39C, 0xC39D, 0xC39E, 0xC39F, 0xC3A0, 0xC3A1, 0xC3A2, 0xC3A3, 0xC3A4, 0xC3A5, 0xC3A6, 0xC3A7, 0xC3A8, 0xC3A9, 0xC3AA, 0xC3AB, 0xC3AC, 0xC3AD, 0xC3AE, 0xC3AF, 0xC3B0, 0xC3B1, 0xC3B2, 0xC3B3, 0xC3B4, 0xC3B5, 0xC3B6, 0xC3B7, 0xC3B8, 0xC3B9, 0xC3BA, 0xC3BB, 0xC3BC, 0xC3BD, 0xC3BE, 0xC3BF, 0xC3C1, 0xC3C2, 0xC3C3, 0xC3C4, 0xC3C5, 0xC3C6, 0xC3C7, 0xC3C8, 0xC3C9, 0xC3CA, 0xC3CB, 0xC3CC, 0xC3CD, 0xC3CE, 0xC3CF, 0xC3D0, 0xC3D1, 0xC3D2, 0xC3D3, 0xC3D4, 0xC3D5, 0xC3D6, 0xC3D7, 0xC3DA, + 0xC3DB, 0xC3DD, 0xC3DE, 0xC3E1, 0xC3E3, 0xC3E4, 0xC3E5, 0xC3E6, 0xC3E7, 0xC3EA, 0xC3EB, 0xC3EC, 0xC3EE, 0xC3EF, 0xC3F0, 0xC3F1, 0xC3F2, 0xC3F3, 0xC3F6, 0xC3F7, 0xC3F9, 0xC3FA, 0xC3FB, 0xC3FC, 0xC3FD, 0xC3FE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC3FF, 0xC400, 0xC401, 0xC402, 0xC403, 0xC404, 0xC405, 0xC406, 0xC407, 0xC409, 0xC40A, 0xC40B, 0xC40C, 0xC40D, 0xC40E, 0xC40F, 0xC411, 0xC412, 0xC413, 0xC414, 0xC415, 0xC416, 0xC417, 0xC418, 0xC419, 0xC41A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC41B, 0xC41C, 0xC41D, 0xC41E, 0xC41F, 0xC420, 0xC421, 0xC422, 0xC423, 0xC425, 0xC426, 0xC427, 0xC428, 0xC429, 0xC42A, 0xC42B, 0xC42D, 0xC42E, 0xC42F, 0xC431, 0xC432, 0xC433, 0xC435, 0xC436, 0xC437, 0xC438, 0xC439, 0xC43A, 0xC43B, 0xC43E, 0xC43F, 0xC440, 0xC441, 0xC442, 0xC443, 0xC444, 0xC445, 0xC446, 0xC447, 0xC449, 0xC44A, 0xC44B, 0xC44C, 0xC44D, 0xC44E, 0xC44F, 0xC450, 0xC451, 0xC452, 0xC453, 0xC454, 0xC455, 0xC456, 0xC457, 0xC458, 0xC459, 0xC45A, 0xC45B, 0xC45C, 0xC45D, 0xC45E, 0xC45F, 0xC460, 0xC461, 0xC462, 0xC463, 0xC466, 0xC467, 0xC469, 0xC46A, 0xC46B, 0xC46D, 0xC46E, 0xC46F, 0xC470, 0xC471, 0xC472, 0xC473, 0xC476, 0xC477, 0xC478, 0xC47A, 0xC47B, 0xC47C, 0xC47D, 0xC47E, 0xC47F, 0xC481, 0xC482, 0xC483, 0xC484, 0xC485, 0xC486, 0xC487, 0xC488, 0xC489, 0xC48A, 0xC48B, 0xC48C, 0xC48D, 0xC48E, 0xC48F, 0xC490, 0xC491, 0xC492, 0xC493, 0xC495, 0xC496, 0xC497, 0xC498, 0xC499, 0xC49A, 0xC49B, 0xC49D, 0xC49E, 0xC49F, 0xC4A0, 0xC4A1, 0xC4A2, 0xC4A3, 0xC4A4, 0xC4A5, 0xC4A6, 0xC4A7, 0xC4A8, 0xC4A9, + 0xC4AA, 0xC4AB, 0xC4AC, 0xC4AD, 0xC4AE, 0xC4AF, 0xC4B0, 0xC4B1, 0xC4B2, 0xC4B3, 0xC4B4, 0xC4B5, 0xC4B6, 0xC4B7, 0xC4B9, 0xC4BA, 0xC4BB, 0xC4BD, 0xC4BE, 0xC4BF, 0xC4C0, 0xC4C1, 0xC4C2, 0xC4C3, 0xC4C4, 0xC4C5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC4C6, 0xC4C7, 0xC4C8, 0xC4C9, 0xC4CA, 0xC4CB, 0xC4CC, 0xC4CD, 0xC4CE, 0xC4CF, 0xC4D0, 0xC4D1, 0xC4D2, 0xC4D3, 0xC4D4, 0xC4D5, 0xC4D6, 0xC4D7, 0xC4D8, 0xC4D9, 0xC4DA, 0xC4DB, 0xC4DC, 0xC4DD, 0xC4DE, 0xC4DF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC4E0, 0xC4E1, 0xC4E2, 0xC4E3, 0xC4E4, 0xC4E5, 0xC4E6, 0xC4E7, 0xC4E8, 0xC4EA, 0xC4EB, 0xC4EC, 0xC4ED, 0xC4EE, 0xC4EF, 0xC4F2, 0xC4F3, 0xC4F5, 0xC4F6, 0xC4F7, 0xC4F9, 0xC4FB, 0xC4FC, 0xC4FD, 0xC4FE, 0xC502, 0xC503, 0xC504, 0xC505, 0xC506, 0xC507, 0xC508, 0xC509, 0xC50A, 0xC50B, 0xC50D, 0xC50E, 0xC50F, 0xC511, 0xC512, 0xC513, 0xC515, 0xC516, 0xC517, 0xC518, 0xC519, 0xC51A, 0xC51B, 0xC51D, 0xC51E, 0xC51F, 0xC520, 0xC521, 0xC522, 0xC523, 0xC524, 0xC525, 0xC526, 0xC527, 0xC52A, 0xC52B, 0xC52D, 0xC52E, 0xC52F, 0xC531, 0xC532, 0xC533, 0xC534, 0xC535, 0xC536, 0xC537, 0xC53A, 0xC53C, 0xC53E, 0xC53F, 0xC540, 0xC541, 0xC542, 0xC543, 0xC546, 0xC547, 0xC54B, 0xC54F, 0xC550, 0xC551, 0xC552, 0xC556, 0xC55A, 0xC55B, 0xC55C, 0xC55F, 0xC562, 0xC563, 0xC565, 0xC566, 0xC567, 0xC569, 0xC56A, 0xC56B, 0xC56C, 0xC56D, 0xC56E, 0xC56F, 0xC572, 0xC576, 0xC577, 0xC578, 0xC579, 0xC57A, 0xC57B, 0xC57E, 0xC57F, 0xC581, 0xC582, 0xC583, 0xC585, 0xC586, 0xC588, 0xC589, 0xC58A, 0xC58B, 0xC58E, 0xC590, 0xC592, 0xC593, 0xC594, + 0xC596, 0xC599, 0xC59A, 0xC59B, 0xC59D, 0xC59E, 0xC59F, 0xC5A1, 0xC5A2, 0xC5A3, 0xC5A4, 0xC5A5, 0xC5A6, 0xC5A7, 0xC5A8, 0xC5AA, 0xC5AB, 0xC5AC, 0xC5AD, 0xC5AE, 0xC5AF, 0xC5B0, 0xC5B1, 0xC5B2, 0xC5B3, 0xC5B6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC5B7, 0xC5BA, 0xC5BF, 0xC5C0, 0xC5C1, 0xC5C2, 0xC5C3, 0xC5CB, 0xC5CD, 0xC5CF, 0xC5D2, 0xC5D3, 0xC5D5, 0xC5D6, 0xC5D7, 0xC5D9, 0xC5DA, 0xC5DB, 0xC5DC, 0xC5DD, 0xC5DE, 0xC5DF, 0xC5E2, 0xC5E4, 0xC5E6, 0xC5E7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC5E8, 0xC5E9, 0xC5EA, 0xC5EB, 0xC5EF, 0xC5F1, 0xC5F2, 0xC5F3, 0xC5F5, 0xC5F8, 0xC5F9, 0xC5FA, 0xC5FB, 0xC602, 0xC603, 0xC604, 0xC609, 0xC60A, 0xC60B, 0xC60D, 0xC60E, 0xC60F, 0xC611, 0xC612, 0xC613, 0xC614, 0xC615, 0xC616, 0xC617, 0xC61A, 0xC61D, 0xC61E, 0xC61F, 0xC620, 0xC621, 0xC622, 0xC623, 0xC626, 0xC627, 0xC629, 0xC62A, 0xC62B, 0xC62F, 0xC631, 0xC632, 0xC636, 0xC638, 0xC63A, 0xC63C, 0xC63D, 0xC63E, 0xC63F, 0xC642, 0xC643, 0xC645, 0xC646, 0xC647, 0xC649, 0xC64A, 0xC64B, 0xC64C, 0xC64D, 0xC64E, 0xC64F, 0xC652, 0xC656, 0xC657, 0xC658, 0xC659, 0xC65A, 0xC65B, 0xC65E, 0xC65F, 0xC661, 0xC662, 0xC663, 0xC664, 0xC665, 0xC666, 0xC667, 0xC668, 0xC669, 0xC66A, 0xC66B, 0xC66D, 0xC66E, 0xC670, 0xC672, 0xC673, 0xC674, 0xC675, 0xC676, 0xC677, 0xC67A, 0xC67B, 0xC67D, 0xC67E, 0xC67F, 0xC681, 0xC682, 0xC683, 0xC684, 0xC685, 0xC686, 0xC687, 0xC68A, 0xC68C, 0xC68E, 0xC68F, 0xC690, 0xC691, 0xC692, 0xC693, 0xC696, 0xC697, 0xC699, 0xC69A, 0xC69B, 0xC69D, 0xC69E, 0xC69F, 0xC6A0, 0xC6A1, 0xC6A2, 0xC6A3, 0xC6A6, + 0xC6A8, 0xC6AA, 0xC6AB, 0xC6AC, 0xC6AD, 0xC6AE, 0xC6AF, 0xC6B2, 0xC6B3, 0xC6B5, 0xC6B6, 0xC6B7, 0xC6BB, 0xC6BC, 0xC6BD, 0xC6BE, 0xC6BF, 0xC6C2, 0xC6C4, 0xC6C6, 0xC6C7, 0xC6C8, 0xC6C9, 0xC6CA, 0xC6CB, 0xC6CE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC6CF, 0xC6D1, 0xC6D2, 0xC6D3, 0xC6D5, 0xC6D6, 0xC6D7, 0xC6D8, 0xC6D9, 0xC6DA, 0xC6DB, 0xC6DE, 0xC6DF, 0xC6E2, 0xC6E3, 0xC6E4, 0xC6E5, 0xC6E6, 0xC6E7, 0xC6EA, 0xC6EB, 0xC6ED, 0xC6EE, 0xC6EF, 0xC6F1, 0xC6F2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC6F3, 0xC6F4, 0xC6F5, 0xC6F6, 0xC6F7, 0xC6FA, 0xC6FB, 0xC6FC, 0xC6FE, 0xC6FF, 0xC700, 0xC701, 0xC702, 0xC703, 0xC706, 0xC707, 0xC709, 0xC70A, 0xC70B, 0xC70D, 0xC70E, 0xC70F, 0xC710, 0xC711, 0xC712, 0xC713, 0xC716, 0xC718, 0xC71A, 0xC71B, 0xC71C, 0xC71D, 0xC71E, 0xC71F, 0xC722, 0xC723, 0xC725, 0xC726, 0xC727, 0xC729, 0xC72A, 0xC72B, 0xC72C, 0xC72D, 0xC72E, 0xC72F, 0xC732, 0xC734, 0xC736, 0xC738, 0xC739, 0xC73A, 0xC73B, 0xC73E, 0xC73F, 0xC741, 0xC742, 0xC743, 0xC745, 0xC746, 0xC747, 0xC748, 0xC749, 0xC74B, 0xC74E, 0xC750, 0xC759, 0xC75A, 0xC75B, 0xC75D, 0xC75E, 0xC75F, 0xC761, 0xC762, 0xC763, 0xC764, 0xC765, 0xC766, 0xC767, 0xC769, 0xC76A, 0xC76C, 0xC76D, 0xC76E, 0xC76F, 0xC770, 0xC771, 0xC772, 0xC773, 0xC776, 0xC777, 0xC779, 0xC77A, 0xC77B, 0xC77F, 0xC780, 0xC781, 0xC782, 0xC786, 0xC78B, 0xC78C, 0xC78D, 0xC78F, 0xC792, 0xC793, 0xC795, 0xC799, 0xC79B, 0xC79C, 0xC79D, 0xC79E, 0xC79F, 0xC7A2, 0xC7A7, 0xC7A8, 0xC7A9, 0xC7AA, 0xC7AB, 0xC7AE, 0xC7AF, 0xC7B1, 0xC7B2, 0xC7B3, 0xC7B5, 0xC7B6, 0xC7B7, + 0xC7B8, 0xC7B9, 0xC7BA, 0xC7BB, 0xC7BE, 0xC7C2, 0xC7C3, 0xC7C4, 0xC7C5, 0xC7C6, 0xC7C7, 0xC7CA, 0xC7CB, 0xC7CD, 0xC7CF, 0xC7D1, 0xC7D2, 0xC7D3, 0xC7D4, 0xC7D5, 0xC7D6, 0xC7D7, 0xC7D9, 0xC7DA, 0xC7DB, 0xC7DC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC7DE, 0xC7DF, 0xC7E0, 0xC7E1, 0xC7E2, 0xC7E3, 0xC7E5, 0xC7E6, 0xC7E7, 0xC7E9, 0xC7EA, 0xC7EB, 0xC7ED, 0xC7EE, 0xC7EF, 0xC7F0, 0xC7F1, 0xC7F2, 0xC7F3, 0xC7F4, 0xC7F5, 0xC7F6, 0xC7F7, 0xC7F8, 0xC7F9, 0xC7FA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC7FB, 0xC7FC, 0xC7FD, 0xC7FE, 0xC7FF, 0xC802, 0xC803, 0xC805, 0xC806, 0xC807, 0xC809, 0xC80B, 0xC80C, 0xC80D, 0xC80E, 0xC80F, 0xC812, 0xC814, 0xC817, 0xC818, 0xC819, 0xC81A, 0xC81B, 0xC81E, 0xC81F, 0xC821, 0xC822, 0xC823, 0xC825, 0xC826, 0xC827, 0xC828, 0xC829, 0xC82A, 0xC82B, 0xC82E, 0xC830, 0xC832, 0xC833, 0xC834, 0xC835, 0xC836, 0xC837, 0xC839, 0xC83A, 0xC83B, 0xC83D, 0xC83E, 0xC83F, 0xC841, 0xC842, 0xC843, 0xC844, 0xC845, 0xC846, 0xC847, 0xC84A, 0xC84B, 0xC84E, 0xC84F, 0xC850, 0xC851, 0xC852, 0xC853, 0xC855, 0xC856, 0xC857, 0xC858, 0xC859, 0xC85A, 0xC85B, 0xC85C, 0xC85D, 0xC85E, 0xC85F, 0xC860, 0xC861, 0xC862, 0xC863, 0xC864, 0xC865, 0xC866, 0xC867, 0xC868, 0xC869, 0xC86A, 0xC86B, 0xC86C, 0xC86D, 0xC86E, 0xC86F, 0xC872, 0xC873, 0xC875, 0xC876, 0xC877, 0xC879, 0xC87B, 0xC87C, 0xC87D, 0xC87E, 0xC87F, 0xC882, 0xC884, 0xC888, 0xC889, 0xC88A, 0xC88E, 0xC88F, 0xC890, 0xC891, 0xC892, 0xC893, 0xC895, 0xC896, 0xC897, 0xC898, 0xC899, 0xC89A, 0xC89B, 0xC89C, 0xC89E, 0xC8A0, 0xC8A2, 0xC8A3, 0xC8A4, + 0xC8A5, 0xC8A6, 0xC8A7, 0xC8A9, 0xC8AA, 0xC8AB, 0xC8AC, 0xC8AD, 0xC8AE, 0xC8AF, 0xC8B0, 0xC8B1, 0xC8B2, 0xC8B3, 0xC8B4, 0xC8B5, 0xC8B6, 0xC8B7, 0xC8B8, 0xC8B9, 0xC8BA, 0xC8BB, 0xC8BE, 0xC8BF, 0xC8C0, 0xC8C1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC8C2, 0xC8C3, 0xC8C5, 0xC8C6, 0xC8C7, 0xC8C9, 0xC8CA, 0xC8CB, 0xC8CD, 0xC8CE, 0xC8CF, 0xC8D0, 0xC8D1, 0xC8D2, 0xC8D3, 0xC8D6, 0xC8D8, 0xC8DA, 0xC8DB, 0xC8DC, 0xC8DD, 0xC8DE, 0xC8DF, 0xC8E2, 0xC8E3, 0xC8E5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC8E6, 0xC8E7, 0xC8E8, 0xC8E9, 0xC8EA, 0xC8EB, 0xC8EC, 0xC8ED, 0xC8EE, 0xC8EF, 0xC8F0, 0xC8F1, 0xC8F2, 0xC8F3, 0xC8F4, 0xC8F6, 0xC8F7, 0xC8F8, 0xC8F9, 0xC8FA, 0xC8FB, 0xC8FE, 0xC8FF, 0xC901, 0xC902, 0xC903, 0xC907, 0xC908, 0xC909, 0xC90A, 0xC90B, 0xC90E, 0x3000, 0x3001, 0x3002, 0x00B7, 0x2025, 0x2026, 0x00A8, 0x3003, 0x00AD, 0x2015, 0x2225, 0xFF3C, 0x223C, 0x2018, 0x2019, 0x201C, 0x201D, 0x3014, 0x3015, 0x3008, 0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F, 0x3010, 0x3011, 0x00B1, 0x00D7, 0x00F7, 0x2260, 0x2264, 0x2265, 0x221E, 0x2234, 0x00B0, 0x2032, 0x2033, 0x2103, 0x212B, 0xFFE0, 0xFFE1, 0xFFE5, 0x2642, 0x2640, 0x2220, 0x22A5, 0x2312, 0x2202, 0x2207, 0x2261, 0x2252, 0x00A7, 0x203B, 0x2606, 0x2605, 0x25CB, 0x25CF, 0x25CE, 0x25C7, 0x25C6, 0x25A1, 0x25A0, 0x25B3, 0x25B2, 0x25BD, 0x25BC, 0x2192, 0x2190, 0x2191, 0x2193, 0x2194, 0x3013, 0x226A, 0x226B, 0x221A, 0x223D, 0x221D, 0x2235, 0x222B, 0x222C, 0x2208, 0x220B, 0x2286, 0x2287, 0x2282, 0x2283, 0x222A, 0x2229, 0x2227, 0x2228, 0xFFE2, + 0xC910, 0xC912, 0xC913, 0xC914, 0xC915, 0xC916, 0xC917, 0xC919, 0xC91A, 0xC91B, 0xC91C, 0xC91D, 0xC91E, 0xC91F, 0xC920, 0xC921, 0xC922, 0xC923, 0xC924, 0xC925, 0xC926, 0xC927, 0xC928, 0xC929, 0xC92A, 0xC92B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC92D, 0xC92E, 0xC92F, 0xC930, 0xC931, 0xC932, 0xC933, 0xC935, 0xC936, 0xC937, 0xC938, 0xC939, 0xC93A, 0xC93B, 0xC93C, 0xC93D, 0xC93E, 0xC93F, 0xC940, 0xC941, 0xC942, 0xC943, 0xC944, 0xC945, 0xC946, 0xC947, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC948, 0xC949, 0xC94A, 0xC94B, 0xC94C, 0xC94D, 0xC94E, 0xC94F, 0xC952, 0xC953, 0xC955, 0xC956, 0xC957, 0xC959, 0xC95A, 0xC95B, 0xC95C, 0xC95D, 0xC95E, 0xC95F, 0xC962, 0xC964, 0xC965, 0xC966, 0xC967, 0xC968, 0xC969, 0xC96A, 0xC96B, 0xC96D, 0xC96E, 0xC96F, 0x21D2, 0x21D4, 0x2200, 0x2203, 0x00B4, 0xFF5E, 0x02C7, 0x02D8, 0x02DD, 0x02DA, 0x02D9, 0x00B8, 0x02DB, 0x00A1, 0x00BF, 0x02D0, 0x222E, 0x2211, 0x220F, 0x00A4, 0x2109, 0x2030, 0x25C1, 0x25C0, 0x25B7, 0x25B6, 0x2664, 0x2660, 0x2661, 0x2665, 0x2667, 0x2663, 0x2299, 0x25C8, 0x25A3, 0x25D0, 0x25D1, 0x2592, 0x25A4, 0x25A5, 0x25A8, 0x25A7, 0x25A6, 0x25A9, 0x2668, 0x260F, 0x260E, 0x261C, 0x261E, 0x00B6, 0x2020, 0x2021, 0x2195, 0x2197, 0x2199, 0x2196, 0x2198, 0x266D, 0x2669, 0x266A, 0x266C, 0x327F, 0x321C, 0x2116, 0x33C7, 0x2122, 0x33C2, 0x33D8, 0x2121, 0x20AC, 0x00AE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xC971, 0xC972, 0xC973, 0xC975, 0xC976, 0xC977, 0xC978, 0xC979, 0xC97A, 0xC97B, 0xC97D, 0xC97E, 0xC97F, 0xC980, 0xC981, 0xC982, 0xC983, 0xC984, 0xC985, 0xC986, 0xC987, 0xC98A, 0xC98B, 0xC98D, 0xC98E, 0xC98F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC991, 0xC992, 0xC993, 0xC994, 0xC995, 0xC996, 0xC997, 0xC99A, 0xC99C, 0xC99E, 0xC99F, 0xC9A0, 0xC9A1, 0xC9A2, 0xC9A3, 0xC9A4, 0xC9A5, 0xC9A6, 0xC9A7, 0xC9A8, 0xC9A9, 0xC9AA, 0xC9AB, 0xC9AC, 0xC9AD, 0xC9AE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC9AF, 0xC9B0, 0xC9B1, 0xC9B2, 0xC9B3, 0xC9B4, 0xC9B5, 0xC9B6, 0xC9B7, 0xC9B8, 0xC9B9, 0xC9BA, 0xC9BB, 0xC9BC, 0xC9BD, 0xC9BE, 0xC9BF, 0xC9C2, 0xC9C3, 0xC9C5, 0xC9C6, 0xC9C9, 0xC9CB, 0xC9CC, 0xC9CD, 0xC9CE, 0xC9CF, 0xC9D2, 0xC9D4, 0xC9D7, 0xC9D8, 0xC9DB, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, 0xFF20, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0xFF3B, 0xFFE6, 0xFF3D, 0xFF3E, 0xFF3F, 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFFE3, + 0xC9DE, 0xC9DF, 0xC9E1, 0xC9E3, 0xC9E5, 0xC9E6, 0xC9E8, 0xC9E9, 0xC9EA, 0xC9EB, 0xC9EE, 0xC9F2, 0xC9F3, 0xC9F4, 0xC9F5, 0xC9F6, 0xC9F7, 0xC9FA, 0xC9FB, 0xC9FD, 0xC9FE, 0xC9FF, 0xCA01, 0xCA02, 0xCA03, 0xCA04, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCA05, 0xCA06, 0xCA07, 0xCA0A, 0xCA0E, 0xCA0F, 0xCA10, 0xCA11, 0xCA12, 0xCA13, 0xCA15, 0xCA16, 0xCA17, 0xCA19, 0xCA1A, 0xCA1B, 0xCA1C, 0xCA1D, 0xCA1E, 0xCA1F, 0xCA20, 0xCA21, 0xCA22, 0xCA23, 0xCA24, 0xCA25, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCA26, 0xCA27, 0xCA28, 0xCA2A, 0xCA2B, 0xCA2C, 0xCA2D, 0xCA2E, 0xCA2F, 0xCA30, 0xCA31, 0xCA32, 0xCA33, 0xCA34, 0xCA35, 0xCA36, 0xCA37, 0xCA38, 0xCA39, 0xCA3A, 0xCA3B, 0xCA3C, 0xCA3D, 0xCA3E, 0xCA3F, 0xCA40, 0xCA41, 0xCA42, 0xCA43, 0xCA44, 0xCA45, 0xCA46, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138, 0x3139, 0x313A, 0x313B, 0x313C, 0x313D, 0x313E, 0x313F, 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E, 0x314F, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315A, 0x315B, 0x315C, 0x315D, 0x315E, 0x315F, 0x3160, 0x3161, 0x3162, 0x3163, 0x3164, 0x3165, 0x3166, 0x3167, 0x3168, 0x3169, 0x316A, 0x316B, 0x316C, 0x316D, 0x316E, 0x316F, 0x3170, 0x3171, 0x3172, 0x3173, 0x3174, 0x3175, 0x3176, 0x3177, 0x3178, 0x3179, 0x317A, 0x317B, 0x317C, 0x317D, 0x317E, 0x317F, 0x3180, 0x3181, 0x3182, 0x3183, 0x3184, 0x3185, 0x3186, 0x3187, 0x3188, 0x3189, 0x318A, 0x318B, 0x318C, 0x318D, 0x318E, + 0xCA47, 0xCA48, 0xCA49, 0xCA4A, 0xCA4B, 0xCA4E, 0xCA4F, 0xCA51, 0xCA52, 0xCA53, 0xCA55, 0xCA56, 0xCA57, 0xCA58, 0xCA59, 0xCA5A, 0xCA5B, 0xCA5E, 0xCA62, 0xCA63, 0xCA64, 0xCA65, 0xCA66, 0xCA67, 0xCA69, 0xCA6A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCA6B, 0xCA6C, 0xCA6D, 0xCA6E, 0xCA6F, 0xCA70, 0xCA71, 0xCA72, 0xCA73, 0xCA74, 0xCA75, 0xCA76, 0xCA77, 0xCA78, 0xCA79, 0xCA7A, 0xCA7B, 0xCA7C, 0xCA7E, 0xCA7F, 0xCA80, 0xCA81, 0xCA82, 0xCA83, 0xCA85, 0xCA86, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCA87, 0xCA88, 0xCA89, 0xCA8A, 0xCA8B, 0xCA8C, 0xCA8D, 0xCA8E, 0xCA8F, 0xCA90, 0xCA91, 0xCA92, 0xCA93, 0xCA94, 0xCA95, 0xCA96, 0xCA97, 0xCA99, 0xCA9A, 0xCA9B, 0xCA9C, 0xCA9D, 0xCA9E, 0xCA9F, 0xCAA0, 0xCAA1, 0xCAA2, 0xCAA3, 0xCAA4, 0xCAA5, 0xCAA6, 0xCAA7, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCAA8, 0xCAA9, 0xCAAA, 0xCAAB, 0xCAAC, 0xCAAD, 0xCAAE, 0xCAAF, 0xCAB0, 0xCAB1, 0xCAB2, 0xCAB3, 0xCAB4, 0xCAB5, 0xCAB6, 0xCAB7, 0xCAB8, 0xCAB9, 0xCABA, 0xCABB, 0xCABE, 0xCABF, 0xCAC1, 0xCAC2, 0xCAC3, 0xCAC5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCAC6, 0xCAC7, 0xCAC8, 0xCAC9, 0xCACA, 0xCACB, 0xCACE, 0xCAD0, 0xCAD2, 0xCAD4, 0xCAD5, 0xCAD6, 0xCAD7, 0xCADA, 0xCADB, 0xCADC, 0xCADD, 0xCADE, 0xCADF, 0xCAE1, 0xCAE2, 0xCAE3, 0xCAE4, 0xCAE5, 0xCAE6, 0xCAE7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCAE8, 0xCAE9, 0xCAEA, 0xCAEB, 0xCAED, 0xCAEE, 0xCAEF, 0xCAF0, 0xCAF1, 0xCAF2, 0xCAF3, 0xCAF5, 0xCAF6, 0xCAF7, 0xCAF8, 0xCAF9, 0xCAFA, 0xCAFB, 0xCAFC, 0xCAFD, 0xCAFE, 0xCAFF, 0xCB00, 0xCB01, 0xCB02, 0xCB03, 0xCB04, 0xCB05, 0xCB06, 0xCB07, 0xCB09, 0xCB0A, 0x2500, 0x2502, 0x250C, 0x2510, 0x2518, 0x2514, 0x251C, 0x252C, 0x2524, 0x2534, 0x253C, 0x2501, 0x2503, 0x250F, 0x2513, 0x251B, 0x2517, 0x2523, 0x2533, 0x252B, 0x253B, 0x254B, 0x2520, 0x252F, 0x2528, 0x2537, 0x253F, 0x251D, 0x2530, 0x2525, 0x2538, 0x2542, 0x2512, 0x2511, 0x251A, 0x2519, 0x2516, 0x2515, 0x250E, 0x250D, 0x251E, 0x251F, 0x2521, 0x2522, 0x2526, 0x2527, 0x2529, 0x252A, 0x252D, 0x252E, 0x2531, 0x2532, 0x2535, 0x2536, 0x2539, 0x253A, 0x253D, 0x253E, 0x2540, 0x2541, 0x2543, 0x2544, 0x2545, 0x2546, 0x2547, 0x2548, 0x2549, 0x254A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCB0B, 0xCB0C, 0xCB0D, 0xCB0E, 0xCB0F, 0xCB11, 0xCB12, 0xCB13, 0xCB15, 0xCB16, 0xCB17, 0xCB19, 0xCB1A, 0xCB1B, 0xCB1C, 0xCB1D, 0xCB1E, 0xCB1F, 0xCB22, 0xCB23, 0xCB24, 0xCB25, 0xCB26, 0xCB27, 0xCB28, 0xCB29, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCB2A, 0xCB2B, 0xCB2C, 0xCB2D, 0xCB2E, 0xCB2F, 0xCB30, 0xCB31, 0xCB32, 0xCB33, 0xCB34, 0xCB35, 0xCB36, 0xCB37, 0xCB38, 0xCB39, 0xCB3A, 0xCB3B, 0xCB3C, 0xCB3D, 0xCB3E, 0xCB3F, 0xCB40, 0xCB42, 0xCB43, 0xCB44, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCB45, 0xCB46, 0xCB47, 0xCB4A, 0xCB4B, 0xCB4D, 0xCB4E, 0xCB4F, 0xCB51, 0xCB52, 0xCB53, 0xCB54, 0xCB55, 0xCB56, 0xCB57, 0xCB5A, 0xCB5B, 0xCB5C, 0xCB5E, 0xCB5F, 0xCB60, 0xCB61, 0xCB62, 0xCB63, 0xCB65, 0xCB66, 0xCB67, 0xCB68, 0xCB69, 0xCB6A, 0xCB6B, 0xCB6C, 0x3395, 0x3396, 0x3397, 0x2113, 0x3398, 0x33C4, 0x33A3, 0x33A4, 0x33A5, 0x33A6, 0x3399, 0x339A, 0x339B, 0x339C, 0x339D, 0x339E, 0x339F, 0x33A0, 0x33A1, 0x33A2, 0x33CA, 0x338D, 0x338E, 0x338F, 0x33CF, 0x3388, 0x3389, 0x33C8, 0x33A7, 0x33A8, 0x33B0, 0x33B1, 0x33B2, 0x33B3, 0x33B4, 0x33B5, 0x33B6, 0x33B7, 0x33B8, 0x33B9, 0x3380, 0x3381, 0x3382, 0x3383, 0x3384, 0x33BA, 0x33BB, 0x33BC, 0x33BD, 0x33BE, 0x33BF, 0x3390, 0x3391, 0x3392, 0x3393, 0x3394, 0x2126, 0x33C0, 0x33C1, 0x338A, 0x338B, 0x338C, 0x33D6, 0x33C5, 0x33AD, 0x33AE, 0x33AF, 0x33DB, 0x33A9, 0x33AA, 0x33AB, 0x33AC, 0x33DD, 0x33D0, 0x33D3, 0x33C3, 0x33C9, 0x33DC, 0x33C6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCB6D, 0xCB6E, 0xCB6F, 0xCB70, 0xCB71, 0xCB72, 0xCB73, 0xCB74, 0xCB75, 0xCB76, 0xCB77, 0xCB7A, 0xCB7B, 0xCB7C, 0xCB7D, 0xCB7E, 0xCB7F, 0xCB80, 0xCB81, 0xCB82, 0xCB83, 0xCB84, 0xCB85, 0xCB86, 0xCB87, 0xCB88, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCB89, 0xCB8A, 0xCB8B, 0xCB8C, 0xCB8D, 0xCB8E, 0xCB8F, 0xCB90, 0xCB91, 0xCB92, 0xCB93, 0xCB94, 0xCB95, 0xCB96, 0xCB97, 0xCB98, 0xCB99, 0xCB9A, 0xCB9B, 0xCB9D, 0xCB9E, 0xCB9F, 0xCBA0, 0xCBA1, 0xCBA2, 0xCBA3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCBA4, 0xCBA5, 0xCBA6, 0xCBA7, 0xCBA8, 0xCBA9, 0xCBAA, 0xCBAB, 0xCBAC, 0xCBAD, 0xCBAE, 0xCBAF, 0xCBB0, 0xCBB1, 0xCBB2, 0xCBB3, 0xCBB4, 0xCBB5, 0xCBB6, 0xCBB7, 0xCBB9, 0xCBBA, 0xCBBB, 0xCBBC, 0xCBBD, 0xCBBE, 0xCBBF, 0xCBC0, 0xCBC1, 0xCBC2, 0xCBC3, 0xCBC4, 0x00C6, 0x00D0, 0x00AA, 0x0126, 0x0000, 0x0132, 0x0000, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0000, 0x3260, 0x3261, 0x3262, 0x3263, 0x3264, 0x3265, 0x3266, 0x3267, 0x3268, 0x3269, 0x326A, 0x326B, 0x326C, 0x326D, 0x326E, 0x326F, 0x3270, 0x3271, 0x3272, 0x3273, 0x3274, 0x3275, 0x3276, 0x3277, 0x3278, 0x3279, 0x327A, 0x327B, 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4, 0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE, 0x24DF, 0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8, 0x24E9, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x00BD, 0x2153, 0x2154, 0x00BC, 0x00BE, 0x215B, 0x215C, 0x215D, 0x215E, + 0xCBC5, 0xCBC6, 0xCBC7, 0xCBC8, 0xCBC9, 0xCBCA, 0xCBCB, 0xCBCC, 0xCBCD, 0xCBCE, 0xCBCF, 0xCBD0, 0xCBD1, 0xCBD2, 0xCBD3, 0xCBD5, 0xCBD6, 0xCBD7, 0xCBD8, 0xCBD9, 0xCBDA, 0xCBDB, 0xCBDC, 0xCBDD, 0xCBDE, 0xCBDF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCBE0, 0xCBE1, 0xCBE2, 0xCBE3, 0xCBE5, 0xCBE6, 0xCBE8, 0xCBEA, 0xCBEB, 0xCBEC, 0xCBED, 0xCBEE, 0xCBEF, 0xCBF0, 0xCBF1, 0xCBF2, 0xCBF3, 0xCBF4, 0xCBF5, 0xCBF6, 0xCBF7, 0xCBF8, 0xCBF9, 0xCBFA, 0xCBFB, 0xCBFC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCBFD, 0xCBFE, 0xCBFF, 0xCC00, 0xCC01, 0xCC02, 0xCC03, 0xCC04, 0xCC05, 0xCC06, 0xCC07, 0xCC08, 0xCC09, 0xCC0A, 0xCC0B, 0xCC0E, 0xCC0F, 0xCC11, 0xCC12, 0xCC13, 0xCC15, 0xCC16, 0xCC17, 0xCC18, 0xCC19, 0xCC1A, 0xCC1B, 0xCC1E, 0xCC1F, 0xCC20, 0xCC23, 0xCC24, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0138, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x0149, 0x3200, 0x3201, 0x3202, 0x3203, 0x3204, 0x3205, 0x3206, 0x3207, 0x3208, 0x3209, 0x320A, 0x320B, 0x320C, 0x320D, 0x320E, 0x320F, 0x3210, 0x3211, 0x3212, 0x3213, 0x3214, 0x3215, 0x3216, 0x3217, 0x3218, 0x3219, 0x321A, 0x321B, 0x249C, 0x249D, 0x249E, 0x249F, 0x24A0, 0x24A1, 0x24A2, 0x24A3, 0x24A4, 0x24A5, 0x24A6, 0x24A7, 0x24A8, 0x24A9, 0x24AA, 0x24AB, 0x24AC, 0x24AD, 0x24AE, 0x24AF, 0x24B0, 0x24B1, 0x24B2, 0x24B3, 0x24B4, 0x24B5, 0x2474, 0x2475, 0x2476, 0x2477, 0x2478, 0x2479, 0x247A, 0x247B, 0x247C, 0x247D, 0x247E, 0x247F, 0x2480, 0x2481, 0x2482, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x207F, 0x2081, 0x2082, 0x2083, 0x2084, + 0xCC25, 0xCC26, 0xCC2A, 0xCC2B, 0xCC2D, 0xCC2F, 0xCC31, 0xCC32, 0xCC33, 0xCC34, 0xCC35, 0xCC36, 0xCC37, 0xCC3A, 0xCC3F, 0xCC40, 0xCC41, 0xCC42, 0xCC43, 0xCC46, 0xCC47, 0xCC49, 0xCC4A, 0xCC4B, 0xCC4D, 0xCC4E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCC4F, 0xCC50, 0xCC51, 0xCC52, 0xCC53, 0xCC56, 0xCC5A, 0xCC5B, 0xCC5C, 0xCC5D, 0xCC5E, 0xCC5F, 0xCC61, 0xCC62, 0xCC63, 0xCC65, 0xCC67, 0xCC69, 0xCC6A, 0xCC6B, 0xCC6C, 0xCC6D, 0xCC6E, 0xCC6F, 0xCC71, 0xCC72, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCC73, 0xCC74, 0xCC76, 0xCC77, 0xCC78, 0xCC79, 0xCC7A, 0xCC7B, 0xCC7C, 0xCC7D, 0xCC7E, 0xCC7F, 0xCC80, 0xCC81, 0xCC82, 0xCC83, 0xCC84, 0xCC85, 0xCC86, 0xCC87, 0xCC88, 0xCC89, 0xCC8A, 0xCC8B, 0xCC8C, 0xCC8D, 0xCC8E, 0xCC8F, 0xCC90, 0xCC91, 0xCC92, 0xCC93, 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304A, 0x304B, 0x304C, 0x304D, 0x304E, 0x304F, 0x3050, 0x3051, 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, 0x3059, 0x305A, 0x305B, 0x305C, 0x305D, 0x305E, 0x305F, 0x3060, 0x3061, 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, 0x306F, 0x3070, 0x3071, 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307A, 0x307B, 0x307C, 0x307D, 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, 0x308E, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCC94, 0xCC95, 0xCC96, 0xCC97, 0xCC9A, 0xCC9B, 0xCC9D, 0xCC9E, 0xCC9F, 0xCCA1, 0xCCA2, 0xCCA3, 0xCCA4, 0xCCA5, 0xCCA6, 0xCCA7, 0xCCAA, 0xCCAE, 0xCCAF, 0xCCB0, 0xCCB1, 0xCCB2, 0xCCB3, 0xCCB6, 0xCCB7, 0xCCB9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCCBA, 0xCCBB, 0xCCBD, 0xCCBE, 0xCCBF, 0xCCC0, 0xCCC1, 0xCCC2, 0xCCC3, 0xCCC6, 0xCCC8, 0xCCCA, 0xCCCB, 0xCCCC, 0xCCCD, 0xCCCE, 0xCCCF, 0xCCD1, 0xCCD2, 0xCCD3, 0xCCD5, 0xCCD6, 0xCCD7, 0xCCD8, 0xCCD9, 0xCCDA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCCDB, 0xCCDC, 0xCCDD, 0xCCDE, 0xCCDF, 0xCCE0, 0xCCE1, 0xCCE2, 0xCCE3, 0xCCE5, 0xCCE6, 0xCCE7, 0xCCE8, 0xCCE9, 0xCCEA, 0xCCEB, 0xCCED, 0xCCEE, 0xCCEF, 0xCCF1, 0xCCF2, 0xCCF3, 0xCCF4, 0xCCF5, 0xCCF6, 0xCCF7, 0xCCF8, 0xCCF9, 0xCCFA, 0xCCFB, 0xCCFC, 0xCCFD, 0x30A1, 0x30A2, 0x30A3, 0x30A4, 0x30A5, 0x30A6, 0x30A7, 0x30A8, 0x30A9, 0x30AA, 0x30AB, 0x30AC, 0x30AD, 0x30AE, 0x30AF, 0x30B0, 0x30B1, 0x30B2, 0x30B3, 0x30B4, 0x30B5, 0x30B6, 0x30B7, 0x30B8, 0x30B9, 0x30BA, 0x30BB, 0x30BC, 0x30BD, 0x30BE, 0x30BF, 0x30C0, 0x30C1, 0x30C2, 0x30C3, 0x30C4, 0x30C5, 0x30C6, 0x30C7, 0x30C8, 0x30C9, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, 0x30CF, 0x30D0, 0x30D1, 0x30D2, 0x30D3, 0x30D4, 0x30D5, 0x30D6, 0x30D7, 0x30D8, 0x30D9, 0x30DA, 0x30DB, 0x30DC, 0x30DD, 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E3, 0x30E4, 0x30E5, 0x30E6, 0x30E7, 0x30E8, 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EE, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3, 0x30F4, 0x30F5, 0x30F6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCCFE, 0xCCFF, 0xCD00, 0xCD02, 0xCD03, 0xCD04, 0xCD05, 0xCD06, 0xCD07, 0xCD0A, 0xCD0B, 0xCD0D, 0xCD0E, 0xCD0F, 0xCD11, 0xCD12, 0xCD13, 0xCD14, 0xCD15, 0xCD16, 0xCD17, 0xCD1A, 0xCD1C, 0xCD1E, 0xCD1F, 0xCD20, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCD21, 0xCD22, 0xCD23, 0xCD25, 0xCD26, 0xCD27, 0xCD29, 0xCD2A, 0xCD2B, 0xCD2D, 0xCD2E, 0xCD2F, 0xCD30, 0xCD31, 0xCD32, 0xCD33, 0xCD34, 0xCD35, 0xCD36, 0xCD37, 0xCD38, 0xCD3A, 0xCD3B, 0xCD3C, 0xCD3D, 0xCD3E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCD3F, 0xCD40, 0xCD41, 0xCD42, 0xCD43, 0xCD44, 0xCD45, 0xCD46, 0xCD47, 0xCD48, 0xCD49, 0xCD4A, 0xCD4B, 0xCD4C, 0xCD4D, 0xCD4E, 0xCD4F, 0xCD50, 0xCD51, 0xCD52, 0xCD53, 0xCD54, 0xCD55, 0xCD56, 0xCD57, 0xCD58, 0xCD59, 0xCD5A, 0xCD5B, 0xCD5D, 0xCD5E, 0xCD5F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCD61, 0xCD62, 0xCD63, 0xCD65, 0xCD66, 0xCD67, 0xCD68, 0xCD69, 0xCD6A, 0xCD6B, 0xCD6E, 0xCD70, 0xCD72, 0xCD73, 0xCD74, 0xCD75, 0xCD76, 0xCD77, 0xCD79, 0xCD7A, 0xCD7B, 0xCD7C, 0xCD7D, 0xCD7E, 0xCD7F, 0xCD80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCD81, 0xCD82, 0xCD83, 0xCD84, 0xCD85, 0xCD86, 0xCD87, 0xCD89, 0xCD8A, 0xCD8B, 0xCD8C, 0xCD8D, 0xCD8E, 0xCD8F, 0xCD90, 0xCD91, 0xCD92, 0xCD93, 0xCD96, 0xCD97, 0xCD99, 0xCD9A, 0xCD9B, 0xCD9D, 0xCD9E, 0xCD9F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCDA0, 0xCDA1, 0xCDA2, 0xCDA3, 0xCDA6, 0xCDA8, 0xCDAA, 0xCDAB, 0xCDAC, 0xCDAD, 0xCDAE, 0xCDAF, 0xCDB1, 0xCDB2, 0xCDB3, 0xCDB4, 0xCDB5, 0xCDB6, 0xCDB7, 0xCDB8, 0xCDB9, 0xCDBA, 0xCDBB, 0xCDBC, 0xCDBD, 0xCDBE, 0xCDBF, 0xCDC0, 0xCDC1, 0xCDC2, 0xCDC3, 0xCDC5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCDC6, 0xCDC7, 0xCDC8, 0xCDC9, 0xCDCA, 0xCDCB, 0xCDCD, 0xCDCE, 0xCDCF, 0xCDD1, 0xCDD2, 0xCDD3, 0xCDD4, 0xCDD5, 0xCDD6, 0xCDD7, 0xCDD8, 0xCDD9, 0xCDDA, 0xCDDB, 0xCDDC, 0xCDDD, 0xCDDE, 0xCDDF, 0xCDE0, 0xCDE1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCDE2, 0xCDE3, 0xCDE4, 0xCDE5, 0xCDE6, 0xCDE7, 0xCDE9, 0xCDEA, 0xCDEB, 0xCDED, 0xCDEE, 0xCDEF, 0xCDF1, 0xCDF2, 0xCDF3, 0xCDF4, 0xCDF5, 0xCDF6, 0xCDF7, 0xCDFA, 0xCDFC, 0xCDFE, 0xCDFF, 0xCE00, 0xCE01, 0xCE02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCE03, 0xCE05, 0xCE06, 0xCE07, 0xCE09, 0xCE0A, 0xCE0B, 0xCE0D, 0xCE0E, 0xCE0F, 0xCE10, 0xCE11, 0xCE12, 0xCE13, 0xCE15, 0xCE16, 0xCE17, 0xCE18, 0xCE1A, 0xCE1B, 0xCE1C, 0xCE1D, 0xCE1E, 0xCE1F, 0xCE22, 0xCE23, 0xCE25, 0xCE26, 0xCE27, 0xCE29, 0xCE2A, 0xCE2B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCE2C, 0xCE2D, 0xCE2E, 0xCE2F, 0xCE32, 0xCE34, 0xCE36, 0xCE37, 0xCE38, 0xCE39, 0xCE3A, 0xCE3B, 0xCE3C, 0xCE3D, 0xCE3E, 0xCE3F, 0xCE40, 0xCE41, 0xCE42, 0xCE43, 0xCE44, 0xCE45, 0xCE46, 0xCE47, 0xCE48, 0xCE49, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCE4A, 0xCE4B, 0xCE4C, 0xCE4D, 0xCE4E, 0xCE4F, 0xCE50, 0xCE51, 0xCE52, 0xCE53, 0xCE54, 0xCE55, 0xCE56, 0xCE57, 0xCE5A, 0xCE5B, 0xCE5D, 0xCE5E, 0xCE62, 0xCE63, 0xCE64, 0xCE65, 0xCE66, 0xCE67, 0xCE6A, 0xCE6C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCE6E, 0xCE6F, 0xCE70, 0xCE71, 0xCE72, 0xCE73, 0xCE76, 0xCE77, 0xCE79, 0xCE7A, 0xCE7B, 0xCE7D, 0xCE7E, 0xCE7F, 0xCE80, 0xCE81, 0xCE82, 0xCE83, 0xCE86, 0xCE88, 0xCE8A, 0xCE8B, 0xCE8C, 0xCE8D, 0xCE8E, 0xCE8F, 0xCE92, 0xCE93, 0xCE95, 0xCE96, 0xCE97, 0xCE99, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xCE9A, 0xCE9B, 0xCE9C, 0xCE9D, 0xCE9E, 0xCE9F, 0xCEA2, 0xCEA6, 0xCEA7, 0xCEA8, 0xCEA9, 0xCEAA, 0xCEAB, 0xCEAE, 0xCEAF, 0xCEB0, 0xCEB1, 0xCEB2, 0xCEB3, 0xCEB4, 0xCEB5, 0xCEB6, 0xCEB7, 0xCEB8, 0xCEB9, 0xCEBA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCEBB, 0xCEBC, 0xCEBD, 0xCEBE, 0xCEBF, 0xCEC0, 0xCEC2, 0xCEC3, 0xCEC4, 0xCEC5, 0xCEC6, 0xCEC7, 0xCEC8, 0xCEC9, 0xCECA, 0xCECB, 0xCECC, 0xCECD, 0xCECE, 0xCECF, 0xCED0, 0xCED1, 0xCED2, 0xCED3, 0xCED4, 0xCED5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCED6, 0xCED7, 0xCED8, 0xCED9, 0xCEDA, 0xCEDB, 0xCEDC, 0xCEDD, 0xCEDE, 0xCEDF, 0xCEE0, 0xCEE1, 0xCEE2, 0xCEE3, 0xCEE6, 0xCEE7, 0xCEE9, 0xCEEA, 0xCEED, 0xCEEE, 0xCEEF, 0xCEF0, 0xCEF1, 0xCEF2, 0xCEF3, 0xCEF6, 0xCEFA, 0xCEFB, 0xCEFC, 0xCEFD, 0xCEFE, 0xCEFF, 0xAC00, 0xAC01, 0xAC04, 0xAC07, 0xAC08, 0xAC09, 0xAC0A, 0xAC10, 0xAC11, 0xAC12, 0xAC13, 0xAC14, 0xAC15, 0xAC16, 0xAC17, 0xAC19, 0xAC1A, 0xAC1B, 0xAC1C, 0xAC1D, 0xAC20, 0xAC24, 0xAC2C, 0xAC2D, 0xAC2F, 0xAC30, 0xAC31, 0xAC38, 0xAC39, 0xAC3C, 0xAC40, 0xAC4B, 0xAC4D, 0xAC54, 0xAC58, 0xAC5C, 0xAC70, 0xAC71, 0xAC74, 0xAC77, 0xAC78, 0xAC7A, 0xAC80, 0xAC81, 0xAC83, 0xAC84, 0xAC85, 0xAC86, 0xAC89, 0xAC8A, 0xAC8B, 0xAC8C, 0xAC90, 0xAC94, 0xAC9C, 0xAC9D, 0xAC9F, 0xACA0, 0xACA1, 0xACA8, 0xACA9, 0xACAA, 0xACAC, 0xACAF, 0xACB0, 0xACB8, 0xACB9, 0xACBB, 0xACBC, 0xACBD, 0xACC1, 0xACC4, 0xACC8, 0xACCC, 0xACD5, 0xACD7, 0xACE0, 0xACE1, 0xACE4, 0xACE7, 0xACE8, 0xACEA, 0xACEC, 0xACEF, 0xACF0, 0xACF1, 0xACF3, 0xACF5, 0xACF6, 0xACFC, 0xACFD, 0xAD00, 0xAD04, 0xAD06, + 0xCF02, 0xCF03, 0xCF05, 0xCF06, 0xCF07, 0xCF09, 0xCF0A, 0xCF0B, 0xCF0C, 0xCF0D, 0xCF0E, 0xCF0F, 0xCF12, 0xCF14, 0xCF16, 0xCF17, 0xCF18, 0xCF19, 0xCF1A, 0xCF1B, 0xCF1D, 0xCF1E, 0xCF1F, 0xCF21, 0xCF22, 0xCF23, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCF25, 0xCF26, 0xCF27, 0xCF28, 0xCF29, 0xCF2A, 0xCF2B, 0xCF2E, 0xCF32, 0xCF33, 0xCF34, 0xCF35, 0xCF36, 0xCF37, 0xCF39, 0xCF3A, 0xCF3B, 0xCF3C, 0xCF3D, 0xCF3E, 0xCF3F, 0xCF40, 0xCF41, 0xCF42, 0xCF43, 0xCF44, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCF45, 0xCF46, 0xCF47, 0xCF48, 0xCF49, 0xCF4A, 0xCF4B, 0xCF4C, 0xCF4D, 0xCF4E, 0xCF4F, 0xCF50, 0xCF51, 0xCF52, 0xCF53, 0xCF56, 0xCF57, 0xCF59, 0xCF5A, 0xCF5B, 0xCF5D, 0xCF5E, 0xCF5F, 0xCF60, 0xCF61, 0xCF62, 0xCF63, 0xCF66, 0xCF68, 0xCF6A, 0xCF6B, 0xCF6C, 0xAD0C, 0xAD0D, 0xAD0F, 0xAD11, 0xAD18, 0xAD1C, 0xAD20, 0xAD29, 0xAD2C, 0xAD2D, 0xAD34, 0xAD35, 0xAD38, 0xAD3C, 0xAD44, 0xAD45, 0xAD47, 0xAD49, 0xAD50, 0xAD54, 0xAD58, 0xAD61, 0xAD63, 0xAD6C, 0xAD6D, 0xAD70, 0xAD73, 0xAD74, 0xAD75, 0xAD76, 0xAD7B, 0xAD7C, 0xAD7D, 0xAD7F, 0xAD81, 0xAD82, 0xAD88, 0xAD89, 0xAD8C, 0xAD90, 0xAD9C, 0xAD9D, 0xADA4, 0xADB7, 0xADC0, 0xADC1, 0xADC4, 0xADC8, 0xADD0, 0xADD1, 0xADD3, 0xADDC, 0xADE0, 0xADE4, 0xADF8, 0xADF9, 0xADFC, 0xADFF, 0xAE00, 0xAE01, 0xAE08, 0xAE09, 0xAE0B, 0xAE0D, 0xAE14, 0xAE30, 0xAE31, 0xAE34, 0xAE37, 0xAE38, 0xAE3A, 0xAE40, 0xAE41, 0xAE43, 0xAE45, 0xAE46, 0xAE4A, 0xAE4C, 0xAE4D, 0xAE4E, 0xAE50, 0xAE54, 0xAE56, 0xAE5C, 0xAE5D, 0xAE5F, 0xAE60, 0xAE61, 0xAE65, 0xAE68, 0xAE69, 0xAE6C, 0xAE70, 0xAE78, + 0xCF6D, 0xCF6E, 0xCF6F, 0xCF72, 0xCF73, 0xCF75, 0xCF76, 0xCF77, 0xCF79, 0xCF7A, 0xCF7B, 0xCF7C, 0xCF7D, 0xCF7E, 0xCF7F, 0xCF81, 0xCF82, 0xCF83, 0xCF84, 0xCF86, 0xCF87, 0xCF88, 0xCF89, 0xCF8A, 0xCF8B, 0xCF8D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCF8E, 0xCF8F, 0xCF90, 0xCF91, 0xCF92, 0xCF93, 0xCF94, 0xCF95, 0xCF96, 0xCF97, 0xCF98, 0xCF99, 0xCF9A, 0xCF9B, 0xCF9C, 0xCF9D, 0xCF9E, 0xCF9F, 0xCFA0, 0xCFA2, 0xCFA3, 0xCFA4, 0xCFA5, 0xCFA6, 0xCFA7, 0xCFA9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCFAA, 0xCFAB, 0xCFAC, 0xCFAD, 0xCFAE, 0xCFAF, 0xCFB1, 0xCFB2, 0xCFB3, 0xCFB4, 0xCFB5, 0xCFB6, 0xCFB7, 0xCFB8, 0xCFB9, 0xCFBA, 0xCFBB, 0xCFBC, 0xCFBD, 0xCFBE, 0xCFBF, 0xCFC0, 0xCFC1, 0xCFC2, 0xCFC3, 0xCFC5, 0xCFC6, 0xCFC7, 0xCFC8, 0xCFC9, 0xCFCA, 0xCFCB, 0xAE79, 0xAE7B, 0xAE7C, 0xAE7D, 0xAE84, 0xAE85, 0xAE8C, 0xAEBC, 0xAEBD, 0xAEBE, 0xAEC0, 0xAEC4, 0xAECC, 0xAECD, 0xAECF, 0xAED0, 0xAED1, 0xAED8, 0xAED9, 0xAEDC, 0xAEE8, 0xAEEB, 0xAEED, 0xAEF4, 0xAEF8, 0xAEFC, 0xAF07, 0xAF08, 0xAF0D, 0xAF10, 0xAF2C, 0xAF2D, 0xAF30, 0xAF32, 0xAF34, 0xAF3C, 0xAF3D, 0xAF3F, 0xAF41, 0xAF42, 0xAF43, 0xAF48, 0xAF49, 0xAF50, 0xAF5C, 0xAF5D, 0xAF64, 0xAF65, 0xAF79, 0xAF80, 0xAF84, 0xAF88, 0xAF90, 0xAF91, 0xAF95, 0xAF9C, 0xAFB8, 0xAFB9, 0xAFBC, 0xAFC0, 0xAFC7, 0xAFC8, 0xAFC9, 0xAFCB, 0xAFCD, 0xAFCE, 0xAFD4, 0xAFDC, 0xAFE8, 0xAFE9, 0xAFF0, 0xAFF1, 0xAFF4, 0xAFF8, 0xB000, 0xB001, 0xB004, 0xB00C, 0xB010, 0xB014, 0xB01C, 0xB01D, 0xB028, 0xB044, 0xB045, 0xB048, 0xB04A, 0xB04C, 0xB04E, 0xB053, 0xB054, 0xB055, 0xB057, 0xB059, + 0xCFCC, 0xCFCD, 0xCFCE, 0xCFCF, 0xCFD0, 0xCFD1, 0xCFD2, 0xCFD3, 0xCFD4, 0xCFD5, 0xCFD6, 0xCFD7, 0xCFD8, 0xCFD9, 0xCFDA, 0xCFDB, 0xCFDC, 0xCFDD, 0xCFDE, 0xCFDF, 0xCFE2, 0xCFE3, 0xCFE5, 0xCFE6, 0xCFE7, 0xCFE9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xCFEA, 0xCFEB, 0xCFEC, 0xCFED, 0xCFEE, 0xCFEF, 0xCFF2, 0xCFF4, 0xCFF6, 0xCFF7, 0xCFF8, 0xCFF9, 0xCFFA, 0xCFFB, 0xCFFD, 0xCFFE, 0xCFFF, 0xD001, 0xD002, 0xD003, 0xD005, 0xD006, 0xD007, 0xD008, 0xD009, 0xD00A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD00B, 0xD00C, 0xD00D, 0xD00E, 0xD00F, 0xD010, 0xD012, 0xD013, 0xD014, 0xD015, 0xD016, 0xD017, 0xD019, 0xD01A, 0xD01B, 0xD01C, 0xD01D, 0xD01E, 0xD01F, 0xD020, 0xD021, 0xD022, 0xD023, 0xD024, 0xD025, 0xD026, 0xD027, 0xD028, 0xD029, 0xD02A, 0xD02B, 0xD02C, 0xB05D, 0xB07C, 0xB07D, 0xB080, 0xB084, 0xB08C, 0xB08D, 0xB08F, 0xB091, 0xB098, 0xB099, 0xB09A, 0xB09C, 0xB09F, 0xB0A0, 0xB0A1, 0xB0A2, 0xB0A8, 0xB0A9, 0xB0AB, 0xB0AC, 0xB0AD, 0xB0AE, 0xB0AF, 0xB0B1, 0xB0B3, 0xB0B4, 0xB0B5, 0xB0B8, 0xB0BC, 0xB0C4, 0xB0C5, 0xB0C7, 0xB0C8, 0xB0C9, 0xB0D0, 0xB0D1, 0xB0D4, 0xB0D8, 0xB0E0, 0xB0E5, 0xB108, 0xB109, 0xB10B, 0xB10C, 0xB110, 0xB112, 0xB113, 0xB118, 0xB119, 0xB11B, 0xB11C, 0xB11D, 0xB123, 0xB124, 0xB125, 0xB128, 0xB12C, 0xB134, 0xB135, 0xB137, 0xB138, 0xB139, 0xB140, 0xB141, 0xB144, 0xB148, 0xB150, 0xB151, 0xB154, 0xB155, 0xB158, 0xB15C, 0xB160, 0xB178, 0xB179, 0xB17C, 0xB180, 0xB182, 0xB188, 0xB189, 0xB18B, 0xB18D, 0xB192, 0xB193, 0xB194, 0xB198, 0xB19C, 0xB1A8, 0xB1CC, 0xB1D0, 0xB1D4, 0xB1DC, 0xB1DD, + 0xD02E, 0xD02F, 0xD030, 0xD031, 0xD032, 0xD033, 0xD036, 0xD037, 0xD039, 0xD03A, 0xD03B, 0xD03D, 0xD03E, 0xD03F, 0xD040, 0xD041, 0xD042, 0xD043, 0xD046, 0xD048, 0xD04A, 0xD04B, 0xD04C, 0xD04D, 0xD04E, 0xD04F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD051, 0xD052, 0xD053, 0xD055, 0xD056, 0xD057, 0xD059, 0xD05A, 0xD05B, 0xD05C, 0xD05D, 0xD05E, 0xD05F, 0xD061, 0xD062, 0xD063, 0xD064, 0xD065, 0xD066, 0xD067, 0xD068, 0xD069, 0xD06A, 0xD06B, 0xD06E, 0xD06F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD071, 0xD072, 0xD073, 0xD075, 0xD076, 0xD077, 0xD078, 0xD079, 0xD07A, 0xD07B, 0xD07E, 0xD07F, 0xD080, 0xD082, 0xD083, 0xD084, 0xD085, 0xD086, 0xD087, 0xD088, 0xD089, 0xD08A, 0xD08B, 0xD08C, 0xD08D, 0xD08E, 0xD08F, 0xD090, 0xD091, 0xD092, 0xD093, 0xD094, 0xB1DF, 0xB1E8, 0xB1E9, 0xB1EC, 0xB1F0, 0xB1F9, 0xB1FB, 0xB1FD, 0xB204, 0xB205, 0xB208, 0xB20B, 0xB20C, 0xB214, 0xB215, 0xB217, 0xB219, 0xB220, 0xB234, 0xB23C, 0xB258, 0xB25C, 0xB260, 0xB268, 0xB269, 0xB274, 0xB275, 0xB27C, 0xB284, 0xB285, 0xB289, 0xB290, 0xB291, 0xB294, 0xB298, 0xB299, 0xB29A, 0xB2A0, 0xB2A1, 0xB2A3, 0xB2A5, 0xB2A6, 0xB2AA, 0xB2AC, 0xB2B0, 0xB2B4, 0xB2C8, 0xB2C9, 0xB2CC, 0xB2D0, 0xB2D2, 0xB2D8, 0xB2D9, 0xB2DB, 0xB2DD, 0xB2E2, 0xB2E4, 0xB2E5, 0xB2E6, 0xB2E8, 0xB2EB, 0xB2EC, 0xB2ED, 0xB2EE, 0xB2EF, 0xB2F3, 0xB2F4, 0xB2F5, 0xB2F7, 0xB2F8, 0xB2F9, 0xB2FA, 0xB2FB, 0xB2FF, 0xB300, 0xB301, 0xB304, 0xB308, 0xB310, 0xB311, 0xB313, 0xB314, 0xB315, 0xB31C, 0xB354, 0xB355, 0xB356, 0xB358, 0xB35B, 0xB35C, 0xB35E, 0xB35F, 0xB364, 0xB365, + 0xD095, 0xD096, 0xD097, 0xD098, 0xD099, 0xD09A, 0xD09B, 0xD09C, 0xD09D, 0xD09E, 0xD09F, 0xD0A0, 0xD0A1, 0xD0A2, 0xD0A3, 0xD0A6, 0xD0A7, 0xD0A9, 0xD0AA, 0xD0AB, 0xD0AD, 0xD0AE, 0xD0AF, 0xD0B0, 0xD0B1, 0xD0B2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD0B3, 0xD0B6, 0xD0B8, 0xD0BA, 0xD0BB, 0xD0BC, 0xD0BD, 0xD0BE, 0xD0BF, 0xD0C2, 0xD0C3, 0xD0C5, 0xD0C6, 0xD0C7, 0xD0CA, 0xD0CB, 0xD0CC, 0xD0CD, 0xD0CE, 0xD0CF, 0xD0D2, 0xD0D6, 0xD0D7, 0xD0D8, 0xD0D9, 0xD0DA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD0DB, 0xD0DE, 0xD0DF, 0xD0E1, 0xD0E2, 0xD0E3, 0xD0E5, 0xD0E6, 0xD0E7, 0xD0E8, 0xD0E9, 0xD0EA, 0xD0EB, 0xD0EE, 0xD0F2, 0xD0F3, 0xD0F4, 0xD0F5, 0xD0F6, 0xD0F7, 0xD0F9, 0xD0FA, 0xD0FB, 0xD0FC, 0xD0FD, 0xD0FE, 0xD0FF, 0xD100, 0xD101, 0xD102, 0xD103, 0xD104, 0xB367, 0xB369, 0xB36B, 0xB36E, 0xB370, 0xB371, 0xB374, 0xB378, 0xB380, 0xB381, 0xB383, 0xB384, 0xB385, 0xB38C, 0xB390, 0xB394, 0xB3A0, 0xB3A1, 0xB3A8, 0xB3AC, 0xB3C4, 0xB3C5, 0xB3C8, 0xB3CB, 0xB3CC, 0xB3CE, 0xB3D0, 0xB3D4, 0xB3D5, 0xB3D7, 0xB3D9, 0xB3DB, 0xB3DD, 0xB3E0, 0xB3E4, 0xB3E8, 0xB3FC, 0xB410, 0xB418, 0xB41C, 0xB420, 0xB428, 0xB429, 0xB42B, 0xB434, 0xB450, 0xB451, 0xB454, 0xB458, 0xB460, 0xB461, 0xB463, 0xB465, 0xB46C, 0xB480, 0xB488, 0xB49D, 0xB4A4, 0xB4A8, 0xB4AC, 0xB4B5, 0xB4B7, 0xB4B9, 0xB4C0, 0xB4C4, 0xB4C8, 0xB4D0, 0xB4D5, 0xB4DC, 0xB4DD, 0xB4E0, 0xB4E3, 0xB4E4, 0xB4E6, 0xB4EC, 0xB4ED, 0xB4EF, 0xB4F1, 0xB4F8, 0xB514, 0xB515, 0xB518, 0xB51B, 0xB51C, 0xB524, 0xB525, 0xB527, 0xB528, 0xB529, 0xB52A, 0xB530, 0xB531, 0xB534, 0xB538, + 0xD105, 0xD106, 0xD107, 0xD108, 0xD109, 0xD10A, 0xD10B, 0xD10C, 0xD10E, 0xD10F, 0xD110, 0xD111, 0xD112, 0xD113, 0xD114, 0xD115, 0xD116, 0xD117, 0xD118, 0xD119, 0xD11A, 0xD11B, 0xD11C, 0xD11D, 0xD11E, 0xD11F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD120, 0xD121, 0xD122, 0xD123, 0xD124, 0xD125, 0xD126, 0xD127, 0xD128, 0xD129, 0xD12A, 0xD12B, 0xD12C, 0xD12D, 0xD12E, 0xD12F, 0xD132, 0xD133, 0xD135, 0xD136, 0xD137, 0xD139, 0xD13B, 0xD13C, 0xD13D, 0xD13E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD13F, 0xD142, 0xD146, 0xD147, 0xD148, 0xD149, 0xD14A, 0xD14B, 0xD14E, 0xD14F, 0xD151, 0xD152, 0xD153, 0xD155, 0xD156, 0xD157, 0xD158, 0xD159, 0xD15A, 0xD15B, 0xD15E, 0xD160, 0xD162, 0xD163, 0xD164, 0xD165, 0xD166, 0xD167, 0xD169, 0xD16A, 0xD16B, 0xD16D, 0xB540, 0xB541, 0xB543, 0xB544, 0xB545, 0xB54B, 0xB54C, 0xB54D, 0xB550, 0xB554, 0xB55C, 0xB55D, 0xB55F, 0xB560, 0xB561, 0xB5A0, 0xB5A1, 0xB5A4, 0xB5A8, 0xB5AA, 0xB5AB, 0xB5B0, 0xB5B1, 0xB5B3, 0xB5B4, 0xB5B5, 0xB5BB, 0xB5BC, 0xB5BD, 0xB5C0, 0xB5C4, 0xB5CC, 0xB5CD, 0xB5CF, 0xB5D0, 0xB5D1, 0xB5D8, 0xB5EC, 0xB610, 0xB611, 0xB614, 0xB618, 0xB625, 0xB62C, 0xB634, 0xB648, 0xB664, 0xB668, 0xB69C, 0xB69D, 0xB6A0, 0xB6A4, 0xB6AB, 0xB6AC, 0xB6B1, 0xB6D4, 0xB6F0, 0xB6F4, 0xB6F8, 0xB700, 0xB701, 0xB705, 0xB728, 0xB729, 0xB72C, 0xB72F, 0xB730, 0xB738, 0xB739, 0xB73B, 0xB744, 0xB748, 0xB74C, 0xB754, 0xB755, 0xB760, 0xB764, 0xB768, 0xB770, 0xB771, 0xB773, 0xB775, 0xB77C, 0xB77D, 0xB780, 0xB784, 0xB78C, 0xB78D, 0xB78F, 0xB790, 0xB791, 0xB792, 0xB796, 0xB797, + 0xD16E, 0xD16F, 0xD170, 0xD171, 0xD172, 0xD173, 0xD174, 0xD175, 0xD176, 0xD177, 0xD178, 0xD179, 0xD17A, 0xD17B, 0xD17D, 0xD17E, 0xD17F, 0xD180, 0xD181, 0xD182, 0xD183, 0xD185, 0xD186, 0xD187, 0xD189, 0xD18A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD18B, 0xD18C, 0xD18D, 0xD18E, 0xD18F, 0xD190, 0xD191, 0xD192, 0xD193, 0xD194, 0xD195, 0xD196, 0xD197, 0xD198, 0xD199, 0xD19A, 0xD19B, 0xD19C, 0xD19D, 0xD19E, 0xD19F, 0xD1A2, 0xD1A3, 0xD1A5, 0xD1A6, 0xD1A7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD1A9, 0xD1AA, 0xD1AB, 0xD1AC, 0xD1AD, 0xD1AE, 0xD1AF, 0xD1B2, 0xD1B4, 0xD1B6, 0xD1B7, 0xD1B8, 0xD1B9, 0xD1BB, 0xD1BD, 0xD1BE, 0xD1BF, 0xD1C1, 0xD1C2, 0xD1C3, 0xD1C4, 0xD1C5, 0xD1C6, 0xD1C7, 0xD1C8, 0xD1C9, 0xD1CA, 0xD1CB, 0xD1CC, 0xD1CD, 0xD1CE, 0xD1CF, 0xB798, 0xB799, 0xB79C, 0xB7A0, 0xB7A8, 0xB7A9, 0xB7AB, 0xB7AC, 0xB7AD, 0xB7B4, 0xB7B5, 0xB7B8, 0xB7C7, 0xB7C9, 0xB7EC, 0xB7ED, 0xB7F0, 0xB7F4, 0xB7FC, 0xB7FD, 0xB7FF, 0xB800, 0xB801, 0xB807, 0xB808, 0xB809, 0xB80C, 0xB810, 0xB818, 0xB819, 0xB81B, 0xB81D, 0xB824, 0xB825, 0xB828, 0xB82C, 0xB834, 0xB835, 0xB837, 0xB838, 0xB839, 0xB840, 0xB844, 0xB851, 0xB853, 0xB85C, 0xB85D, 0xB860, 0xB864, 0xB86C, 0xB86D, 0xB86F, 0xB871, 0xB878, 0xB87C, 0xB88D, 0xB8A8, 0xB8B0, 0xB8B4, 0xB8B8, 0xB8C0, 0xB8C1, 0xB8C3, 0xB8C5, 0xB8CC, 0xB8D0, 0xB8D4, 0xB8DD, 0xB8DF, 0xB8E1, 0xB8E8, 0xB8E9, 0xB8EC, 0xB8F0, 0xB8F8, 0xB8F9, 0xB8FB, 0xB8FD, 0xB904, 0xB918, 0xB920, 0xB93C, 0xB93D, 0xB940, 0xB944, 0xB94C, 0xB94F, 0xB951, 0xB958, 0xB959, 0xB95C, 0xB960, 0xB968, 0xB969, + 0xD1D0, 0xD1D1, 0xD1D2, 0xD1D3, 0xD1D4, 0xD1D5, 0xD1D6, 0xD1D7, 0xD1D9, 0xD1DA, 0xD1DB, 0xD1DC, 0xD1DD, 0xD1DE, 0xD1DF, 0xD1E0, 0xD1E1, 0xD1E2, 0xD1E3, 0xD1E4, 0xD1E5, 0xD1E6, 0xD1E7, 0xD1E8, 0xD1E9, 0xD1EA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD1EB, 0xD1EC, 0xD1ED, 0xD1EE, 0xD1EF, 0xD1F0, 0xD1F1, 0xD1F2, 0xD1F3, 0xD1F5, 0xD1F6, 0xD1F7, 0xD1F9, 0xD1FA, 0xD1FB, 0xD1FC, 0xD1FD, 0xD1FE, 0xD1FF, 0xD200, 0xD201, 0xD202, 0xD203, 0xD204, 0xD205, 0xD206, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD208, 0xD20A, 0xD20B, 0xD20C, 0xD20D, 0xD20E, 0xD20F, 0xD211, 0xD212, 0xD213, 0xD214, 0xD215, 0xD216, 0xD217, 0xD218, 0xD219, 0xD21A, 0xD21B, 0xD21C, 0xD21D, 0xD21E, 0xD21F, 0xD220, 0xD221, 0xD222, 0xD223, 0xD224, 0xD225, 0xD226, 0xD227, 0xD228, 0xD229, 0xB96B, 0xB96D, 0xB974, 0xB975, 0xB978, 0xB97C, 0xB984, 0xB985, 0xB987, 0xB989, 0xB98A, 0xB98D, 0xB98E, 0xB9AC, 0xB9AD, 0xB9B0, 0xB9B4, 0xB9BC, 0xB9BD, 0xB9BF, 0xB9C1, 0xB9C8, 0xB9C9, 0xB9CC, 0xB9CE, 0xB9CF, 0xB9D0, 0xB9D1, 0xB9D2, 0xB9D8, 0xB9D9, 0xB9DB, 0xB9DD, 0xB9DE, 0xB9E1, 0xB9E3, 0xB9E4, 0xB9E5, 0xB9E8, 0xB9EC, 0xB9F4, 0xB9F5, 0xB9F7, 0xB9F8, 0xB9F9, 0xB9FA, 0xBA00, 0xBA01, 0xBA08, 0xBA15, 0xBA38, 0xBA39, 0xBA3C, 0xBA40, 0xBA42, 0xBA48, 0xBA49, 0xBA4B, 0xBA4D, 0xBA4E, 0xBA53, 0xBA54, 0xBA55, 0xBA58, 0xBA5C, 0xBA64, 0xBA65, 0xBA67, 0xBA68, 0xBA69, 0xBA70, 0xBA71, 0xBA74, 0xBA78, 0xBA83, 0xBA84, 0xBA85, 0xBA87, 0xBA8C, 0xBAA8, 0xBAA9, 0xBAAB, 0xBAAC, 0xBAB0, 0xBAB2, 0xBAB8, 0xBAB9, 0xBABB, 0xBABD, 0xBAC4, 0xBAC8, 0xBAD8, 0xBAD9, 0xBAFC, + 0xD22A, 0xD22B, 0xD22E, 0xD22F, 0xD231, 0xD232, 0xD233, 0xD235, 0xD236, 0xD237, 0xD238, 0xD239, 0xD23A, 0xD23B, 0xD23E, 0xD240, 0xD242, 0xD243, 0xD244, 0xD245, 0xD246, 0xD247, 0xD249, 0xD24A, 0xD24B, 0xD24C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD24D, 0xD24E, 0xD24F, 0xD250, 0xD251, 0xD252, 0xD253, 0xD254, 0xD255, 0xD256, 0xD257, 0xD258, 0xD259, 0xD25A, 0xD25B, 0xD25D, 0xD25E, 0xD25F, 0xD260, 0xD261, 0xD262, 0xD263, 0xD265, 0xD266, 0xD267, 0xD268, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD269, 0xD26A, 0xD26B, 0xD26C, 0xD26D, 0xD26E, 0xD26F, 0xD270, 0xD271, 0xD272, 0xD273, 0xD274, 0xD275, 0xD276, 0xD277, 0xD278, 0xD279, 0xD27A, 0xD27B, 0xD27C, 0xD27D, 0xD27E, 0xD27F, 0xD282, 0xD283, 0xD285, 0xD286, 0xD287, 0xD289, 0xD28A, 0xD28B, 0xD28C, 0xBB00, 0xBB04, 0xBB0D, 0xBB0F, 0xBB11, 0xBB18, 0xBB1C, 0xBB20, 0xBB29, 0xBB2B, 0xBB34, 0xBB35, 0xBB36, 0xBB38, 0xBB3B, 0xBB3C, 0xBB3D, 0xBB3E, 0xBB44, 0xBB45, 0xBB47, 0xBB49, 0xBB4D, 0xBB4F, 0xBB50, 0xBB54, 0xBB58, 0xBB61, 0xBB63, 0xBB6C, 0xBB88, 0xBB8C, 0xBB90, 0xBBA4, 0xBBA8, 0xBBAC, 0xBBB4, 0xBBB7, 0xBBC0, 0xBBC4, 0xBBC8, 0xBBD0, 0xBBD3, 0xBBF8, 0xBBF9, 0xBBFC, 0xBBFF, 0xBC00, 0xBC02, 0xBC08, 0xBC09, 0xBC0B, 0xBC0C, 0xBC0D, 0xBC0F, 0xBC11, 0xBC14, 0xBC15, 0xBC16, 0xBC17, 0xBC18, 0xBC1B, 0xBC1C, 0xBC1D, 0xBC1E, 0xBC1F, 0xBC24, 0xBC25, 0xBC27, 0xBC29, 0xBC2D, 0xBC30, 0xBC31, 0xBC34, 0xBC38, 0xBC40, 0xBC41, 0xBC43, 0xBC44, 0xBC45, 0xBC49, 0xBC4C, 0xBC4D, 0xBC50, 0xBC5D, 0xBC84, 0xBC85, 0xBC88, 0xBC8B, 0xBC8C, 0xBC8E, 0xBC94, 0xBC95, 0xBC97, + 0xD28D, 0xD28E, 0xD28F, 0xD292, 0xD293, 0xD294, 0xD296, 0xD297, 0xD298, 0xD299, 0xD29A, 0xD29B, 0xD29D, 0xD29E, 0xD29F, 0xD2A1, 0xD2A2, 0xD2A3, 0xD2A5, 0xD2A6, 0xD2A7, 0xD2A8, 0xD2A9, 0xD2AA, 0xD2AB, 0xD2AD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD2AE, 0xD2AF, 0xD2B0, 0xD2B2, 0xD2B3, 0xD2B4, 0xD2B5, 0xD2B6, 0xD2B7, 0xD2BA, 0xD2BB, 0xD2BD, 0xD2BE, 0xD2C1, 0xD2C3, 0xD2C4, 0xD2C5, 0xD2C6, 0xD2C7, 0xD2CA, 0xD2CC, 0xD2CD, 0xD2CE, 0xD2CF, 0xD2D0, 0xD2D1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD2D2, 0xD2D3, 0xD2D5, 0xD2D6, 0xD2D7, 0xD2D9, 0xD2DA, 0xD2DB, 0xD2DD, 0xD2DE, 0xD2DF, 0xD2E0, 0xD2E1, 0xD2E2, 0xD2E3, 0xD2E6, 0xD2E7, 0xD2E8, 0xD2E9, 0xD2EA, 0xD2EB, 0xD2EC, 0xD2ED, 0xD2EE, 0xD2EF, 0xD2F2, 0xD2F3, 0xD2F5, 0xD2F6, 0xD2F7, 0xD2F9, 0xD2FA, 0xBC99, 0xBC9A, 0xBCA0, 0xBCA1, 0xBCA4, 0xBCA7, 0xBCA8, 0xBCB0, 0xBCB1, 0xBCB3, 0xBCB4, 0xBCB5, 0xBCBC, 0xBCBD, 0xBCC0, 0xBCC4, 0xBCCD, 0xBCCF, 0xBCD0, 0xBCD1, 0xBCD5, 0xBCD8, 0xBCDC, 0xBCF4, 0xBCF5, 0xBCF6, 0xBCF8, 0xBCFC, 0xBD04, 0xBD05, 0xBD07, 0xBD09, 0xBD10, 0xBD14, 0xBD24, 0xBD2C, 0xBD40, 0xBD48, 0xBD49, 0xBD4C, 0xBD50, 0xBD58, 0xBD59, 0xBD64, 0xBD68, 0xBD80, 0xBD81, 0xBD84, 0xBD87, 0xBD88, 0xBD89, 0xBD8A, 0xBD90, 0xBD91, 0xBD93, 0xBD95, 0xBD99, 0xBD9A, 0xBD9C, 0xBDA4, 0xBDB0, 0xBDB8, 0xBDD4, 0xBDD5, 0xBDD8, 0xBDDC, 0xBDE9, 0xBDF0, 0xBDF4, 0xBDF8, 0xBE00, 0xBE03, 0xBE05, 0xBE0C, 0xBE0D, 0xBE10, 0xBE14, 0xBE1C, 0xBE1D, 0xBE1F, 0xBE44, 0xBE45, 0xBE48, 0xBE4C, 0xBE4E, 0xBE54, 0xBE55, 0xBE57, 0xBE59, 0xBE5A, 0xBE5B, 0xBE60, 0xBE61, 0xBE64, + 0xD2FB, 0xD2FC, 0xD2FD, 0xD2FE, 0xD2FF, 0xD302, 0xD304, 0xD306, 0xD307, 0xD308, 0xD309, 0xD30A, 0xD30B, 0xD30F, 0xD311, 0xD312, 0xD313, 0xD315, 0xD317, 0xD318, 0xD319, 0xD31A, 0xD31B, 0xD31E, 0xD322, 0xD323, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD324, 0xD326, 0xD327, 0xD32A, 0xD32B, 0xD32D, 0xD32E, 0xD32F, 0xD331, 0xD332, 0xD333, 0xD334, 0xD335, 0xD336, 0xD337, 0xD33A, 0xD33E, 0xD33F, 0xD340, 0xD341, 0xD342, 0xD343, 0xD346, 0xD347, 0xD348, 0xD349, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD34A, 0xD34B, 0xD34C, 0xD34D, 0xD34E, 0xD34F, 0xD350, 0xD351, 0xD352, 0xD353, 0xD354, 0xD355, 0xD356, 0xD357, 0xD358, 0xD359, 0xD35A, 0xD35B, 0xD35C, 0xD35D, 0xD35E, 0xD35F, 0xD360, 0xD361, 0xD362, 0xD363, 0xD364, 0xD365, 0xD366, 0xD367, 0xD368, 0xD369, 0xBE68, 0xBE6A, 0xBE70, 0xBE71, 0xBE73, 0xBE74, 0xBE75, 0xBE7B, 0xBE7C, 0xBE7D, 0xBE80, 0xBE84, 0xBE8C, 0xBE8D, 0xBE8F, 0xBE90, 0xBE91, 0xBE98, 0xBE99, 0xBEA8, 0xBED0, 0xBED1, 0xBED4, 0xBED7, 0xBED8, 0xBEE0, 0xBEE3, 0xBEE4, 0xBEE5, 0xBEEC, 0xBF01, 0xBF08, 0xBF09, 0xBF18, 0xBF19, 0xBF1B, 0xBF1C, 0xBF1D, 0xBF40, 0xBF41, 0xBF44, 0xBF48, 0xBF50, 0xBF51, 0xBF55, 0xBF94, 0xBFB0, 0xBFC5, 0xBFCC, 0xBFCD, 0xBFD0, 0xBFD4, 0xBFDC, 0xBFDF, 0xBFE1, 0xC03C, 0xC051, 0xC058, 0xC05C, 0xC060, 0xC068, 0xC069, 0xC090, 0xC091, 0xC094, 0xC098, 0xC0A0, 0xC0A1, 0xC0A3, 0xC0A5, 0xC0AC, 0xC0AD, 0xC0AF, 0xC0B0, 0xC0B3, 0xC0B4, 0xC0B5, 0xC0B6, 0xC0BC, 0xC0BD, 0xC0BF, 0xC0C0, 0xC0C1, 0xC0C5, 0xC0C8, 0xC0C9, 0xC0CC, 0xC0D0, 0xC0D8, 0xC0D9, 0xC0DB, 0xC0DC, 0xC0DD, 0xC0E4, + 0xD36A, 0xD36B, 0xD36C, 0xD36D, 0xD36E, 0xD36F, 0xD370, 0xD371, 0xD372, 0xD373, 0xD374, 0xD375, 0xD376, 0xD377, 0xD378, 0xD379, 0xD37A, 0xD37B, 0xD37E, 0xD37F, 0xD381, 0xD382, 0xD383, 0xD385, 0xD386, 0xD387, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD388, 0xD389, 0xD38A, 0xD38B, 0xD38E, 0xD392, 0xD393, 0xD394, 0xD395, 0xD396, 0xD397, 0xD39A, 0xD39B, 0xD39D, 0xD39E, 0xD39F, 0xD3A1, 0xD3A2, 0xD3A3, 0xD3A4, 0xD3A5, 0xD3A6, 0xD3A7, 0xD3AA, 0xD3AC, 0xD3AE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD3AF, 0xD3B0, 0xD3B1, 0xD3B2, 0xD3B3, 0xD3B5, 0xD3B6, 0xD3B7, 0xD3B9, 0xD3BA, 0xD3BB, 0xD3BD, 0xD3BE, 0xD3BF, 0xD3C0, 0xD3C1, 0xD3C2, 0xD3C3, 0xD3C6, 0xD3C7, 0xD3CA, 0xD3CB, 0xD3CC, 0xD3CD, 0xD3CE, 0xD3CF, 0xD3D1, 0xD3D2, 0xD3D3, 0xD3D4, 0xD3D5, 0xD3D6, 0xC0E5, 0xC0E8, 0xC0EC, 0xC0F4, 0xC0F5, 0xC0F7, 0xC0F9, 0xC100, 0xC104, 0xC108, 0xC110, 0xC115, 0xC11C, 0xC11D, 0xC11E, 0xC11F, 0xC120, 0xC123, 0xC124, 0xC126, 0xC127, 0xC12C, 0xC12D, 0xC12F, 0xC130, 0xC131, 0xC136, 0xC138, 0xC139, 0xC13C, 0xC140, 0xC148, 0xC149, 0xC14B, 0xC14C, 0xC14D, 0xC154, 0xC155, 0xC158, 0xC15C, 0xC164, 0xC165, 0xC167, 0xC168, 0xC169, 0xC170, 0xC174, 0xC178, 0xC185, 0xC18C, 0xC18D, 0xC18E, 0xC190, 0xC194, 0xC196, 0xC19C, 0xC19D, 0xC19F, 0xC1A1, 0xC1A5, 0xC1A8, 0xC1A9, 0xC1AC, 0xC1B0, 0xC1BD, 0xC1C4, 0xC1C8, 0xC1CC, 0xC1D4, 0xC1D7, 0xC1D8, 0xC1E0, 0xC1E4, 0xC1E8, 0xC1F0, 0xC1F1, 0xC1F3, 0xC1FC, 0xC1FD, 0xC200, 0xC204, 0xC20C, 0xC20D, 0xC20F, 0xC211, 0xC218, 0xC219, 0xC21C, 0xC21F, 0xC220, 0xC228, 0xC229, 0xC22B, 0xC22D, + 0xD3D7, 0xD3D9, 0xD3DA, 0xD3DB, 0xD3DC, 0xD3DD, 0xD3DE, 0xD3DF, 0xD3E0, 0xD3E2, 0xD3E4, 0xD3E5, 0xD3E6, 0xD3E7, 0xD3E8, 0xD3E9, 0xD3EA, 0xD3EB, 0xD3EE, 0xD3EF, 0xD3F1, 0xD3F2, 0xD3F3, 0xD3F5, 0xD3F6, 0xD3F7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD3F8, 0xD3F9, 0xD3FA, 0xD3FB, 0xD3FE, 0xD400, 0xD402, 0xD403, 0xD404, 0xD405, 0xD406, 0xD407, 0xD409, 0xD40A, 0xD40B, 0xD40C, 0xD40D, 0xD40E, 0xD40F, 0xD410, 0xD411, 0xD412, 0xD413, 0xD414, 0xD415, 0xD416, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD417, 0xD418, 0xD419, 0xD41A, 0xD41B, 0xD41C, 0xD41E, 0xD41F, 0xD420, 0xD421, 0xD422, 0xD423, 0xD424, 0xD425, 0xD426, 0xD427, 0xD428, 0xD429, 0xD42A, 0xD42B, 0xD42C, 0xD42D, 0xD42E, 0xD42F, 0xD430, 0xD431, 0xD432, 0xD433, 0xD434, 0xD435, 0xD436, 0xD437, 0xC22F, 0xC231, 0xC232, 0xC234, 0xC248, 0xC250, 0xC251, 0xC254, 0xC258, 0xC260, 0xC265, 0xC26C, 0xC26D, 0xC270, 0xC274, 0xC27C, 0xC27D, 0xC27F, 0xC281, 0xC288, 0xC289, 0xC290, 0xC298, 0xC29B, 0xC29D, 0xC2A4, 0xC2A5, 0xC2A8, 0xC2AC, 0xC2AD, 0xC2B4, 0xC2B5, 0xC2B7, 0xC2B9, 0xC2DC, 0xC2DD, 0xC2E0, 0xC2E3, 0xC2E4, 0xC2EB, 0xC2EC, 0xC2ED, 0xC2EF, 0xC2F1, 0xC2F6, 0xC2F8, 0xC2F9, 0xC2FB, 0xC2FC, 0xC300, 0xC308, 0xC309, 0xC30C, 0xC30D, 0xC313, 0xC314, 0xC315, 0xC318, 0xC31C, 0xC324, 0xC325, 0xC328, 0xC329, 0xC345, 0xC368, 0xC369, 0xC36C, 0xC370, 0xC372, 0xC378, 0xC379, 0xC37C, 0xC37D, 0xC384, 0xC388, 0xC38C, 0xC3C0, 0xC3D8, 0xC3D9, 0xC3DC, 0xC3DF, 0xC3E0, 0xC3E2, 0xC3E8, 0xC3E9, 0xC3ED, 0xC3F4, 0xC3F5, 0xC3F8, 0xC408, 0xC410, 0xC424, 0xC42C, 0xC430, + 0xD438, 0xD439, 0xD43A, 0xD43B, 0xD43C, 0xD43D, 0xD43E, 0xD43F, 0xD441, 0xD442, 0xD443, 0xD445, 0xD446, 0xD447, 0xD448, 0xD449, 0xD44A, 0xD44B, 0xD44C, 0xD44D, 0xD44E, 0xD44F, 0xD450, 0xD451, 0xD452, 0xD453, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD454, 0xD455, 0xD456, 0xD457, 0xD458, 0xD459, 0xD45A, 0xD45B, 0xD45D, 0xD45E, 0xD45F, 0xD461, 0xD462, 0xD463, 0xD465, 0xD466, 0xD467, 0xD468, 0xD469, 0xD46A, 0xD46B, 0xD46C, 0xD46E, 0xD470, 0xD471, 0xD472, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD473, 0xD474, 0xD475, 0xD476, 0xD477, 0xD47A, 0xD47B, 0xD47D, 0xD47E, 0xD481, 0xD483, 0xD484, 0xD485, 0xD486, 0xD487, 0xD48A, 0xD48C, 0xD48E, 0xD48F, 0xD490, 0xD491, 0xD492, 0xD493, 0xD495, 0xD496, 0xD497, 0xD498, 0xD499, 0xD49A, 0xD49B, 0xD49C, 0xD49D, 0xC434, 0xC43C, 0xC43D, 0xC448, 0xC464, 0xC465, 0xC468, 0xC46C, 0xC474, 0xC475, 0xC479, 0xC480, 0xC494, 0xC49C, 0xC4B8, 0xC4BC, 0xC4E9, 0xC4F0, 0xC4F1, 0xC4F4, 0xC4F8, 0xC4FA, 0xC4FF, 0xC500, 0xC501, 0xC50C, 0xC510, 0xC514, 0xC51C, 0xC528, 0xC529, 0xC52C, 0xC530, 0xC538, 0xC539, 0xC53B, 0xC53D, 0xC544, 0xC545, 0xC548, 0xC549, 0xC54A, 0xC54C, 0xC54D, 0xC54E, 0xC553, 0xC554, 0xC555, 0xC557, 0xC558, 0xC559, 0xC55D, 0xC55E, 0xC560, 0xC561, 0xC564, 0xC568, 0xC570, 0xC571, 0xC573, 0xC574, 0xC575, 0xC57C, 0xC57D, 0xC580, 0xC584, 0xC587, 0xC58C, 0xC58D, 0xC58F, 0xC591, 0xC595, 0xC597, 0xC598, 0xC59C, 0xC5A0, 0xC5A9, 0xC5B4, 0xC5B5, 0xC5B8, 0xC5B9, 0xC5BB, 0xC5BC, 0xC5BD, 0xC5BE, 0xC5C4, 0xC5C5, 0xC5C6, 0xC5C7, 0xC5C8, 0xC5C9, 0xC5CA, 0xC5CC, 0xC5CE, + 0xD49E, 0xD49F, 0xD4A0, 0xD4A1, 0xD4A2, 0xD4A3, 0xD4A4, 0xD4A5, 0xD4A6, 0xD4A7, 0xD4A8, 0xD4AA, 0xD4AB, 0xD4AC, 0xD4AD, 0xD4AE, 0xD4AF, 0xD4B0, 0xD4B1, 0xD4B2, 0xD4B3, 0xD4B4, 0xD4B5, 0xD4B6, 0xD4B7, 0xD4B8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD4B9, 0xD4BA, 0xD4BB, 0xD4BC, 0xD4BD, 0xD4BE, 0xD4BF, 0xD4C0, 0xD4C1, 0xD4C2, 0xD4C3, 0xD4C4, 0xD4C5, 0xD4C6, 0xD4C7, 0xD4C8, 0xD4C9, 0xD4CA, 0xD4CB, 0xD4CD, 0xD4CE, 0xD4CF, 0xD4D1, 0xD4D2, 0xD4D3, 0xD4D5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD4D6, 0xD4D7, 0xD4D8, 0xD4D9, 0xD4DA, 0xD4DB, 0xD4DD, 0xD4DE, 0xD4E0, 0xD4E1, 0xD4E2, 0xD4E3, 0xD4E4, 0xD4E5, 0xD4E6, 0xD4E7, 0xD4E9, 0xD4EA, 0xD4EB, 0xD4ED, 0xD4EE, 0xD4EF, 0xD4F1, 0xD4F2, 0xD4F3, 0xD4F4, 0xD4F5, 0xD4F6, 0xD4F7, 0xD4F9, 0xD4FA, 0xD4FC, 0xC5D0, 0xC5D1, 0xC5D4, 0xC5D8, 0xC5E0, 0xC5E1, 0xC5E3, 0xC5E5, 0xC5EC, 0xC5ED, 0xC5EE, 0xC5F0, 0xC5F4, 0xC5F6, 0xC5F7, 0xC5FC, 0xC5FD, 0xC5FE, 0xC5FF, 0xC600, 0xC601, 0xC605, 0xC606, 0xC607, 0xC608, 0xC60C, 0xC610, 0xC618, 0xC619, 0xC61B, 0xC61C, 0xC624, 0xC625, 0xC628, 0xC62C, 0xC62D, 0xC62E, 0xC630, 0xC633, 0xC634, 0xC635, 0xC637, 0xC639, 0xC63B, 0xC640, 0xC641, 0xC644, 0xC648, 0xC650, 0xC651, 0xC653, 0xC654, 0xC655, 0xC65C, 0xC65D, 0xC660, 0xC66C, 0xC66F, 0xC671, 0xC678, 0xC679, 0xC67C, 0xC680, 0xC688, 0xC689, 0xC68B, 0xC68D, 0xC694, 0xC695, 0xC698, 0xC69C, 0xC6A4, 0xC6A5, 0xC6A7, 0xC6A9, 0xC6B0, 0xC6B1, 0xC6B4, 0xC6B8, 0xC6B9, 0xC6BA, 0xC6C0, 0xC6C1, 0xC6C3, 0xC6C5, 0xC6CC, 0xC6CD, 0xC6D0, 0xC6D4, 0xC6DC, 0xC6DD, 0xC6E0, 0xC6E1, 0xC6E8, + 0xD4FE, 0xD4FF, 0xD500, 0xD501, 0xD502, 0xD503, 0xD505, 0xD506, 0xD507, 0xD509, 0xD50A, 0xD50B, 0xD50D, 0xD50E, 0xD50F, 0xD510, 0xD511, 0xD512, 0xD513, 0xD516, 0xD518, 0xD519, 0xD51A, 0xD51B, 0xD51C, 0xD51D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD51E, 0xD51F, 0xD520, 0xD521, 0xD522, 0xD523, 0xD524, 0xD525, 0xD526, 0xD527, 0xD528, 0xD529, 0xD52A, 0xD52B, 0xD52C, 0xD52D, 0xD52E, 0xD52F, 0xD530, 0xD531, 0xD532, 0xD533, 0xD534, 0xD535, 0xD536, 0xD537, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD538, 0xD539, 0xD53A, 0xD53B, 0xD53E, 0xD53F, 0xD541, 0xD542, 0xD543, 0xD545, 0xD546, 0xD547, 0xD548, 0xD549, 0xD54A, 0xD54B, 0xD54E, 0xD550, 0xD552, 0xD553, 0xD554, 0xD555, 0xD556, 0xD557, 0xD55A, 0xD55B, 0xD55D, 0xD55E, 0xD55F, 0xD561, 0xD562, 0xD563, 0xC6E9, 0xC6EC, 0xC6F0, 0xC6F8, 0xC6F9, 0xC6FD, 0xC704, 0xC705, 0xC708, 0xC70C, 0xC714, 0xC715, 0xC717, 0xC719, 0xC720, 0xC721, 0xC724, 0xC728, 0xC730, 0xC731, 0xC733, 0xC735, 0xC737, 0xC73C, 0xC73D, 0xC740, 0xC744, 0xC74A, 0xC74C, 0xC74D, 0xC74F, 0xC751, 0xC752, 0xC753, 0xC754, 0xC755, 0xC756, 0xC757, 0xC758, 0xC75C, 0xC760, 0xC768, 0xC76B, 0xC774, 0xC775, 0xC778, 0xC77C, 0xC77D, 0xC77E, 0xC783, 0xC784, 0xC785, 0xC787, 0xC788, 0xC789, 0xC78A, 0xC78E, 0xC790, 0xC791, 0xC794, 0xC796, 0xC797, 0xC798, 0xC79A, 0xC7A0, 0xC7A1, 0xC7A3, 0xC7A4, 0xC7A5, 0xC7A6, 0xC7AC, 0xC7AD, 0xC7B0, 0xC7B4, 0xC7BC, 0xC7BD, 0xC7BF, 0xC7C0, 0xC7C1, 0xC7C8, 0xC7C9, 0xC7CC, 0xC7CE, 0xC7D0, 0xC7D8, 0xC7DD, 0xC7E4, 0xC7E8, 0xC7EC, 0xC800, 0xC801, 0xC804, 0xC808, 0xC80A, + 0xD564, 0xD566, 0xD567, 0xD56A, 0xD56C, 0xD56E, 0xD56F, 0xD570, 0xD571, 0xD572, 0xD573, 0xD576, 0xD577, 0xD579, 0xD57A, 0xD57B, 0xD57D, 0xD57E, 0xD57F, 0xD580, 0xD581, 0xD582, 0xD583, 0xD586, 0xD58A, 0xD58B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD58C, 0xD58D, 0xD58E, 0xD58F, 0xD591, 0xD592, 0xD593, 0xD594, 0xD595, 0xD596, 0xD597, 0xD598, 0xD599, 0xD59A, 0xD59B, 0xD59C, 0xD59D, 0xD59E, 0xD59F, 0xD5A0, 0xD5A1, 0xD5A2, 0xD5A3, 0xD5A4, 0xD5A6, 0xD5A7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD5A8, 0xD5A9, 0xD5AA, 0xD5AB, 0xD5AC, 0xD5AD, 0xD5AE, 0xD5AF, 0xD5B0, 0xD5B1, 0xD5B2, 0xD5B3, 0xD5B4, 0xD5B5, 0xD5B6, 0xD5B7, 0xD5B8, 0xD5B9, 0xD5BA, 0xD5BB, 0xD5BC, 0xD5BD, 0xD5BE, 0xD5BF, 0xD5C0, 0xD5C1, 0xD5C2, 0xD5C3, 0xD5C4, 0xD5C5, 0xD5C6, 0xD5C7, 0xC810, 0xC811, 0xC813, 0xC815, 0xC816, 0xC81C, 0xC81D, 0xC820, 0xC824, 0xC82C, 0xC82D, 0xC82F, 0xC831, 0xC838, 0xC83C, 0xC840, 0xC848, 0xC849, 0xC84C, 0xC84D, 0xC854, 0xC870, 0xC871, 0xC874, 0xC878, 0xC87A, 0xC880, 0xC881, 0xC883, 0xC885, 0xC886, 0xC887, 0xC88B, 0xC88C, 0xC88D, 0xC894, 0xC89D, 0xC89F, 0xC8A1, 0xC8A8, 0xC8BC, 0xC8BD, 0xC8C4, 0xC8C8, 0xC8CC, 0xC8D4, 0xC8D5, 0xC8D7, 0xC8D9, 0xC8E0, 0xC8E1, 0xC8E4, 0xC8F5, 0xC8FC, 0xC8FD, 0xC900, 0xC904, 0xC905, 0xC906, 0xC90C, 0xC90D, 0xC90F, 0xC911, 0xC918, 0xC92C, 0xC934, 0xC950, 0xC951, 0xC954, 0xC958, 0xC960, 0xC961, 0xC963, 0xC96C, 0xC970, 0xC974, 0xC97C, 0xC988, 0xC989, 0xC98C, 0xC990, 0xC998, 0xC999, 0xC99B, 0xC99D, 0xC9C0, 0xC9C1, 0xC9C4, 0xC9C7, 0xC9C8, 0xC9CA, 0xC9D0, 0xC9D1, 0xC9D3, + 0xD5CA, 0xD5CB, 0xD5CD, 0xD5CE, 0xD5CF, 0xD5D1, 0xD5D3, 0xD5D4, 0xD5D5, 0xD5D6, 0xD5D7, 0xD5DA, 0xD5DC, 0xD5DE, 0xD5DF, 0xD5E0, 0xD5E1, 0xD5E2, 0xD5E3, 0xD5E6, 0xD5E7, 0xD5E9, 0xD5EA, 0xD5EB, 0xD5ED, 0xD5EE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD5EF, 0xD5F0, 0xD5F1, 0xD5F2, 0xD5F3, 0xD5F6, 0xD5F8, 0xD5FA, 0xD5FB, 0xD5FC, 0xD5FD, 0xD5FE, 0xD5FF, 0xD602, 0xD603, 0xD605, 0xD606, 0xD607, 0xD609, 0xD60A, 0xD60B, 0xD60C, 0xD60D, 0xD60E, 0xD60F, 0xD612, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD616, 0xD617, 0xD618, 0xD619, 0xD61A, 0xD61B, 0xD61D, 0xD61E, 0xD61F, 0xD621, 0xD622, 0xD623, 0xD625, 0xD626, 0xD627, 0xD628, 0xD629, 0xD62A, 0xD62B, 0xD62C, 0xD62E, 0xD62F, 0xD630, 0xD631, 0xD632, 0xD633, 0xD634, 0xD635, 0xD636, 0xD637, 0xD63A, 0xD63B, 0xC9D5, 0xC9D6, 0xC9D9, 0xC9DA, 0xC9DC, 0xC9DD, 0xC9E0, 0xC9E2, 0xC9E4, 0xC9E7, 0xC9EC, 0xC9ED, 0xC9EF, 0xC9F0, 0xC9F1, 0xC9F8, 0xC9F9, 0xC9FC, 0xCA00, 0xCA08, 0xCA09, 0xCA0B, 0xCA0C, 0xCA0D, 0xCA14, 0xCA18, 0xCA29, 0xCA4C, 0xCA4D, 0xCA50, 0xCA54, 0xCA5C, 0xCA5D, 0xCA5F, 0xCA60, 0xCA61, 0xCA68, 0xCA7D, 0xCA84, 0xCA98, 0xCABC, 0xCABD, 0xCAC0, 0xCAC4, 0xCACC, 0xCACD, 0xCACF, 0xCAD1, 0xCAD3, 0xCAD8, 0xCAD9, 0xCAE0, 0xCAEC, 0xCAF4, 0xCB08, 0xCB10, 0xCB14, 0xCB18, 0xCB20, 0xCB21, 0xCB41, 0xCB48, 0xCB49, 0xCB4C, 0xCB50, 0xCB58, 0xCB59, 0xCB5D, 0xCB64, 0xCB78, 0xCB79, 0xCB9C, 0xCBB8, 0xCBD4, 0xCBE4, 0xCBE7, 0xCBE9, 0xCC0C, 0xCC0D, 0xCC10, 0xCC14, 0xCC1C, 0xCC1D, 0xCC21, 0xCC22, 0xCC27, 0xCC28, 0xCC29, 0xCC2C, 0xCC2E, 0xCC30, 0xCC38, 0xCC39, 0xCC3B, + 0xD63D, 0xD63E, 0xD63F, 0xD641, 0xD642, 0xD643, 0xD644, 0xD646, 0xD647, 0xD64A, 0xD64C, 0xD64E, 0xD64F, 0xD650, 0xD652, 0xD653, 0xD656, 0xD657, 0xD659, 0xD65A, 0xD65B, 0xD65D, 0xD65E, 0xD65F, 0xD660, 0xD661, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD662, 0xD663, 0xD664, 0xD665, 0xD666, 0xD668, 0xD66A, 0xD66B, 0xD66C, 0xD66D, 0xD66E, 0xD66F, 0xD672, 0xD673, 0xD675, 0xD676, 0xD677, 0xD678, 0xD679, 0xD67A, 0xD67B, 0xD67C, 0xD67D, 0xD67E, 0xD67F, 0xD680, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD681, 0xD682, 0xD684, 0xD686, 0xD687, 0xD688, 0xD689, 0xD68A, 0xD68B, 0xD68E, 0xD68F, 0xD691, 0xD692, 0xD693, 0xD695, 0xD696, 0xD697, 0xD698, 0xD699, 0xD69A, 0xD69B, 0xD69C, 0xD69E, 0xD6A0, 0xD6A2, 0xD6A3, 0xD6A4, 0xD6A5, 0xD6A6, 0xD6A7, 0xD6A9, 0xD6AA, 0xCC3C, 0xCC3D, 0xCC3E, 0xCC44, 0xCC45, 0xCC48, 0xCC4C, 0xCC54, 0xCC55, 0xCC57, 0xCC58, 0xCC59, 0xCC60, 0xCC64, 0xCC66, 0xCC68, 0xCC70, 0xCC75, 0xCC98, 0xCC99, 0xCC9C, 0xCCA0, 0xCCA8, 0xCCA9, 0xCCAB, 0xCCAC, 0xCCAD, 0xCCB4, 0xCCB5, 0xCCB8, 0xCCBC, 0xCCC4, 0xCCC5, 0xCCC7, 0xCCC9, 0xCCD0, 0xCCD4, 0xCCE4, 0xCCEC, 0xCCF0, 0xCD01, 0xCD08, 0xCD09, 0xCD0C, 0xCD10, 0xCD18, 0xCD19, 0xCD1B, 0xCD1D, 0xCD24, 0xCD28, 0xCD2C, 0xCD39, 0xCD5C, 0xCD60, 0xCD64, 0xCD6C, 0xCD6D, 0xCD6F, 0xCD71, 0xCD78, 0xCD88, 0xCD94, 0xCD95, 0xCD98, 0xCD9C, 0xCDA4, 0xCDA5, 0xCDA7, 0xCDA9, 0xCDB0, 0xCDC4, 0xCDCC, 0xCDD0, 0xCDE8, 0xCDEC, 0xCDF0, 0xCDF8, 0xCDF9, 0xCDFB, 0xCDFD, 0xCE04, 0xCE08, 0xCE0C, 0xCE14, 0xCE19, 0xCE20, 0xCE21, 0xCE24, 0xCE28, 0xCE30, 0xCE31, 0xCE33, 0xCE35, + 0xD6AB, 0xD6AD, 0xD6AE, 0xD6AF, 0xD6B1, 0xD6B2, 0xD6B3, 0xD6B4, 0xD6B5, 0xD6B6, 0xD6B7, 0xD6B8, 0xD6BA, 0xD6BC, 0xD6BD, 0xD6BE, 0xD6BF, 0xD6C0, 0xD6C1, 0xD6C2, 0xD6C3, 0xD6C6, 0xD6C7, 0xD6C9, 0xD6CA, 0xD6CB, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD6CD, 0xD6CE, 0xD6CF, 0xD6D0, 0xD6D2, 0xD6D3, 0xD6D5, 0xD6D6, 0xD6D8, 0xD6DA, 0xD6DB, 0xD6DC, 0xD6DD, 0xD6DE, 0xD6DF, 0xD6E1, 0xD6E2, 0xD6E3, 0xD6E5, 0xD6E6, 0xD6E7, 0xD6E9, 0xD6EA, 0xD6EB, 0xD6EC, 0xD6ED, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD6EE, 0xD6EF, 0xD6F1, 0xD6F2, 0xD6F3, 0xD6F4, 0xD6F6, 0xD6F7, 0xD6F8, 0xD6F9, 0xD6FA, 0xD6FB, 0xD6FE, 0xD6FF, 0xD701, 0xD702, 0xD703, 0xD705, 0xD706, 0xD707, 0xD708, 0xD709, 0xD70A, 0xD70B, 0xD70C, 0xD70D, 0xD70E, 0xD70F, 0xD710, 0xD712, 0xD713, 0xD714, 0xCE58, 0xCE59, 0xCE5C, 0xCE5F, 0xCE60, 0xCE61, 0xCE68, 0xCE69, 0xCE6B, 0xCE6D, 0xCE74, 0xCE75, 0xCE78, 0xCE7C, 0xCE84, 0xCE85, 0xCE87, 0xCE89, 0xCE90, 0xCE91, 0xCE94, 0xCE98, 0xCEA0, 0xCEA1, 0xCEA3, 0xCEA4, 0xCEA5, 0xCEAC, 0xCEAD, 0xCEC1, 0xCEE4, 0xCEE5, 0xCEE8, 0xCEEB, 0xCEEC, 0xCEF4, 0xCEF5, 0xCEF7, 0xCEF8, 0xCEF9, 0xCF00, 0xCF01, 0xCF04, 0xCF08, 0xCF10, 0xCF11, 0xCF13, 0xCF15, 0xCF1C, 0xCF20, 0xCF24, 0xCF2C, 0xCF2D, 0xCF2F, 0xCF30, 0xCF31, 0xCF38, 0xCF54, 0xCF55, 0xCF58, 0xCF5C, 0xCF64, 0xCF65, 0xCF67, 0xCF69, 0xCF70, 0xCF71, 0xCF74, 0xCF78, 0xCF80, 0xCF85, 0xCF8C, 0xCFA1, 0xCFA8, 0xCFB0, 0xCFC4, 0xCFE0, 0xCFE1, 0xCFE4, 0xCFE8, 0xCFF0, 0xCFF1, 0xCFF3, 0xCFF5, 0xCFFC, 0xD000, 0xD004, 0xD011, 0xD018, 0xD02D, 0xD034, 0xD035, 0xD038, 0xD03C, + 0xD715, 0xD716, 0xD717, 0xD71A, 0xD71B, 0xD71D, 0xD71E, 0xD71F, 0xD721, 0xD722, 0xD723, 0xD724, 0xD725, 0xD726, 0xD727, 0xD72A, 0xD72C, 0xD72E, 0xD72F, 0xD730, 0xD731, 0xD732, 0xD733, 0xD736, 0xD737, 0xD739, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD73A, 0xD73B, 0xD73D, 0xD73E, 0xD73F, 0xD740, 0xD741, 0xD742, 0xD743, 0xD745, 0xD746, 0xD748, 0xD74A, 0xD74B, 0xD74C, 0xD74D, 0xD74E, 0xD74F, 0xD752, 0xD753, 0xD755, 0xD75A, 0xD75B, 0xD75C, 0xD75D, 0xD75E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD75F, 0xD762, 0xD764, 0xD766, 0xD767, 0xD768, 0xD76A, 0xD76B, 0xD76D, 0xD76E, 0xD76F, 0xD771, 0xD772, 0xD773, 0xD775, 0xD776, 0xD777, 0xD778, 0xD779, 0xD77A, 0xD77B, 0xD77E, 0xD77F, 0xD780, 0xD782, 0xD783, 0xD784, 0xD785, 0xD786, 0xD787, 0xD78A, 0xD78B, 0xD044, 0xD045, 0xD047, 0xD049, 0xD050, 0xD054, 0xD058, 0xD060, 0xD06C, 0xD06D, 0xD070, 0xD074, 0xD07C, 0xD07D, 0xD081, 0xD0A4, 0xD0A5, 0xD0A8, 0xD0AC, 0xD0B4, 0xD0B5, 0xD0B7, 0xD0B9, 0xD0C0, 0xD0C1, 0xD0C4, 0xD0C8, 0xD0C9, 0xD0D0, 0xD0D1, 0xD0D3, 0xD0D4, 0xD0D5, 0xD0DC, 0xD0DD, 0xD0E0, 0xD0E4, 0xD0EC, 0xD0ED, 0xD0EF, 0xD0F0, 0xD0F1, 0xD0F8, 0xD10D, 0xD130, 0xD131, 0xD134, 0xD138, 0xD13A, 0xD140, 0xD141, 0xD143, 0xD144, 0xD145, 0xD14C, 0xD14D, 0xD150, 0xD154, 0xD15C, 0xD15D, 0xD15F, 0xD161, 0xD168, 0xD16C, 0xD17C, 0xD184, 0xD188, 0xD1A0, 0xD1A1, 0xD1A4, 0xD1A8, 0xD1B0, 0xD1B1, 0xD1B3, 0xD1B5, 0xD1BA, 0xD1BC, 0xD1C0, 0xD1D8, 0xD1F4, 0xD1F8, 0xD207, 0xD209, 0xD210, 0xD22C, 0xD22D, 0xD230, 0xD234, 0xD23C, 0xD23D, 0xD23F, 0xD241, 0xD248, 0xD25C, + 0xD78D, 0xD78E, 0xD78F, 0xD791, 0xD792, 0xD793, 0xD794, 0xD795, 0xD796, 0xD797, 0xD79A, 0xD79C, 0xD79E, 0xD79F, 0xD7A0, 0xD7A1, 0xD7A2, 0xD7A3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD264, 0xD280, 0xD281, 0xD284, 0xD288, 0xD290, 0xD291, 0xD295, 0xD29C, 0xD2A0, 0xD2A4, 0xD2AC, 0xD2B1, 0xD2B8, 0xD2B9, 0xD2BC, 0xD2BF, 0xD2C0, 0xD2C2, 0xD2C8, 0xD2C9, 0xD2CB, 0xD2D4, 0xD2D8, 0xD2DC, 0xD2E4, 0xD2E5, 0xD2F0, 0xD2F1, 0xD2F4, 0xD2F8, 0xD300, 0xD301, 0xD303, 0xD305, 0xD30C, 0xD30D, 0xD30E, 0xD310, 0xD314, 0xD316, 0xD31C, 0xD31D, 0xD31F, 0xD320, 0xD321, 0xD325, 0xD328, 0xD329, 0xD32C, 0xD330, 0xD338, 0xD339, 0xD33B, 0xD33C, 0xD33D, 0xD344, 0xD345, 0xD37C, 0xD37D, 0xD380, 0xD384, 0xD38C, 0xD38D, 0xD38F, 0xD390, 0xD391, 0xD398, 0xD399, 0xD39C, 0xD3A0, 0xD3A8, 0xD3A9, 0xD3AB, 0xD3AD, 0xD3B4, 0xD3B8, 0xD3BC, 0xD3C4, 0xD3C5, 0xD3C8, 0xD3C9, 0xD3D0, 0xD3D8, 0xD3E1, 0xD3E3, 0xD3EC, 0xD3ED, 0xD3F0, 0xD3F4, 0xD3FC, 0xD3FD, 0xD3FF, 0xD401, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD408, 0xD41D, 0xD440, 0xD444, 0xD45C, 0xD460, 0xD464, 0xD46D, 0xD46F, 0xD478, 0xD479, 0xD47C, 0xD47F, 0xD480, 0xD482, 0xD488, 0xD489, 0xD48B, 0xD48D, 0xD494, 0xD4A9, 0xD4CC, 0xD4D0, 0xD4D4, 0xD4DC, 0xD4DF, 0xD4E8, 0xD4EC, 0xD4F0, 0xD4F8, 0xD4FB, 0xD4FD, 0xD504, 0xD508, 0xD50C, 0xD514, 0xD515, 0xD517, 0xD53C, 0xD53D, 0xD540, 0xD544, 0xD54C, 0xD54D, 0xD54F, 0xD551, 0xD558, 0xD559, 0xD55C, 0xD560, 0xD565, 0xD568, 0xD569, 0xD56B, 0xD56D, 0xD574, 0xD575, 0xD578, 0xD57C, 0xD584, 0xD585, 0xD587, 0xD588, 0xD589, 0xD590, 0xD5A5, 0xD5C8, 0xD5C9, 0xD5CC, 0xD5D0, 0xD5D2, 0xD5D8, 0xD5D9, 0xD5DB, 0xD5DD, 0xD5E4, 0xD5E5, 0xD5E8, 0xD5EC, 0xD5F4, 0xD5F5, 0xD5F7, 0xD5F9, 0xD600, 0xD601, 0xD604, 0xD608, 0xD610, 0xD611, 0xD613, 0xD614, 0xD615, 0xD61C, 0xD620, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xD624, 0xD62D, 0xD638, 0xD639, 0xD63C, 0xD640, 0xD645, 0xD648, 0xD649, 0xD64B, 0xD64D, 0xD651, 0xD654, 0xD655, 0xD658, 0xD65C, 0xD667, 0xD669, 0xD670, 0xD671, 0xD674, 0xD683, 0xD685, 0xD68C, 0xD68D, 0xD690, 0xD694, 0xD69D, 0xD69F, 0xD6A1, 0xD6A8, 0xD6AC, 0xD6B0, 0xD6B9, 0xD6BB, 0xD6C4, 0xD6C5, 0xD6C8, 0xD6CC, 0xD6D1, 0xD6D4, 0xD6D7, 0xD6D9, 0xD6E0, 0xD6E4, 0xD6E8, 0xD6F0, 0xD6F5, 0xD6FC, 0xD6FD, 0xD700, 0xD704, 0xD711, 0xD718, 0xD719, 0xD71C, 0xD720, 0xD728, 0xD729, 0xD72B, 0xD72D, 0xD734, 0xD735, 0xD738, 0xD73C, 0xD744, 0xD747, 0xD749, 0xD750, 0xD751, 0xD754, 0xD756, 0xD757, 0xD758, 0xD759, 0xD760, 0xD761, 0xD763, 0xD765, 0xD769, 0xD76C, 0xD770, 0xD774, 0xD77C, 0xD77D, 0xD781, 0xD788, 0xD789, 0xD78C, 0xD790, 0xD798, 0xD799, 0xD79B, 0xD79D, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4F3D, 0x4F73, 0x5047, 0x50F9, 0x52A0, 0x53EF, 0x5475, 0x54E5, 0x5609, 0x5AC1, 0x5BB6, 0x6687, 0x67B6, 0x67B7, 0x67EF, 0x6B4C, 0x73C2, 0x75C2, 0x7A3C, 0x82DB, 0x8304, 0x8857, 0x8888, 0x8A36, 0x8CC8, 0x8DCF, 0x8EFB, 0x8FE6, 0x99D5, 0x523B, 0x5374, 0x5404, 0x606A, 0x6164, 0x6BBC, 0x73CF, 0x811A, 0x89BA, 0x89D2, 0x95A3, 0x4F83, 0x520A, 0x58BE, 0x5978, 0x59E6, 0x5E72, 0x5E79, 0x61C7, 0x63C0, 0x6746, 0x67EC, 0x687F, 0x6F97, 0x764E, 0x770B, 0x78F5, 0x7A08, 0x7AFF, 0x7C21, 0x809D, 0x826E, 0x8271, 0x8AEB, 0x9593, 0x4E6B, 0x559D, 0x66F7, 0x6E34, 0x78A3, 0x7AED, 0x845B, 0x8910, 0x874E, 0x97A8, 0x52D8, 0x574E, 0x582A, 0x5D4C, 0x611F, 0x61BE, 0x6221, 0x6562, 0x67D1, 0x6A44, 0x6E1B, 0x7518, 0x75B3, 0x76E3, 0x77B0, 0x7D3A, 0x90AF, 0x9451, 0x9452, 0x9F95, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5323, 0x5CAC, 0x7532, 0x80DB, 0x9240, 0x9598, 0x525B, 0x5808, 0x59DC, 0x5CA1, 0x5D17, 0x5EB7, 0x5F3A, 0x5F4A, 0x6177, 0x6C5F, 0x757A, 0x7586, 0x7CE0, 0x7D73, 0x7DB1, 0x7F8C, 0x8154, 0x8221, 0x8591, 0x8941, 0x8B1B, 0x92FC, 0x964D, 0x9C47, 0x4ECB, 0x4EF7, 0x500B, 0x51F1, 0x584F, 0x6137, 0x613E, 0x6168, 0x6539, 0x69EA, 0x6F11, 0x75A5, 0x7686, 0x76D6, 0x7B87, 0x82A5, 0x84CB, 0xF900, 0x93A7, 0x958B, 0x5580, 0x5BA2, 0x5751, 0xF901, 0x7CB3, 0x7FB9, 0x91B5, 0x5028, 0x53BB, 0x5C45, 0x5DE8, 0x62D2, 0x636E, 0x64DA, 0x64E7, 0x6E20, 0x70AC, 0x795B, 0x8DDD, 0x8E1E, 0xF902, 0x907D, 0x9245, 0x92F8, 0x4E7E, 0x4EF6, 0x5065, 0x5DFE, 0x5EFA, 0x6106, 0x6957, 0x8171, 0x8654, 0x8E47, 0x9375, 0x9A2B, 0x4E5E, 0x5091, 0x6770, 0x6840, 0x5109, 0x528D, 0x5292, 0x6AA2, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x77BC, 0x9210, 0x9ED4, 0x52AB, 0x602F, 0x8FF2, 0x5048, 0x61A9, 0x63ED, 0x64CA, 0x683C, 0x6A84, 0x6FC0, 0x8188, 0x89A1, 0x9694, 0x5805, 0x727D, 0x72AC, 0x7504, 0x7D79, 0x7E6D, 0x80A9, 0x898B, 0x8B74, 0x9063, 0x9D51, 0x6289, 0x6C7A, 0x6F54, 0x7D50, 0x7F3A, 0x8A23, 0x517C, 0x614A, 0x7B9D, 0x8B19, 0x9257, 0x938C, 0x4EAC, 0x4FD3, 0x501E, 0x50BE, 0x5106, 0x52C1, 0x52CD, 0x537F, 0x5770, 0x5883, 0x5E9A, 0x5F91, 0x6176, 0x61AC, 0x64CE, 0x656C, 0x666F, 0x66BB, 0x66F4, 0x6897, 0x6D87, 0x7085, 0x70F1, 0x749F, 0x74A5, 0x74CA, 0x75D9, 0x786C, 0x78EC, 0x7ADF, 0x7AF6, 0x7D45, 0x7D93, 0x8015, 0x803F, 0x811B, 0x8396, 0x8B66, 0x8F15, 0x9015, 0x93E1, 0x9803, 0x9838, 0x9A5A, 0x9BE8, 0x4FC2, 0x5553, 0x583A, 0x5951, 0x5B63, 0x5C46, 0x60B8, 0x6212, 0x6842, 0x68B0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x68E8, 0x6EAA, 0x754C, 0x7678, 0x78CE, 0x7A3D, 0x7CFB, 0x7E6B, 0x7E7C, 0x8A08, 0x8AA1, 0x8C3F, 0x968E, 0x9DC4, 0x53E4, 0x53E9, 0x544A, 0x5471, 0x56FA, 0x59D1, 0x5B64, 0x5C3B, 0x5EAB, 0x62F7, 0x6537, 0x6545, 0x6572, 0x66A0, 0x67AF, 0x69C1, 0x6CBD, 0x75FC, 0x7690, 0x777E, 0x7A3F, 0x7F94, 0x8003, 0x80A1, 0x818F, 0x82E6, 0x82FD, 0x83F0, 0x85C1, 0x8831, 0x88B4, 0x8AA5, 0xF903, 0x8F9C, 0x932E, 0x96C7, 0x9867, 0x9AD8, 0x9F13, 0x54ED, 0x659B, 0x66F2, 0x688F, 0x7A40, 0x8C37, 0x9D60, 0x56F0, 0x5764, 0x5D11, 0x6606, 0x68B1, 0x68CD, 0x6EFE, 0x7428, 0x889E, 0x9BE4, 0x6C68, 0xF904, 0x9AA8, 0x4F9B, 0x516C, 0x5171, 0x529F, 0x5B54, 0x5DE5, 0x6050, 0x606D, 0x62F1, 0x63A7, 0x653B, 0x73D9, 0x7A7A, 0x86A3, 0x8CA2, 0x978F, 0x4E32, 0x5BE1, 0x6208, 0x679C, 0x74DC, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x79D1, 0x83D3, 0x8A87, 0x8AB2, 0x8DE8, 0x904E, 0x934B, 0x9846, 0x5ED3, 0x69E8, 0x85FF, 0x90ED, 0xF905, 0x51A0, 0x5B98, 0x5BEC, 0x6163, 0x68FA, 0x6B3E, 0x704C, 0x742F, 0x74D8, 0x7BA1, 0x7F50, 0x83C5, 0x89C0, 0x8CAB, 0x95DC, 0x9928, 0x522E, 0x605D, 0x62EC, 0x9002, 0x4F8A, 0x5149, 0x5321, 0x58D9, 0x5EE3, 0x66E0, 0x6D38, 0x709A, 0x72C2, 0x73D6, 0x7B50, 0x80F1, 0x945B, 0x5366, 0x639B, 0x7F6B, 0x4E56, 0x5080, 0x584A, 0x58DE, 0x602A, 0x6127, 0x62D0, 0x69D0, 0x9B41, 0x5B8F, 0x7D18, 0x80B1, 0x8F5F, 0x4EA4, 0x50D1, 0x54AC, 0x55AC, 0x5B0C, 0x5DA0, 0x5DE7, 0x652A, 0x654E, 0x6821, 0x6A4B, 0x72E1, 0x768E, 0x77EF, 0x7D5E, 0x7FF9, 0x81A0, 0x854E, 0x86DF, 0x8F03, 0x8F4E, 0x90CA, 0x9903, 0x9A55, 0x9BAB, 0x4E18, 0x4E45, 0x4E5D, 0x4EC7, 0x4FF1, 0x5177, 0x52FE, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5340, 0x53E3, 0x53E5, 0x548E, 0x5614, 0x5775, 0x57A2, 0x5BC7, 0x5D87, 0x5ED0, 0x61FC, 0x62D8, 0x6551, 0x67B8, 0x67E9, 0x69CB, 0x6B50, 0x6BC6, 0x6BEC, 0x6C42, 0x6E9D, 0x7078, 0x72D7, 0x7396, 0x7403, 0x77BF, 0x77E9, 0x7A76, 0x7D7F, 0x8009, 0x81FC, 0x8205, 0x820A, 0x82DF, 0x8862, 0x8B33, 0x8CFC, 0x8EC0, 0x9011, 0x90B1, 0x9264, 0x92B6, 0x99D2, 0x9A45, 0x9CE9, 0x9DD7, 0x9F9C, 0x570B, 0x5C40, 0x83CA, 0x97A0, 0x97AB, 0x9EB4, 0x541B, 0x7A98, 0x7FA4, 0x88D9, 0x8ECD, 0x90E1, 0x5800, 0x5C48, 0x6398, 0x7A9F, 0x5BAE, 0x5F13, 0x7A79, 0x7AAE, 0x828E, 0x8EAC, 0x5026, 0x5238, 0x52F8, 0x5377, 0x5708, 0x62F3, 0x6372, 0x6B0A, 0x6DC3, 0x7737, 0x53A5, 0x7357, 0x8568, 0x8E76, 0x95D5, 0x673A, 0x6AC3, 0x6F70, 0x8A6D, 0x8ECC, 0x994B, 0xF906, 0x6677, 0x6B78, 0x8CB4, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9B3C, 0xF907, 0x53EB, 0x572D, 0x594E, 0x63C6, 0x69FB, 0x73EA, 0x7845, 0x7ABA, 0x7AC5, 0x7CFE, 0x8475, 0x898F, 0x8D73, 0x9035, 0x95A8, 0x52FB, 0x5747, 0x7547, 0x7B60, 0x83CC, 0x921E, 0xF908, 0x6A58, 0x514B, 0x524B, 0x5287, 0x621F, 0x68D8, 0x6975, 0x9699, 0x50C5, 0x52A4, 0x52E4, 0x61C3, 0x65A4, 0x6839, 0x69FF, 0x747E, 0x7B4B, 0x82B9, 0x83EB, 0x89B2, 0x8B39, 0x8FD1, 0x9949, 0xF909, 0x4ECA, 0x5997, 0x64D2, 0x6611, 0x6A8E, 0x7434, 0x7981, 0x79BD, 0x82A9, 0x887E, 0x887F, 0x895F, 0xF90A, 0x9326, 0x4F0B, 0x53CA, 0x6025, 0x6271, 0x6C72, 0x7D1A, 0x7D66, 0x4E98, 0x5162, 0x77DC, 0x80AF, 0x4F01, 0x4F0E, 0x5176, 0x5180, 0x55DC, 0x5668, 0x573B, 0x57FA, 0x57FC, 0x5914, 0x5947, 0x5993, 0x5BC4, 0x5C90, 0x5D0E, 0x5DF1, 0x5E7E, 0x5FCC, 0x6280, 0x65D7, 0x65E3, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x671E, 0x671F, 0x675E, 0x68CB, 0x68C4, 0x6A5F, 0x6B3A, 0x6C23, 0x6C7D, 0x6C82, 0x6DC7, 0x7398, 0x7426, 0x742A, 0x7482, 0x74A3, 0x7578, 0x757F, 0x7881, 0x78EF, 0x7941, 0x7947, 0x7948, 0x797A, 0x7B95, 0x7D00, 0x7DBA, 0x7F88, 0x8006, 0x802D, 0x808C, 0x8A18, 0x8B4F, 0x8C48, 0x8D77, 0x9321, 0x9324, 0x98E2, 0x9951, 0x9A0E, 0x9A0F, 0x9A65, 0x9E92, 0x7DCA, 0x4F76, 0x5409, 0x62EE, 0x6854, 0x91D1, 0x55AB, 0x513A, 0xF90B, 0xF90C, 0x5A1C, 0x61E6, 0xF90D, 0x62CF, 0x62FF, 0xF90E, 0xF90F, 0xF910, 0xF911, 0xF912, 0xF913, 0x90A3, 0xF914, 0xF915, 0xF916, 0xF917, 0xF918, 0x8AFE, 0xF919, 0xF91A, 0xF91B, 0xF91C, 0x6696, 0xF91D, 0x7156, 0xF91E, 0xF91F, 0x96E3, 0xF920, 0x634F, 0x637A, 0x5357, 0xF921, 0x678F, 0x6960, 0x6E73, 0xF922, 0x7537, 0xF923, 0xF924, 0xF925, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7D0D, 0xF926, 0xF927, 0x8872, 0x56CA, 0x5A18, 0xF928, 0xF929, 0xF92A, 0xF92B, 0xF92C, 0x4E43, 0xF92D, 0x5167, 0x5948, 0x67F0, 0x8010, 0xF92E, 0x5973, 0x5E74, 0x649A, 0x79CA, 0x5FF5, 0x606C, 0x62C8, 0x637B, 0x5BE7, 0x5BD7, 0x52AA, 0xF92F, 0x5974, 0x5F29, 0x6012, 0xF930, 0xF931, 0xF932, 0x7459, 0xF933, 0xF934, 0xF935, 0xF936, 0xF937, 0xF938, 0x99D1, 0xF939, 0xF93A, 0xF93B, 0xF93C, 0xF93D, 0xF93E, 0xF93F, 0xF940, 0xF941, 0xF942, 0xF943, 0x6FC3, 0xF944, 0xF945, 0x81BF, 0x8FB2, 0x60F1, 0xF946, 0xF947, 0x8166, 0xF948, 0xF949, 0x5C3F, 0xF94A, 0xF94B, 0xF94C, 0xF94D, 0xF94E, 0xF94F, 0xF950, 0xF951, 0x5AE9, 0x8A25, 0x677B, 0x7D10, 0xF952, 0xF953, 0xF954, 0xF955, 0xF956, 0xF957, 0x80FD, 0xF958, 0xF959, 0x5C3C, 0x6CE5, 0x533F, 0x6EBA, 0x591A, 0x8336, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4E39, 0x4EB6, 0x4F46, 0x55AE, 0x5718, 0x58C7, 0x5F56, 0x65B7, 0x65E6, 0x6A80, 0x6BB5, 0x6E4D, 0x77ED, 0x7AEF, 0x7C1E, 0x7DDE, 0x86CB, 0x8892, 0x9132, 0x935B, 0x64BB, 0x6FBE, 0x737A, 0x75B8, 0x9054, 0x5556, 0x574D, 0x61BA, 0x64D4, 0x66C7, 0x6DE1, 0x6E5B, 0x6F6D, 0x6FB9, 0x75F0, 0x8043, 0x81BD, 0x8541, 0x8983, 0x8AC7, 0x8B5A, 0x931F, 0x6C93, 0x7553, 0x7B54, 0x8E0F, 0x905D, 0x5510, 0x5802, 0x5858, 0x5E62, 0x6207, 0x649E, 0x68E0, 0x7576, 0x7CD6, 0x87B3, 0x9EE8, 0x4EE3, 0x5788, 0x576E, 0x5927, 0x5C0D, 0x5CB1, 0x5E36, 0x5F85, 0x6234, 0x64E1, 0x73B3, 0x81FA, 0x888B, 0x8CB8, 0x968A, 0x9EDB, 0x5B85, 0x5FB7, 0x60B3, 0x5012, 0x5200, 0x5230, 0x5716, 0x5835, 0x5857, 0x5C0E, 0x5C60, 0x5CF6, 0x5D8B, 0x5EA6, 0x5F92, 0x60BC, 0x6311, 0x6389, 0x6417, 0x6843, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x68F9, 0x6AC2, 0x6DD8, 0x6E21, 0x6ED4, 0x6FE4, 0x71FE, 0x76DC, 0x7779, 0x79B1, 0x7A3B, 0x8404, 0x89A9, 0x8CED, 0x8DF3, 0x8E48, 0x9003, 0x9014, 0x9053, 0x90FD, 0x934D, 0x9676, 0x97DC, 0x6BD2, 0x7006, 0x7258, 0x72A2, 0x7368, 0x7763, 0x79BF, 0x7BE4, 0x7E9B, 0x8B80, 0x58A9, 0x60C7, 0x6566, 0x65FD, 0x66BE, 0x6C8C, 0x711E, 0x71C9, 0x8C5A, 0x9813, 0x4E6D, 0x7A81, 0x4EDD, 0x51AC, 0x51CD, 0x52D5, 0x540C, 0x61A7, 0x6771, 0x6850, 0x68DF, 0x6D1E, 0x6F7C, 0x75BC, 0x77B3, 0x7AE5, 0x80F4, 0x8463, 0x9285, 0x515C, 0x6597, 0x675C, 0x6793, 0x75D8, 0x7AC7, 0x8373, 0xF95A, 0x8C46, 0x9017, 0x982D, 0x5C6F, 0x81C0, 0x829A, 0x9041, 0x906F, 0x920D, 0x5F97, 0x5D9D, 0x6A59, 0x71C8, 0x767B, 0x7B49, 0x85E4, 0x8B04, 0x9127, 0x9A30, 0x5587, 0x61F6, 0xF95B, 0x7669, 0x7F85, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x863F, 0x87BA, 0x88F8, 0x908F, 0xF95C, 0x6D1B, 0x70D9, 0x73DE, 0x7D61, 0x843D, 0xF95D, 0x916A, 0x99F1, 0xF95E, 0x4E82, 0x5375, 0x6B04, 0x6B12, 0x703E, 0x721B, 0x862D, 0x9E1E, 0x524C, 0x8FA3, 0x5D50, 0x64E5, 0x652C, 0x6B16, 0x6FEB, 0x7C43, 0x7E9C, 0x85CD, 0x8964, 0x89BD, 0x62C9, 0x81D8, 0x881F, 0x5ECA, 0x6717, 0x6D6A, 0x72FC, 0x7405, 0x746F, 0x8782, 0x90DE, 0x4F86, 0x5D0D, 0x5FA0, 0x840A, 0x51B7, 0x63A0, 0x7565, 0x4EAE, 0x5006, 0x5169, 0x51C9, 0x6881, 0x6A11, 0x7CAE, 0x7CB1, 0x7CE7, 0x826F, 0x8AD2, 0x8F1B, 0x91CF, 0x4FB6, 0x5137, 0x52F5, 0x5442, 0x5EEC, 0x616E, 0x623E, 0x65C5, 0x6ADA, 0x6FFE, 0x792A, 0x85DC, 0x8823, 0x95AD, 0x9A62, 0x9A6A, 0x9E97, 0x9ECE, 0x529B, 0x66C6, 0x6B77, 0x701D, 0x792B, 0x8F62, 0x9742, 0x6190, 0x6200, 0x6523, 0x6F23, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7149, 0x7489, 0x7DF4, 0x806F, 0x84EE, 0x8F26, 0x9023, 0x934A, 0x51BD, 0x5217, 0x52A3, 0x6D0C, 0x70C8, 0x88C2, 0x5EC9, 0x6582, 0x6BAE, 0x6FC2, 0x7C3E, 0x7375, 0x4EE4, 0x4F36, 0x56F9, 0xF95F, 0x5CBA, 0x5DBA, 0x601C, 0x73B2, 0x7B2D, 0x7F9A, 0x7FCE, 0x8046, 0x901E, 0x9234, 0x96F6, 0x9748, 0x9818, 0x9F61, 0x4F8B, 0x6FA7, 0x79AE, 0x91B4, 0x96B7, 0x52DE, 0xF960, 0x6488, 0x64C4, 0x6AD3, 0x6F5E, 0x7018, 0x7210, 0x76E7, 0x8001, 0x8606, 0x865C, 0x8DEF, 0x8F05, 0x9732, 0x9B6F, 0x9DFA, 0x9E75, 0x788C, 0x797F, 0x7DA0, 0x83C9, 0x9304, 0x9E7F, 0x9E93, 0x8AD6, 0x58DF, 0x5F04, 0x6727, 0x7027, 0x74CF, 0x7C60, 0x807E, 0x5121, 0x7028, 0x7262, 0x78CA, 0x8CC2, 0x8CDA, 0x8CF4, 0x96F7, 0x4E86, 0x50DA, 0x5BEE, 0x5ED6, 0x6599, 0x71CE, 0x7642, 0x77AD, 0x804A, 0x84FC, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x907C, 0x9B27, 0x9F8D, 0x58D8, 0x5A41, 0x5C62, 0x6A13, 0x6DDA, 0x6F0F, 0x763B, 0x7D2F, 0x7E37, 0x851E, 0x8938, 0x93E4, 0x964B, 0x5289, 0x65D2, 0x67F3, 0x69B4, 0x6D41, 0x6E9C, 0x700F, 0x7409, 0x7460, 0x7559, 0x7624, 0x786B, 0x8B2C, 0x985E, 0x516D, 0x622E, 0x9678, 0x4F96, 0x502B, 0x5D19, 0x6DEA, 0x7DB8, 0x8F2A, 0x5F8B, 0x6144, 0x6817, 0xF961, 0x9686, 0x52D2, 0x808B, 0x51DC, 0x51CC, 0x695E, 0x7A1C, 0x7DBE, 0x83F1, 0x9675, 0x4FDA, 0x5229, 0x5398, 0x540F, 0x550E, 0x5C65, 0x60A7, 0x674E, 0x68A8, 0x6D6C, 0x7281, 0x72F8, 0x7406, 0x7483, 0xF962, 0x75E2, 0x7C6C, 0x7F79, 0x7FB8, 0x8389, 0x88CF, 0x88E1, 0x91CC, 0x91D0, 0x96E2, 0x9BC9, 0x541D, 0x6F7E, 0x71D0, 0x7498, 0x85FA, 0x8EAA, 0x96A3, 0x9C57, 0x9E9F, 0x6797, 0x6DCB, 0x7433, 0x81E8, 0x9716, 0x782C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7ACB, 0x7B20, 0x7C92, 0x6469, 0x746A, 0x75F2, 0x78BC, 0x78E8, 0x99AC, 0x9B54, 0x9EBB, 0x5BDE, 0x5E55, 0x6F20, 0x819C, 0x83AB, 0x9088, 0x4E07, 0x534D, 0x5A29, 0x5DD2, 0x5F4E, 0x6162, 0x633D, 0x6669, 0x66FC, 0x6EFF, 0x6F2B, 0x7063, 0x779E, 0x842C, 0x8513, 0x883B, 0x8F13, 0x9945, 0x9C3B, 0x551C, 0x62B9, 0x672B, 0x6CAB, 0x8309, 0x896A, 0x977A, 0x4EA1, 0x5984, 0x5FD8, 0x5FD9, 0x671B, 0x7DB2, 0x7F54, 0x8292, 0x832B, 0x83BD, 0x8F1E, 0x9099, 0x57CB, 0x59B9, 0x5A92, 0x5BD0, 0x6627, 0x679A, 0x6885, 0x6BCF, 0x7164, 0x7F75, 0x8CB7, 0x8CE3, 0x9081, 0x9B45, 0x8108, 0x8C8A, 0x964C, 0x9A40, 0x9EA5, 0x5B5F, 0x6C13, 0x731B, 0x76F2, 0x76DF, 0x840C, 0x51AA, 0x8993, 0x514D, 0x5195, 0x52C9, 0x68C9, 0x6C94, 0x7704, 0x7720, 0x7DBF, 0x7DEC, 0x9762, 0x9EB5, 0x6EC5, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8511, 0x51A5, 0x540D, 0x547D, 0x660E, 0x669D, 0x6927, 0x6E9F, 0x76BF, 0x7791, 0x8317, 0x84C2, 0x879F, 0x9169, 0x9298, 0x9CF4, 0x8882, 0x4FAE, 0x5192, 0x52DF, 0x59C6, 0x5E3D, 0x6155, 0x6478, 0x6479, 0x66AE, 0x67D0, 0x6A21, 0x6BCD, 0x6BDB, 0x725F, 0x7261, 0x7441, 0x7738, 0x77DB, 0x8017, 0x82BC, 0x8305, 0x8B00, 0x8B28, 0x8C8C, 0x6728, 0x6C90, 0x7267, 0x76EE, 0x7766, 0x7A46, 0x9DA9, 0x6B7F, 0x6C92, 0x5922, 0x6726, 0x8499, 0x536F, 0x5893, 0x5999, 0x5EDF, 0x63CF, 0x6634, 0x6773, 0x6E3A, 0x732B, 0x7AD7, 0x82D7, 0x9328, 0x52D9, 0x5DEB, 0x61AE, 0x61CB, 0x620A, 0x62C7, 0x64AB, 0x65E0, 0x6959, 0x6B66, 0x6BCB, 0x7121, 0x73F7, 0x755D, 0x7E46, 0x821E, 0x8302, 0x856A, 0x8AA3, 0x8CBF, 0x9727, 0x9D61, 0x58A8, 0x9ED8, 0x5011, 0x520E, 0x543B, 0x554F, 0x6587, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6C76, 0x7D0A, 0x7D0B, 0x805E, 0x868A, 0x9580, 0x96EF, 0x52FF, 0x6C95, 0x7269, 0x5473, 0x5A9A, 0x5C3E, 0x5D4B, 0x5F4C, 0x5FAE, 0x672A, 0x68B6, 0x6963, 0x6E3C, 0x6E44, 0x7709, 0x7C73, 0x7F8E, 0x8587, 0x8B0E, 0x8FF7, 0x9761, 0x9EF4, 0x5CB7, 0x60B6, 0x610D, 0x61AB, 0x654F, 0x65FB, 0x65FC, 0x6C11, 0x6CEF, 0x739F, 0x73C9, 0x7DE1, 0x9594, 0x5BC6, 0x871C, 0x8B10, 0x525D, 0x535A, 0x62CD, 0x640F, 0x64B2, 0x6734, 0x6A38, 0x6CCA, 0x73C0, 0x749E, 0x7B94, 0x7C95, 0x7E1B, 0x818A, 0x8236, 0x8584, 0x8FEB, 0x96F9, 0x99C1, 0x4F34, 0x534A, 0x53CD, 0x53DB, 0x62CC, 0x642C, 0x6500, 0x6591, 0x69C3, 0x6CEE, 0x6F58, 0x73ED, 0x7554, 0x7622, 0x76E4, 0x76FC, 0x78D0, 0x78FB, 0x792C, 0x7D46, 0x822C, 0x87E0, 0x8FD4, 0x9812, 0x98EF, 0x52C3, 0x62D4, 0x64A5, 0x6E24, 0x6F51, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x767C, 0x8DCB, 0x91B1, 0x9262, 0x9AEE, 0x9B43, 0x5023, 0x508D, 0x574A, 0x59A8, 0x5C28, 0x5E47, 0x5F77, 0x623F, 0x653E, 0x65B9, 0x65C1, 0x6609, 0x678B, 0x699C, 0x6EC2, 0x78C5, 0x7D21, 0x80AA, 0x8180, 0x822B, 0x82B3, 0x84A1, 0x868C, 0x8A2A, 0x8B17, 0x90A6, 0x9632, 0x9F90, 0x500D, 0x4FF3, 0xF963, 0x57F9, 0x5F98, 0x62DC, 0x6392, 0x676F, 0x6E43, 0x7119, 0x76C3, 0x80CC, 0x80DA, 0x88F4, 0x88F5, 0x8919, 0x8CE0, 0x8F29, 0x914D, 0x966A, 0x4F2F, 0x4F70, 0x5E1B, 0x67CF, 0x6822, 0x767D, 0x767E, 0x9B44, 0x5E61, 0x6A0A, 0x7169, 0x71D4, 0x756A, 0xF964, 0x7E41, 0x8543, 0x85E9, 0x98DC, 0x4F10, 0x7B4F, 0x7F70, 0x95A5, 0x51E1, 0x5E06, 0x68B5, 0x6C3E, 0x6C4E, 0x6CDB, 0x72AF, 0x7BC4, 0x8303, 0x6CD5, 0x743A, 0x50FB, 0x5288, 0x58C1, 0x64D8, 0x6A97, 0x74A7, 0x7656, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x78A7, 0x8617, 0x95E2, 0x9739, 0xF965, 0x535E, 0x5F01, 0x8B8A, 0x8FA8, 0x8FAF, 0x908A, 0x5225, 0x77A5, 0x9C49, 0x9F08, 0x4E19, 0x5002, 0x5175, 0x5C5B, 0x5E77, 0x661E, 0x663A, 0x67C4, 0x68C5, 0x70B3, 0x7501, 0x75C5, 0x79C9, 0x7ADD, 0x8F27, 0x9920, 0x9A08, 0x4FDD, 0x5821, 0x5831, 0x5BF6, 0x666E, 0x6B65, 0x6D11, 0x6E7A, 0x6F7D, 0x73E4, 0x752B, 0x83E9, 0x88DC, 0x8913, 0x8B5C, 0x8F14, 0x4F0F, 0x50D5, 0x5310, 0x535C, 0x5B93, 0x5FA9, 0x670D, 0x798F, 0x8179, 0x832F, 0x8514, 0x8907, 0x8986, 0x8F39, 0x8F3B, 0x99A5, 0x9C12, 0x672C, 0x4E76, 0x4FF8, 0x5949, 0x5C01, 0x5CEF, 0x5CF0, 0x6367, 0x68D2, 0x70FD, 0x71A2, 0x742B, 0x7E2B, 0x84EC, 0x8702, 0x9022, 0x92D2, 0x9CF3, 0x4E0D, 0x4ED8, 0x4FEF, 0x5085, 0x5256, 0x526F, 0x5426, 0x5490, 0x57E0, 0x592B, 0x5A66, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5B5A, 0x5B75, 0x5BCC, 0x5E9C, 0xF966, 0x6276, 0x6577, 0x65A7, 0x6D6E, 0x6EA5, 0x7236, 0x7B26, 0x7C3F, 0x7F36, 0x8150, 0x8151, 0x819A, 0x8240, 0x8299, 0x83A9, 0x8A03, 0x8CA0, 0x8CE6, 0x8CFB, 0x8D74, 0x8DBA, 0x90E8, 0x91DC, 0x961C, 0x9644, 0x99D9, 0x9CE7, 0x5317, 0x5206, 0x5429, 0x5674, 0x58B3, 0x5954, 0x596E, 0x5FFF, 0x61A4, 0x626E, 0x6610, 0x6C7E, 0x711A, 0x76C6, 0x7C89, 0x7CDE, 0x7D1B, 0x82AC, 0x8CC1, 0x96F0, 0xF967, 0x4F5B, 0x5F17, 0x5F7F, 0x62C2, 0x5D29, 0x670B, 0x68DA, 0x787C, 0x7E43, 0x9D6C, 0x4E15, 0x5099, 0x5315, 0x532A, 0x5351, 0x5983, 0x5A62, 0x5E87, 0x60B2, 0x618A, 0x6249, 0x6279, 0x6590, 0x6787, 0x69A7, 0x6BD4, 0x6BD6, 0x6BD7, 0x6BD8, 0x6CB8, 0xF968, 0x7435, 0x75FA, 0x7812, 0x7891, 0x79D5, 0x79D8, 0x7C83, 0x7DCB, 0x7FE1, 0x80A5, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x813E, 0x81C2, 0x83F2, 0x871A, 0x88E8, 0x8AB9, 0x8B6C, 0x8CBB, 0x9119, 0x975E, 0x98DB, 0x9F3B, 0x56AC, 0x5B2A, 0x5F6C, 0x658C, 0x6AB3, 0x6BAF, 0x6D5C, 0x6FF1, 0x7015, 0x725D, 0x73AD, 0x8CA7, 0x8CD3, 0x983B, 0x6191, 0x6C37, 0x8058, 0x9A01, 0x4E4D, 0x4E8B, 0x4E9B, 0x4ED5, 0x4F3A, 0x4F3C, 0x4F7F, 0x4FDF, 0x50FF, 0x53F2, 0x53F8, 0x5506, 0x55E3, 0x56DB, 0x58EB, 0x5962, 0x5A11, 0x5BEB, 0x5BFA, 0x5C04, 0x5DF3, 0x5E2B, 0x5F99, 0x601D, 0x6368, 0x659C, 0x65AF, 0x67F6, 0x67FB, 0x68AD, 0x6B7B, 0x6C99, 0x6CD7, 0x6E23, 0x7009, 0x7345, 0x7802, 0x793E, 0x7940, 0x7960, 0x79C1, 0x7BE9, 0x7D17, 0x7D72, 0x8086, 0x820D, 0x838E, 0x84D1, 0x86C7, 0x88DF, 0x8A50, 0x8A5E, 0x8B1D, 0x8CDC, 0x8D66, 0x8FAD, 0x90AA, 0x98FC, 0x99DF, 0x9E9D, 0x524A, 0xF969, 0x6714, 0xF96A, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5098, 0x522A, 0x5C71, 0x6563, 0x6C55, 0x73CA, 0x7523, 0x759D, 0x7B97, 0x849C, 0x9178, 0x9730, 0x4E77, 0x6492, 0x6BBA, 0x715E, 0x85A9, 0x4E09, 0xF96B, 0x6749, 0x68EE, 0x6E17, 0x829F, 0x8518, 0x886B, 0x63F7, 0x6F81, 0x9212, 0x98AF, 0x4E0A, 0x50B7, 0x50CF, 0x511F, 0x5546, 0x55AA, 0x5617, 0x5B40, 0x5C19, 0x5CE0, 0x5E38, 0x5E8A, 0x5EA0, 0x5EC2, 0x60F3, 0x6851, 0x6A61, 0x6E58, 0x723D, 0x7240, 0x72C0, 0x76F8, 0x7965, 0x7BB1, 0x7FD4, 0x88F3, 0x89F4, 0x8A73, 0x8C61, 0x8CDE, 0x971C, 0x585E, 0x74BD, 0x8CFD, 0x55C7, 0xF96C, 0x7A61, 0x7D22, 0x8272, 0x7272, 0x751F, 0x7525, 0xF96D, 0x7B19, 0x5885, 0x58FB, 0x5DBC, 0x5E8F, 0x5EB6, 0x5F90, 0x6055, 0x6292, 0x637F, 0x654D, 0x6691, 0x66D9, 0x66F8, 0x6816, 0x68F2, 0x7280, 0x745E, 0x7B6E, 0x7D6E, 0x7DD6, 0x7F72, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x80E5, 0x8212, 0x85AF, 0x897F, 0x8A93, 0x901D, 0x92E4, 0x9ECD, 0x9F20, 0x5915, 0x596D, 0x5E2D, 0x60DC, 0x6614, 0x6673, 0x6790, 0x6C50, 0x6DC5, 0x6F5F, 0x77F3, 0x78A9, 0x84C6, 0x91CB, 0x932B, 0x4ED9, 0x50CA, 0x5148, 0x5584, 0x5B0B, 0x5BA3, 0x6247, 0x657E, 0x65CB, 0x6E32, 0x717D, 0x7401, 0x7444, 0x7487, 0x74BF, 0x766C, 0x79AA, 0x7DDA, 0x7E55, 0x7FA8, 0x817A, 0x81B3, 0x8239, 0x861A, 0x87EC, 0x8A75, 0x8DE3, 0x9078, 0x9291, 0x9425, 0x994D, 0x9BAE, 0x5368, 0x5C51, 0x6954, 0x6CC4, 0x6D29, 0x6E2B, 0x820C, 0x859B, 0x893B, 0x8A2D, 0x8AAA, 0x96EA, 0x9F67, 0x5261, 0x66B9, 0x6BB2, 0x7E96, 0x87FE, 0x8D0D, 0x9583, 0x965D, 0x651D, 0x6D89, 0x71EE, 0xF96E, 0x57CE, 0x59D3, 0x5BAC, 0x6027, 0x60FA, 0x6210, 0x661F, 0x665F, 0x7329, 0x73F9, 0x76DB, 0x7701, 0x7B6C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8056, 0x8072, 0x8165, 0x8AA0, 0x9192, 0x4E16, 0x52E2, 0x6B72, 0x6D17, 0x7A05, 0x7B39, 0x7D30, 0xF96F, 0x8CB0, 0x53EC, 0x562F, 0x5851, 0x5BB5, 0x5C0F, 0x5C11, 0x5DE2, 0x6240, 0x6383, 0x6414, 0x662D, 0x68B3, 0x6CBC, 0x6D88, 0x6EAF, 0x701F, 0x70A4, 0x71D2, 0x7526, 0x758F, 0x758E, 0x7619, 0x7B11, 0x7BE0, 0x7C2B, 0x7D20, 0x7D39, 0x852C, 0x856D, 0x8607, 0x8A34, 0x900D, 0x9061, 0x90B5, 0x92B7, 0x97F6, 0x9A37, 0x4FD7, 0x5C6C, 0x675F, 0x6D91, 0x7C9F, 0x7E8C, 0x8B16, 0x8D16, 0x901F, 0x5B6B, 0x5DFD, 0x640D, 0x84C0, 0x905C, 0x98E1, 0x7387, 0x5B8B, 0x609A, 0x677E, 0x6DDE, 0x8A1F, 0x8AA6, 0x9001, 0x980C, 0x5237, 0xF970, 0x7051, 0x788E, 0x9396, 0x8870, 0x91D7, 0x4FEE, 0x53D7, 0x55FD, 0x56DA, 0x5782, 0x58FD, 0x5AC2, 0x5B88, 0x5CAB, 0x5CC0, 0x5E25, 0x6101, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x620D, 0x624B, 0x6388, 0x641C, 0x6536, 0x6578, 0x6A39, 0x6B8A, 0x6C34, 0x6D19, 0x6F31, 0x71E7, 0x72E9, 0x7378, 0x7407, 0x74B2, 0x7626, 0x7761, 0x79C0, 0x7A57, 0x7AEA, 0x7CB9, 0x7D8F, 0x7DAC, 0x7E61, 0x7F9E, 0x8129, 0x8331, 0x8490, 0x84DA, 0x85EA, 0x8896, 0x8AB0, 0x8B90, 0x8F38, 0x9042, 0x9083, 0x916C, 0x9296, 0x92B9, 0x968B, 0x96A7, 0x96A8, 0x96D6, 0x9700, 0x9808, 0x9996, 0x9AD3, 0x9B1A, 0x53D4, 0x587E, 0x5919, 0x5B70, 0x5BBF, 0x6DD1, 0x6F5A, 0x719F, 0x7421, 0x74B9, 0x8085, 0x83FD, 0x5DE1, 0x5F87, 0x5FAA, 0x6042, 0x65EC, 0x6812, 0x696F, 0x6A53, 0x6B89, 0x6D35, 0x6DF3, 0x73E3, 0x76FE, 0x77AC, 0x7B4D, 0x7D14, 0x8123, 0x821C, 0x8340, 0x84F4, 0x8563, 0x8A62, 0x8AC4, 0x9187, 0x931E, 0x9806, 0x99B4, 0x620C, 0x8853, 0x8FF0, 0x9265, 0x5D07, 0x5D27, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5D69, 0x745F, 0x819D, 0x8768, 0x6FD5, 0x62FE, 0x7FD2, 0x8936, 0x8972, 0x4E1E, 0x4E58, 0x50E7, 0x52DD, 0x5347, 0x627F, 0x6607, 0x7E69, 0x8805, 0x965E, 0x4F8D, 0x5319, 0x5636, 0x59CB, 0x5AA4, 0x5C38, 0x5C4E, 0x5C4D, 0x5E02, 0x5F11, 0x6043, 0x65BD, 0x662F, 0x6642, 0x67BE, 0x67F4, 0x731C, 0x77E2, 0x793A, 0x7FC5, 0x8494, 0x84CD, 0x8996, 0x8A66, 0x8A69, 0x8AE1, 0x8C55, 0x8C7A, 0x57F4, 0x5BD4, 0x5F0F, 0x606F, 0x62ED, 0x690D, 0x6B96, 0x6E5C, 0x7184, 0x7BD2, 0x8755, 0x8B58, 0x8EFE, 0x98DF, 0x98FE, 0x4F38, 0x4F81, 0x4FE1, 0x547B, 0x5A20, 0x5BB8, 0x613C, 0x65B0, 0x6668, 0x71FC, 0x7533, 0x795E, 0x7D33, 0x814E, 0x81E3, 0x8398, 0x85AA, 0x85CE, 0x8703, 0x8A0A, 0x8EAB, 0x8F9B, 0xF971, 0x8FC5, 0x5931, 0x5BA4, 0x5BE6, 0x6089, 0x5BE9, 0x5C0B, 0x5FC3, 0x6C81, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xF972, 0x6DF1, 0x700B, 0x751A, 0x82AF, 0x8AF6, 0x4EC0, 0x5341, 0xF973, 0x96D9, 0x6C0F, 0x4E9E, 0x4FC4, 0x5152, 0x555E, 0x5A25, 0x5CE8, 0x6211, 0x7259, 0x82BD, 0x83AA, 0x86FE, 0x8859, 0x8A1D, 0x963F, 0x96C5, 0x9913, 0x9D09, 0x9D5D, 0x580A, 0x5CB3, 0x5DBD, 0x5E44, 0x60E1, 0x6115, 0x63E1, 0x6A02, 0x6E25, 0x9102, 0x9354, 0x984E, 0x9C10, 0x9F77, 0x5B89, 0x5CB8, 0x6309, 0x664F, 0x6848, 0x773C, 0x96C1, 0x978D, 0x9854, 0x9B9F, 0x65A1, 0x8B01, 0x8ECB, 0x95BC, 0x5535, 0x5CA9, 0x5DD6, 0x5EB5, 0x6697, 0x764C, 0x83F4, 0x95C7, 0x58D3, 0x62BC, 0x72CE, 0x9D28, 0x4EF0, 0x592E, 0x600F, 0x663B, 0x6B83, 0x79E7, 0x9D26, 0x5393, 0x54C0, 0x57C3, 0x5D16, 0x611B, 0x66D6, 0x6DAF, 0x788D, 0x827E, 0x9698, 0x9744, 0x5384, 0x627C, 0x6396, 0x6DB2, 0x7E0A, 0x814B, 0x984D, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6AFB, 0x7F4C, 0x9DAF, 0x9E1A, 0x4E5F, 0x503B, 0x51B6, 0x591C, 0x60F9, 0x63F6, 0x6930, 0x723A, 0x8036, 0xF974, 0x91CE, 0x5F31, 0xF975, 0xF976, 0x7D04, 0x82E5, 0x846F, 0x84BB, 0x85E5, 0x8E8D, 0xF977, 0x4F6F, 0xF978, 0xF979, 0x58E4, 0x5B43, 0x6059, 0x63DA, 0x6518, 0x656D, 0x6698, 0xF97A, 0x694A, 0x6A23, 0x6D0B, 0x7001, 0x716C, 0x75D2, 0x760D, 0x79B3, 0x7A70, 0xF97B, 0x7F8A, 0xF97C, 0x8944, 0xF97D, 0x8B93, 0x91C0, 0x967D, 0xF97E, 0x990A, 0x5704, 0x5FA1, 0x65BC, 0x6F01, 0x7600, 0x79A6, 0x8A9E, 0x99AD, 0x9B5A, 0x9F6C, 0x5104, 0x61B6, 0x6291, 0x6A8D, 0x81C6, 0x5043, 0x5830, 0x5F66, 0x7109, 0x8A00, 0x8AFA, 0x5B7C, 0x8616, 0x4FFA, 0x513C, 0x56B4, 0x5944, 0x63A9, 0x6DF9, 0x5DAA, 0x696D, 0x5186, 0x4E88, 0x4F59, 0xF97F, 0xF980, 0xF981, 0x5982, 0xF982, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xF983, 0x6B5F, 0x6C5D, 0xF984, 0x74B5, 0x7916, 0xF985, 0x8207, 0x8245, 0x8339, 0x8F3F, 0x8F5D, 0xF986, 0x9918, 0xF987, 0xF988, 0xF989, 0x4EA6, 0xF98A, 0x57DF, 0x5F79, 0x6613, 0xF98B, 0xF98C, 0x75AB, 0x7E79, 0x8B6F, 0xF98D, 0x9006, 0x9A5B, 0x56A5, 0x5827, 0x59F8, 0x5A1F, 0x5BB4, 0xF98E, 0x5EF6, 0xF98F, 0xF990, 0x6350, 0x633B, 0xF991, 0x693D, 0x6C87, 0x6CBF, 0x6D8E, 0x6D93, 0x6DF5, 0x6F14, 0xF992, 0x70DF, 0x7136, 0x7159, 0xF993, 0x71C3, 0x71D5, 0xF994, 0x784F, 0x786F, 0xF995, 0x7B75, 0x7DE3, 0xF996, 0x7E2F, 0xF997, 0x884D, 0x8EDF, 0xF998, 0xF999, 0xF99A, 0x925B, 0xF99B, 0x9CF6, 0xF99C, 0xF99D, 0xF99E, 0x6085, 0x6D85, 0xF99F, 0x71B1, 0xF9A0, 0xF9A1, 0x95B1, 0x53AD, 0xF9A2, 0xF9A3, 0xF9A4, 0x67D3, 0xF9A5, 0x708E, 0x7130, 0x7430, 0x8276, 0x82D2, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xF9A6, 0x95BB, 0x9AE5, 0x9E7D, 0x66C4, 0xF9A7, 0x71C1, 0x8449, 0xF9A8, 0xF9A9, 0x584B, 0xF9AA, 0xF9AB, 0x5DB8, 0x5F71, 0xF9AC, 0x6620, 0x668E, 0x6979, 0x69AE, 0x6C38, 0x6CF3, 0x6E36, 0x6F41, 0x6FDA, 0x701B, 0x702F, 0x7150, 0x71DF, 0x7370, 0xF9AD, 0x745B, 0xF9AE, 0x74D4, 0x76C8, 0x7A4E, 0x7E93, 0xF9AF, 0xF9B0, 0x82F1, 0x8A60, 0x8FCE, 0xF9B1, 0x9348, 0xF9B2, 0x9719, 0xF9B3, 0xF9B4, 0x4E42, 0x502A, 0xF9B5, 0x5208, 0x53E1, 0x66F3, 0x6C6D, 0x6FCA, 0x730A, 0x777F, 0x7A62, 0x82AE, 0x85DD, 0x8602, 0xF9B6, 0x88D4, 0x8A63, 0x8B7D, 0x8C6B, 0xF9B7, 0x92B3, 0xF9B8, 0x9713, 0x9810, 0x4E94, 0x4F0D, 0x4FC9, 0x50B2, 0x5348, 0x543E, 0x5433, 0x55DA, 0x5862, 0x58BA, 0x5967, 0x5A1B, 0x5BE4, 0x609F, 0xF9B9, 0x61CA, 0x6556, 0x65FF, 0x6664, 0x68A7, 0x6C5A, 0x6FB3, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x70CF, 0x71AC, 0x7352, 0x7B7D, 0x8708, 0x8AA4, 0x9C32, 0x9F07, 0x5C4B, 0x6C83, 0x7344, 0x7389, 0x923A, 0x6EAB, 0x7465, 0x761F, 0x7A69, 0x7E15, 0x860A, 0x5140, 0x58C5, 0x64C1, 0x74EE, 0x7515, 0x7670, 0x7FC1, 0x9095, 0x96CD, 0x9954, 0x6E26, 0x74E6, 0x7AA9, 0x7AAA, 0x81E5, 0x86D9, 0x8778, 0x8A1B, 0x5A49, 0x5B8C, 0x5B9B, 0x68A1, 0x6900, 0x6D63, 0x73A9, 0x7413, 0x742C, 0x7897, 0x7DE9, 0x7FEB, 0x8118, 0x8155, 0x839E, 0x8C4C, 0x962E, 0x9811, 0x66F0, 0x5F80, 0x65FA, 0x6789, 0x6C6A, 0x738B, 0x502D, 0x5A03, 0x6B6A, 0x77EE, 0x5916, 0x5D6C, 0x5DCD, 0x7325, 0x754F, 0xF9BA, 0xF9BB, 0x50E5, 0x51F9, 0x582F, 0x592D, 0x5996, 0x59DA, 0x5BE5, 0xF9BC, 0xF9BD, 0x5DA2, 0x62D7, 0x6416, 0x6493, 0x64FE, 0xF9BE, 0x66DC, 0xF9BF, 0x6A48, 0xF9C0, 0x71FF, 0x7464, 0xF9C1, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7A88, 0x7AAF, 0x7E47, 0x7E5E, 0x8000, 0x8170, 0xF9C2, 0x87EF, 0x8981, 0x8B20, 0x9059, 0xF9C3, 0x9080, 0x9952, 0x617E, 0x6B32, 0x6D74, 0x7E1F, 0x8925, 0x8FB1, 0x4FD1, 0x50AD, 0x5197, 0x52C7, 0x57C7, 0x5889, 0x5BB9, 0x5EB8, 0x6142, 0x6995, 0x6D8C, 0x6E67, 0x6EB6, 0x7194, 0x7462, 0x7528, 0x752C, 0x8073, 0x8338, 0x84C9, 0x8E0A, 0x9394, 0x93DE, 0xF9C4, 0x4E8E, 0x4F51, 0x5076, 0x512A, 0x53C8, 0x53CB, 0x53F3, 0x5B87, 0x5BD3, 0x5C24, 0x611A, 0x6182, 0x65F4, 0x725B, 0x7397, 0x7440, 0x76C2, 0x7950, 0x7991, 0x79B9, 0x7D06, 0x7FBD, 0x828B, 0x85D5, 0x865E, 0x8FC2, 0x9047, 0x90F5, 0x91EA, 0x9685, 0x96E8, 0x96E9, 0x52D6, 0x5F67, 0x65ED, 0x6631, 0x682F, 0x715C, 0x7A36, 0x90C1, 0x980A, 0x4E91, 0xF9C5, 0x6A52, 0x6B9E, 0x6F90, 0x7189, 0x8018, 0x82B8, 0x8553, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x904B, 0x9695, 0x96F2, 0x97FB, 0x851A, 0x9B31, 0x4E90, 0x718A, 0x96C4, 0x5143, 0x539F, 0x54E1, 0x5713, 0x5712, 0x57A3, 0x5A9B, 0x5AC4, 0x5BC3, 0x6028, 0x613F, 0x63F4, 0x6C85, 0x6D39, 0x6E72, 0x6E90, 0x7230, 0x733F, 0x7457, 0x82D1, 0x8881, 0x8F45, 0x9060, 0xF9C6, 0x9662, 0x9858, 0x9D1B, 0x6708, 0x8D8A, 0x925E, 0x4F4D, 0x5049, 0x50DE, 0x5371, 0x570D, 0x59D4, 0x5A01, 0x5C09, 0x6170, 0x6690, 0x6E2D, 0x7232, 0x744B, 0x7DEF, 0x80C3, 0x840E, 0x8466, 0x853F, 0x875F, 0x885B, 0x8918, 0x8B02, 0x9055, 0x97CB, 0x9B4F, 0x4E73, 0x4F91, 0x5112, 0x516A, 0xF9C7, 0x552F, 0x55A9, 0x5B7A, 0x5BA5, 0x5E7C, 0x5E7D, 0x5EBE, 0x60A0, 0x60DF, 0x6108, 0x6109, 0x63C4, 0x6538, 0x6709, 0xF9C8, 0x67D4, 0x67DA, 0xF9C9, 0x6961, 0x6962, 0x6CB9, 0x6D27, 0xF9CA, 0x6E38, 0xF9CB, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6FE1, 0x7336, 0x7337, 0xF9CC, 0x745C, 0x7531, 0xF9CD, 0x7652, 0xF9CE, 0xF9CF, 0x7DAD, 0x81FE, 0x8438, 0x88D5, 0x8A98, 0x8ADB, 0x8AED, 0x8E30, 0x8E42, 0x904A, 0x903E, 0x907A, 0x9149, 0x91C9, 0x936E, 0xF9D0, 0xF9D1, 0x5809, 0xF9D2, 0x6BD3, 0x8089, 0x80B2, 0xF9D3, 0xF9D4, 0x5141, 0x596B, 0x5C39, 0xF9D5, 0xF9D6, 0x6F64, 0x73A7, 0x80E4, 0x8D07, 0xF9D7, 0x9217, 0x958F, 0xF9D8, 0xF9D9, 0xF9DA, 0xF9DB, 0x807F, 0x620E, 0x701C, 0x7D68, 0x878D, 0xF9DC, 0x57A0, 0x6069, 0x6147, 0x6BB7, 0x8ABE, 0x9280, 0x96B1, 0x4E59, 0x541F, 0x6DEB, 0x852D, 0x9670, 0x97F3, 0x98EE, 0x63D6, 0x6CE3, 0x9091, 0x51DD, 0x61C9, 0x81BA, 0x9DF9, 0x4F9D, 0x501A, 0x5100, 0x5B9C, 0x610F, 0x61FF, 0x64EC, 0x6905, 0x6BC5, 0x7591, 0x77E3, 0x7FA9, 0x8264, 0x858F, 0x87FB, 0x8863, 0x8ABC, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8B70, 0x91AB, 0x4E8C, 0x4EE5, 0x4F0A, 0xF9DD, 0xF9DE, 0x5937, 0x59E8, 0xF9DF, 0x5DF2, 0x5F1B, 0x5F5B, 0x6021, 0xF9E0, 0xF9E1, 0xF9E2, 0xF9E3, 0x723E, 0x73E5, 0xF9E4, 0x7570, 0x75CD, 0xF9E5, 0x79FB, 0xF9E6, 0x800C, 0x8033, 0x8084, 0x82E1, 0x8351, 0xF9E7, 0xF9E8, 0x8CBD, 0x8CB3, 0x9087, 0xF9E9, 0xF9EA, 0x98F4, 0x990C, 0xF9EB, 0xF9EC, 0x7037, 0x76CA, 0x7FCA, 0x7FCC, 0x7FFC, 0x8B1A, 0x4EBA, 0x4EC1, 0x5203, 0x5370, 0xF9ED, 0x54BD, 0x56E0, 0x59FB, 0x5BC5, 0x5F15, 0x5FCD, 0x6E6E, 0xF9EE, 0xF9EF, 0x7D6A, 0x8335, 0xF9F0, 0x8693, 0x8A8D, 0xF9F1, 0x976D, 0x9777, 0xF9F2, 0xF9F3, 0x4E00, 0x4F5A, 0x4F7E, 0x58F9, 0x65E5, 0x6EA2, 0x9038, 0x93B0, 0x99B9, 0x4EFB, 0x58EC, 0x598A, 0x59D9, 0x6041, 0xF9F4, 0xF9F5, 0x7A14, 0xF9F6, 0x834F, 0x8CC3, 0x5165, 0x5344, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xF9F7, 0xF9F8, 0xF9F9, 0x4ECD, 0x5269, 0x5B55, 0x82BF, 0x4ED4, 0x523A, 0x54A8, 0x59C9, 0x59FF, 0x5B50, 0x5B57, 0x5B5C, 0x6063, 0x6148, 0x6ECB, 0x7099, 0x716E, 0x7386, 0x74F7, 0x75B5, 0x78C1, 0x7D2B, 0x8005, 0x81EA, 0x8328, 0x8517, 0x85C9, 0x8AEE, 0x8CC7, 0x96CC, 0x4F5C, 0x52FA, 0x56BC, 0x65AB, 0x6628, 0x707C, 0x70B8, 0x7235, 0x7DBD, 0x828D, 0x914C, 0x96C0, 0x9D72, 0x5B71, 0x68E7, 0x6B98, 0x6F7A, 0x76DE, 0x5C91, 0x66AB, 0x6F5B, 0x7BB4, 0x7C2A, 0x8836, 0x96DC, 0x4E08, 0x4ED7, 0x5320, 0x5834, 0x58BB, 0x58EF, 0x596C, 0x5C07, 0x5E33, 0x5E84, 0x5F35, 0x638C, 0x66B2, 0x6756, 0x6A1F, 0x6AA3, 0x6B0C, 0x6F3F, 0x7246, 0xF9FA, 0x7350, 0x748B, 0x7AE0, 0x7CA7, 0x8178, 0x81DF, 0x81E7, 0x838A, 0x846C, 0x8523, 0x8594, 0x85CF, 0x88DD, 0x8D13, 0x91AC, 0x9577, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x969C, 0x518D, 0x54C9, 0x5728, 0x5BB0, 0x624D, 0x6750, 0x683D, 0x6893, 0x6E3D, 0x6ED3, 0x707D, 0x7E21, 0x88C1, 0x8CA1, 0x8F09, 0x9F4B, 0x9F4E, 0x722D, 0x7B8F, 0x8ACD, 0x931A, 0x4F47, 0x4F4E, 0x5132, 0x5480, 0x59D0, 0x5E95, 0x62B5, 0x6775, 0x696E, 0x6A17, 0x6CAE, 0x6E1A, 0x72D9, 0x732A, 0x75BD, 0x7BB8, 0x7D35, 0x82E7, 0x83F9, 0x8457, 0x85F7, 0x8A5B, 0x8CAF, 0x8E87, 0x9019, 0x90B8, 0x96CE, 0x9F5F, 0x52E3, 0x540A, 0x5AE1, 0x5BC2, 0x6458, 0x6575, 0x6EF4, 0x72C4, 0xF9FB, 0x7684, 0x7A4D, 0x7B1B, 0x7C4D, 0x7E3E, 0x7FDF, 0x837B, 0x8B2B, 0x8CCA, 0x8D64, 0x8DE1, 0x8E5F, 0x8FEA, 0x8FF9, 0x9069, 0x93D1, 0x4F43, 0x4F7A, 0x50B3, 0x5168, 0x5178, 0x524D, 0x526A, 0x5861, 0x587C, 0x5960, 0x5C08, 0x5C55, 0x5EDB, 0x609B, 0x6230, 0x6813, 0x6BBF, 0x6C08, 0x6FB1, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x714E, 0x7420, 0x7530, 0x7538, 0x7551, 0x7672, 0x7B4C, 0x7B8B, 0x7BAD, 0x7BC6, 0x7E8F, 0x8A6E, 0x8F3E, 0x8F49, 0x923F, 0x9293, 0x9322, 0x942B, 0x96FB, 0x985A, 0x986B, 0x991E, 0x5207, 0x622A, 0x6298, 0x6D59, 0x7664, 0x7ACA, 0x7BC0, 0x7D76, 0x5360, 0x5CBE, 0x5E97, 0x6F38, 0x70B9, 0x7C98, 0x9711, 0x9B8E, 0x9EDE, 0x63A5, 0x647A, 0x8776, 0x4E01, 0x4E95, 0x4EAD, 0x505C, 0x5075, 0x5448, 0x59C3, 0x5B9A, 0x5E40, 0x5EAD, 0x5EF7, 0x5F81, 0x60C5, 0x633A, 0x653F, 0x6574, 0x65CC, 0x6676, 0x6678, 0x67FE, 0x6968, 0x6A89, 0x6B63, 0x6C40, 0x6DC0, 0x6DE8, 0x6E1F, 0x6E5E, 0x701E, 0x70A1, 0x738E, 0x73FD, 0x753A, 0x775B, 0x7887, 0x798E, 0x7A0B, 0x7A7D, 0x7CBE, 0x7D8E, 0x8247, 0x8A02, 0x8AEA, 0x8C9E, 0x912D, 0x914A, 0x91D8, 0x9266, 0x92CC, 0x9320, 0x9706, 0x9756, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x975C, 0x9802, 0x9F0E, 0x5236, 0x5291, 0x557C, 0x5824, 0x5E1D, 0x5F1F, 0x608C, 0x63D0, 0x68AF, 0x6FDF, 0x796D, 0x7B2C, 0x81CD, 0x85BA, 0x88FD, 0x8AF8, 0x8E44, 0x918D, 0x9664, 0x969B, 0x973D, 0x984C, 0x9F4A, 0x4FCE, 0x5146, 0x51CB, 0x52A9, 0x5632, 0x5F14, 0x5F6B, 0x63AA, 0x64CD, 0x65E9, 0x6641, 0x66FA, 0x66F9, 0x671D, 0x689D, 0x68D7, 0x69FD, 0x6F15, 0x6F6E, 0x7167, 0x71E5, 0x722A, 0x74AA, 0x773A, 0x7956, 0x795A, 0x79DF, 0x7A20, 0x7A95, 0x7C97, 0x7CDF, 0x7D44, 0x7E70, 0x8087, 0x85FB, 0x86A4, 0x8A54, 0x8ABF, 0x8D99, 0x8E81, 0x9020, 0x906D, 0x91E3, 0x963B, 0x96D5, 0x9CE5, 0x65CF, 0x7C07, 0x8DB3, 0x93C3, 0x5B58, 0x5C0A, 0x5352, 0x62D9, 0x731D, 0x5027, 0x5B97, 0x5F9E, 0x60B0, 0x616B, 0x68D5, 0x6DD9, 0x742E, 0x7A2E, 0x7D42, 0x7D9C, 0x7E31, 0x816B, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8E2A, 0x8E35, 0x937E, 0x9418, 0x4F50, 0x5750, 0x5DE6, 0x5EA7, 0x632B, 0x7F6A, 0x4E3B, 0x4F4F, 0x4F8F, 0x505A, 0x59DD, 0x80C4, 0x546A, 0x5468, 0x55FE, 0x594F, 0x5B99, 0x5DDE, 0x5EDA, 0x665D, 0x6731, 0x67F1, 0x682A, 0x6CE8, 0x6D32, 0x6E4A, 0x6F8D, 0x70B7, 0x73E0, 0x7587, 0x7C4C, 0x7D02, 0x7D2C, 0x7DA2, 0x821F, 0x86DB, 0x8A3B, 0x8A85, 0x8D70, 0x8E8A, 0x8F33, 0x9031, 0x914E, 0x9152, 0x9444, 0x99D0, 0x7AF9, 0x7CA5, 0x4FCA, 0x5101, 0x51C6, 0x57C8, 0x5BEF, 0x5CFB, 0x6659, 0x6A3D, 0x6D5A, 0x6E96, 0x6FEC, 0x710C, 0x756F, 0x7AE3, 0x8822, 0x9021, 0x9075, 0x96CB, 0x99FF, 0x8301, 0x4E2D, 0x4EF2, 0x8846, 0x91CD, 0x537D, 0x6ADB, 0x696B, 0x6C41, 0x847A, 0x589E, 0x618E, 0x66FE, 0x62EF, 0x70DD, 0x7511, 0x75C7, 0x7E52, 0x84B8, 0x8B49, 0x8D08, 0x4E4B, 0x53EA, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x54AB, 0x5730, 0x5740, 0x5FD7, 0x6301, 0x6307, 0x646F, 0x652F, 0x65E8, 0x667A, 0x679D, 0x67B3, 0x6B62, 0x6C60, 0x6C9A, 0x6F2C, 0x77E5, 0x7825, 0x7949, 0x7957, 0x7D19, 0x80A2, 0x8102, 0x81F3, 0x829D, 0x82B7, 0x8718, 0x8A8C, 0xF9FC, 0x8D04, 0x8DBE, 0x9072, 0x76F4, 0x7A19, 0x7A37, 0x7E54, 0x8077, 0x5507, 0x55D4, 0x5875, 0x632F, 0x6422, 0x6649, 0x664B, 0x686D, 0x699B, 0x6B84, 0x6D25, 0x6EB1, 0x73CD, 0x7468, 0x74A1, 0x755B, 0x75B9, 0x76E1, 0x771E, 0x778B, 0x79E6, 0x7E09, 0x7E1D, 0x81FB, 0x852F, 0x8897, 0x8A3A, 0x8CD1, 0x8EEB, 0x8FB0, 0x9032, 0x93AD, 0x9663, 0x9673, 0x9707, 0x4F84, 0x53F1, 0x59EA, 0x5AC9, 0x5E19, 0x684E, 0x74C6, 0x75BE, 0x79E9, 0x7A92, 0x81A3, 0x86ED, 0x8CEA, 0x8DCC, 0x8FED, 0x659F, 0x6715, 0xF9FD, 0x57F7, 0x6F57, 0x7DDD, 0x8F2F, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x93F6, 0x96C6, 0x5FB5, 0x61F2, 0x6F84, 0x4E14, 0x4F98, 0x501F, 0x53C9, 0x55DF, 0x5D6F, 0x5DEE, 0x6B21, 0x6B64, 0x78CB, 0x7B9A, 0xF9FE, 0x8E49, 0x8ECA, 0x906E, 0x6349, 0x643E, 0x7740, 0x7A84, 0x932F, 0x947F, 0x9F6A, 0x64B0, 0x6FAF, 0x71E6, 0x74A8, 0x74DA, 0x7AC4, 0x7C12, 0x7E82, 0x7CB2, 0x7E98, 0x8B9A, 0x8D0A, 0x947D, 0x9910, 0x994C, 0x5239, 0x5BDF, 0x64E6, 0x672D, 0x7D2E, 0x50ED, 0x53C3, 0x5879, 0x6158, 0x6159, 0x61FA, 0x65AC, 0x7AD9, 0x8B92, 0x8B96, 0x5009, 0x5021, 0x5275, 0x5531, 0x5A3C, 0x5EE0, 0x5F70, 0x6134, 0x655E, 0x660C, 0x6636, 0x66A2, 0x69CD, 0x6EC4, 0x6F32, 0x7316, 0x7621, 0x7A93, 0x8139, 0x8259, 0x83D6, 0x84BC, 0x50B5, 0x57F0, 0x5BC0, 0x5BE8, 0x5F69, 0x63A1, 0x7826, 0x7DB5, 0x83DC, 0x8521, 0x91C7, 0x91F5, 0x518A, 0x67F5, 0x7B56, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8CAC, 0x51C4, 0x59BB, 0x60BD, 0x8655, 0x501C, 0xF9FF, 0x5254, 0x5C3A, 0x617D, 0x621A, 0x62D3, 0x64F2, 0x65A5, 0x6ECC, 0x7620, 0x810A, 0x8E60, 0x965F, 0x96BB, 0x4EDF, 0x5343, 0x5598, 0x5929, 0x5DDD, 0x64C5, 0x6CC9, 0x6DFA, 0x7394, 0x7A7F, 0x821B, 0x85A6, 0x8CE4, 0x8E10, 0x9077, 0x91E7, 0x95E1, 0x9621, 0x97C6, 0x51F8, 0x54F2, 0x5586, 0x5FB9, 0x64A4, 0x6F88, 0x7DB4, 0x8F1F, 0x8F4D, 0x9435, 0x50C9, 0x5C16, 0x6CBE, 0x6DFB, 0x751B, 0x77BB, 0x7C3D, 0x7C64, 0x8A79, 0x8AC2, 0x581E, 0x59BE, 0x5E16, 0x6377, 0x7252, 0x758A, 0x776B, 0x8ADC, 0x8CBC, 0x8F12, 0x5EF3, 0x6674, 0x6DF8, 0x807D, 0x83C1, 0x8ACB, 0x9751, 0x9BD6, 0xFA00, 0x5243, 0x66FF, 0x6D95, 0x6EEF, 0x7DE0, 0x8AE6, 0x902E, 0x905E, 0x9AD4, 0x521D, 0x527F, 0x54E8, 0x6194, 0x6284, 0x62DB, 0x68A2, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6912, 0x695A, 0x6A35, 0x7092, 0x7126, 0x785D, 0x7901, 0x790E, 0x79D2, 0x7A0D, 0x8096, 0x8278, 0x82D5, 0x8349, 0x8549, 0x8C82, 0x8D85, 0x9162, 0x918B, 0x91AE, 0x4FC3, 0x56D1, 0x71ED, 0x77D7, 0x8700, 0x89F8, 0x5BF8, 0x5FD6, 0x6751, 0x90A8, 0x53E2, 0x585A, 0x5BF5, 0x60A4, 0x6181, 0x6460, 0x7E3D, 0x8070, 0x8525, 0x9283, 0x64AE, 0x50AC, 0x5D14, 0x6700, 0x589C, 0x62BD, 0x63A8, 0x690E, 0x6978, 0x6A1E, 0x6E6B, 0x76BA, 0x79CB, 0x82BB, 0x8429, 0x8ACF, 0x8DA8, 0x8FFD, 0x9112, 0x914B, 0x919C, 0x9310, 0x9318, 0x939A, 0x96DB, 0x9A36, 0x9C0D, 0x4E11, 0x755C, 0x795D, 0x7AFA, 0x7B51, 0x7BC9, 0x7E2E, 0x84C4, 0x8E59, 0x8E74, 0x8EF8, 0x9010, 0x6625, 0x693F, 0x7443, 0x51FA, 0x672E, 0x9EDC, 0x5145, 0x5FE0, 0x6C96, 0x87F2, 0x885D, 0x8877, 0x60B4, 0x81B5, 0x8403, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8D05, 0x53D6, 0x5439, 0x5634, 0x5A36, 0x5C31, 0x708A, 0x7FE0, 0x805A, 0x8106, 0x81ED, 0x8DA3, 0x9189, 0x9A5F, 0x9DF2, 0x5074, 0x4EC4, 0x53A0, 0x60FB, 0x6E2C, 0x5C64, 0x4F88, 0x5024, 0x55E4, 0x5CD9, 0x5E5F, 0x6065, 0x6894, 0x6CBB, 0x6DC4, 0x71BE, 0x75D4, 0x75F4, 0x7661, 0x7A1A, 0x7A49, 0x7DC7, 0x7DFB, 0x7F6E, 0x81F4, 0x86A9, 0x8F1C, 0x96C9, 0x99B3, 0x9F52, 0x5247, 0x52C5, 0x98ED, 0x89AA, 0x4E03, 0x67D2, 0x6F06, 0x4FB5, 0x5BE2, 0x6795, 0x6C88, 0x6D78, 0x741B, 0x7827, 0x91DD, 0x937C, 0x87C4, 0x79E4, 0x7A31, 0x5FEB, 0x4ED6, 0x54A4, 0x553E, 0x58AE, 0x59A5, 0x60F0, 0x6253, 0x62D6, 0x6736, 0x6955, 0x8235, 0x9640, 0x99B1, 0x99DD, 0x502C, 0x5353, 0x5544, 0x577C, 0xFA01, 0x6258, 0xFA02, 0x64E2, 0x666B, 0x67DD, 0x6FC1, 0x6FEF, 0x7422, 0x7438, 0x8A17, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9438, 0x5451, 0x5606, 0x5766, 0x5F48, 0x619A, 0x6B4E, 0x7058, 0x70AD, 0x7DBB, 0x8A95, 0x596A, 0x812B, 0x63A2, 0x7708, 0x803D, 0x8CAA, 0x5854, 0x642D, 0x69BB, 0x5B95, 0x5E11, 0x6E6F, 0xFA03, 0x8569, 0x514C, 0x53F0, 0x592A, 0x6020, 0x614B, 0x6B86, 0x6C70, 0x6CF0, 0x7B1E, 0x80CE, 0x82D4, 0x8DC6, 0x90B0, 0x98B1, 0xFA04, 0x64C7, 0x6FA4, 0x6491, 0x6504, 0x514E, 0x5410, 0x571F, 0x8A0E, 0x615F, 0x6876, 0xFA05, 0x75DB, 0x7B52, 0x7D71, 0x901A, 0x5806, 0x69CC, 0x817F, 0x892A, 0x9000, 0x9839, 0x5078, 0x5957, 0x59AC, 0x6295, 0x900F, 0x9B2A, 0x615D, 0x7279, 0x95D6, 0x5761, 0x5A46, 0x5DF4, 0x628A, 0x64AD, 0x64FA, 0x6777, 0x6CE2, 0x6D3E, 0x722C, 0x7436, 0x7834, 0x7F77, 0x82AD, 0x8DDB, 0x9817, 0x5224, 0x5742, 0x677F, 0x7248, 0x74E3, 0x8CA9, 0x8FA6, 0x9211, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x962A, 0x516B, 0x53ED, 0x634C, 0x4F69, 0x5504, 0x6096, 0x6557, 0x6C9B, 0x6D7F, 0x724C, 0x72FD, 0x7A17, 0x8987, 0x8C9D, 0x5F6D, 0x6F8E, 0x70F9, 0x81A8, 0x610E, 0x4FBF, 0x504F, 0x6241, 0x7247, 0x7BC7, 0x7DE8, 0x7FE9, 0x904D, 0x97AD, 0x9A19, 0x8CB6, 0x576A, 0x5E73, 0x67B0, 0x840D, 0x8A55, 0x5420, 0x5B16, 0x5E63, 0x5EE2, 0x5F0A, 0x6583, 0x80BA, 0x853D, 0x9589, 0x965B, 0x4F48, 0x5305, 0x530D, 0x530F, 0x5486, 0x54FA, 0x5703, 0x5E03, 0x6016, 0x629B, 0x62B1, 0x6355, 0xFA06, 0x6CE1, 0x6D66, 0x75B1, 0x7832, 0x80DE, 0x812F, 0x82DE, 0x8461, 0x84B2, 0x888D, 0x8912, 0x900B, 0x92EA, 0x98FD, 0x9B91, 0x5E45, 0x66B4, 0x66DD, 0x7011, 0x7206, 0xFA07, 0x4FF5, 0x527D, 0x5F6A, 0x6153, 0x6753, 0x6A19, 0x6F02, 0x74E2, 0x7968, 0x8868, 0x8C79, 0x98C7, 0x98C4, 0x9A43, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x54C1, 0x7A1F, 0x6953, 0x8AF7, 0x8C4A, 0x98A8, 0x99AE, 0x5F7C, 0x62AB, 0x75B2, 0x76AE, 0x88AB, 0x907F, 0x9642, 0x5339, 0x5F3C, 0x5FC5, 0x6CCC, 0x73CC, 0x7562, 0x758B, 0x7B46, 0x82FE, 0x999D, 0x4E4F, 0x903C, 0x4E0B, 0x4F55, 0x53A6, 0x590F, 0x5EC8, 0x6630, 0x6CB3, 0x7455, 0x8377, 0x8766, 0x8CC0, 0x9050, 0x971E, 0x9C15, 0x58D1, 0x5B78, 0x8650, 0x8B14, 0x9DB4, 0x5BD2, 0x6068, 0x608D, 0x65F1, 0x6C57, 0x6F22, 0x6FA3, 0x701A, 0x7F55, 0x7FF0, 0x9591, 0x9592, 0x9650, 0x97D3, 0x5272, 0x8F44, 0x51FD, 0x542B, 0x54B8, 0x5563, 0x558A, 0x6ABB, 0x6DB5, 0x7DD8, 0x8266, 0x929C, 0x9677, 0x9E79, 0x5408, 0x54C8, 0x76D2, 0x86E4, 0x95A4, 0x95D4, 0x965C, 0x4EA2, 0x4F09, 0x59EE, 0x5AE6, 0x5DF7, 0x6052, 0x6297, 0x676D, 0x6841, 0x6C86, 0x6E2F, 0x7F38, 0x809B, 0x822A, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFA08, 0xFA09, 0x9805, 0x4EA5, 0x5055, 0x54B3, 0x5793, 0x595A, 0x5B69, 0x5BB3, 0x61C8, 0x6977, 0x6D77, 0x7023, 0x87F9, 0x89E3, 0x8A72, 0x8AE7, 0x9082, 0x99ED, 0x9AB8, 0x52BE, 0x6838, 0x5016, 0x5E78, 0x674F, 0x8347, 0x884C, 0x4EAB, 0x5411, 0x56AE, 0x73E6, 0x9115, 0x97FF, 0x9909, 0x9957, 0x9999, 0x5653, 0x589F, 0x865B, 0x8A31, 0x61B2, 0x6AF6, 0x737B, 0x8ED2, 0x6B47, 0x96AA, 0x9A57, 0x5955, 0x7200, 0x8D6B, 0x9769, 0x4FD4, 0x5CF4, 0x5F26, 0x61F8, 0x665B, 0x6CEB, 0x70AB, 0x7384, 0x73B9, 0x73FE, 0x7729, 0x774D, 0x7D43, 0x7D62, 0x7E23, 0x8237, 0x8852, 0xFA0A, 0x8CE2, 0x9249, 0x986F, 0x5B51, 0x7A74, 0x8840, 0x9801, 0x5ACC, 0x4FE0, 0x5354, 0x593E, 0x5CFD, 0x633E, 0x6D79, 0x72F9, 0x8105, 0x8107, 0x83A2, 0x92CF, 0x9830, 0x4EA8, 0x5144, 0x5211, 0x578B, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5F62, 0x6CC2, 0x6ECE, 0x7005, 0x7050, 0x70AF, 0x7192, 0x73E9, 0x7469, 0x834A, 0x87A2, 0x8861, 0x9008, 0x90A2, 0x93A3, 0x99A8, 0x516E, 0x5F57, 0x60E0, 0x6167, 0x66B3, 0x8559, 0x8E4A, 0x91AF, 0x978B, 0x4E4E, 0x4E92, 0x547C, 0x58D5, 0x58FA, 0x597D, 0x5CB5, 0x5F27, 0x6236, 0x6248, 0x660A, 0x6667, 0x6BEB, 0x6D69, 0x6DCF, 0x6E56, 0x6EF8, 0x6F94, 0x6FE0, 0x6FE9, 0x705D, 0x72D0, 0x7425, 0x745A, 0x74E0, 0x7693, 0x795C, 0x7CCA, 0x7E1E, 0x80E1, 0x82A6, 0x846B, 0x84BF, 0x864E, 0x865F, 0x8774, 0x8B77, 0x8C6A, 0x93AC, 0x9800, 0x9865, 0x60D1, 0x6216, 0x9177, 0x5A5A, 0x660F, 0x6DF7, 0x6E3E, 0x743F, 0x9B42, 0x5FFD, 0x60DA, 0x7B0F, 0x54C4, 0x5F18, 0x6C5E, 0x6CD3, 0x6D2A, 0x70D8, 0x7D05, 0x8679, 0x8A0C, 0x9D3B, 0x5316, 0x548C, 0x5B05, 0x6A3A, 0x706B, 0x7575, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x798D, 0x79BE, 0x82B1, 0x83EF, 0x8A71, 0x8B41, 0x8CA8, 0x9774, 0xFA0B, 0x64F4, 0x652B, 0x78BA, 0x78BB, 0x7A6B, 0x4E38, 0x559A, 0x5950, 0x5BA6, 0x5E7B, 0x60A3, 0x63DB, 0x6B61, 0x6665, 0x6853, 0x6E19, 0x7165, 0x74B0, 0x7D08, 0x9084, 0x9A69, 0x9C25, 0x6D3B, 0x6ED1, 0x733E, 0x8C41, 0x95CA, 0x51F0, 0x5E4C, 0x5FA8, 0x604D, 0x60F6, 0x6130, 0x614C, 0x6643, 0x6644, 0x69A5, 0x6CC1, 0x6E5F, 0x6EC9, 0x6F62, 0x714C, 0x749C, 0x7687, 0x7BC1, 0x7C27, 0x8352, 0x8757, 0x9051, 0x968D, 0x9EC3, 0x532F, 0x56DE, 0x5EFB, 0x5F8A, 0x6062, 0x6094, 0x61F7, 0x6666, 0x6703, 0x6A9C, 0x6DEE, 0x6FAE, 0x7070, 0x736A, 0x7E6A, 0x81BE, 0x8334, 0x86D4, 0x8AA8, 0x8CC4, 0x5283, 0x7372, 0x5B96, 0x6A6B, 0x9404, 0x54EE, 0x5686, 0x5B5D, 0x6548, 0x6585, 0x66C9, 0x689F, 0x6D8D, 0x6DC6, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x723B, 0x80B4, 0x9175, 0x9A4D, 0x4FAF, 0x5019, 0x539A, 0x540E, 0x543C, 0x5589, 0x55C5, 0x5E3F, 0x5F8C, 0x673D, 0x7166, 0x73DD, 0x9005, 0x52DB, 0x52F3, 0x5864, 0x58CE, 0x7104, 0x718F, 0x71FB, 0x85B0, 0x8A13, 0x6688, 0x85A8, 0x55A7, 0x6684, 0x714A, 0x8431, 0x5349, 0x5599, 0x6BC1, 0x5F59, 0x5FBD, 0x63EE, 0x6689, 0x7147, 0x8AF1, 0x8F1D, 0x9EBE, 0x4F11, 0x643A, 0x70CB, 0x7566, 0x8667, 0x6064, 0x8B4E, 0x9DF8, 0x5147, 0x51F6, 0x5308, 0x6D36, 0x80F8, 0x9ED1, 0x6615, 0x6B23, 0x7098, 0x75D5, 0x5403, 0x5C79, 0x7D07, 0x8A16, 0x6B20, 0x6B3D, 0x6B46, 0x5438, 0x6070, 0x6D3D, 0x7FD5, 0x8208, 0x50D6, 0x51DE, 0x559C, 0x566B, 0x56CD, 0x59EC, 0x5B09, 0x5E0C, 0x6199, 0x6198, 0x6231, 0x665E, 0x66E6, 0x7199, 0x71B9, 0x71BA, 0x72A7, 0x79A7, 0x7A00, 0x7FB2, 0x8A70, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +} diff --git a/core/libs/prx/libSceCesCs/Cp950.hpp b/core/libs/prx/libSceCesCs/Cp950.hpp new file mode 100644 index 0000000000..ad0e018b29 --- /dev/null +++ b/core/libs/prx/libSceCesCs/Cp950.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include + +namespace Cp950 { + +inline constexpr uint16_t TO_UCS2[89 * 157] = { + 0x3000, 0xFF0C, 0x3001, 0x3002, 0xFF0E, 0x2027, 0xFF1B, 0xFF1A, 0xFF1F, 0xFF01, 0xFE30, 0x2026, 0x2025, 0xFE50, 0xFE51, 0xFE52, 0x00B7, 0xFE54, 0xFE55, 0xFE56, 0xFE57, 0xFF5C, 0x2013, 0xFE31, 0x2014, 0xFE33, 0x2574, 0xFE34, 0xFE4F, 0xFF08, 0xFF09, 0xFE35, 0xFE36, 0xFF5B, 0xFF5D, 0xFE37, 0xFE38, 0x3014, 0x3015, 0xFE39, 0xFE3A, 0x3010, 0x3011, 0xFE3B, 0xFE3C, 0x300A, 0x300B, 0xFE3D, 0xFE3E, 0x3008, 0x3009, 0xFE3F, 0xFE40, 0x300C, 0x300D, 0xFE41, 0xFE42, 0x300E, 0x300F, 0xFE43, 0xFE44, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0x2018, 0x2019, 0x201C, 0x201D, 0x301D, 0x301E, 0x2035, 0x2032, 0xFF03, 0xFF06, 0xFF0A, 0x203B, 0x00A7, 0x3003, 0x25CB, 0x25CF, 0x25B3, 0x25B2, 0x25CE, 0x2606, 0x2605, 0x25C7, 0x25C6, 0x25A1, 0x25A0, 0x25BD, 0x25BC, 0x32A3, 0x2105, 0x00AF, 0xFFE3, 0xFF3F, 0x02CD, 0xFE49, 0xFE4A, 0xFE4D, 0xFE4E, 0xFE4B, 0xFE4C, 0xFE5F, 0xFE60, 0xFE61, 0xFF0B, 0xFF0D, 0x00D7, 0x00F7, 0x00B1, 0x221A, 0xFF1C, 0xFF1E, 0xFF1D, 0x2266, 0x2267, 0x2260, 0x221E, 0x2252, 0x2261, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFF5E, 0x2229, 0x222A, 0x22A5, 0x2220, 0x221F, 0x22BF, 0x33D2, 0x33D1, 0x222B, 0x222E, 0x2235, 0x2234, 0x2640, 0x2642, 0x2295, 0x2299, 0x2191, 0x2193, 0x2190, 0x2192, 0x2196, 0x2197, 0x2199, 0x2198, 0x2225, 0x2223, 0xFF0F, + 0xFF3C, 0x2215, 0xFE68, 0xFF04, 0xFFE5, 0x3012, 0xFFE0, 0xFFE1, 0xFF05, 0xFF20, 0x2103, 0x2109, 0xFE69, 0xFE6A, 0xFE6B, 0x33D5, 0x339C, 0x339D, 0x339E, 0x33CE, 0x33A1, 0x338E, 0x338F, 0x33C4, 0x00B0, 0x5159, 0x515B, 0x515E, 0x515D, 0x5161, 0x5163, 0x55E7, 0x74E9, 0x7CCE, 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2586, 0x2587, 0x2588, 0x258F, 0x258E, 0x258D, 0x258C, 0x258B, 0x258A, 0x2589, 0x253C, 0x2534, 0x252C, 0x2524, 0x251C, 0x2594, 0x2500, 0x2502, 0x2595, 0x250C, 0x2510, 0x2514, 0x2518, 0x256D, 0x256E, 0x2570, 0x256F, 0x2550, 0x255E, 0x256A, 0x2561, 0x25E2, 0x25E3, 0x25E5, 0x25E4, 0x2571, 0x2572, 0x2573, 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x3021, 0x3022, 0x3023, 0x3024, 0x3025, 0x3026, 0x3027, 0x3028, 0x3029, 0x5341, 0x5344, 0x5345, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, + 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x3105, 0x3106, 0x3107, 0x3108, 0x3109, 0x310A, 0x310B, 0x310C, 0x310D, 0x310E, 0x310F, 0x3110, 0x3111, 0x3112, 0x3113, 0x3114, 0x3115, 0x3116, 0x3117, 0x3118, 0x3119, 0x311A, 0x311B, 0x311C, 0x311D, 0x311E, 0x311F, 0x3120, 0x3121, 0x3122, 0x3123, 0x3124, 0x3125, 0x3126, 0x3127, 0x3128, 0x3129, 0x02D9, 0x02C9, 0x02CA, 0x02C7, 0x02CB, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x4E00, 0x4E59, 0x4E01, 0x4E03, 0x4E43, 0x4E5D, 0x4E86, 0x4E8C, 0x4EBA, 0x513F, 0x5165, 0x516B, 0x51E0, 0x5200, 0x5201, 0x529B, 0x5315, 0x5341, 0x535C, 0x53C8, 0x4E09, 0x4E0B, 0x4E08, 0x4E0A, 0x4E2B, 0x4E38, 0x51E1, 0x4E45, 0x4E48, 0x4E5F, 0x4E5E, 0x4E8E, 0x4EA1, 0x5140, 0x5203, 0x52FA, 0x5343, 0x53C9, 0x53E3, 0x571F, 0x58EB, 0x5915, 0x5927, 0x5973, 0x5B50, 0x5B51, 0x5B53, 0x5BF8, 0x5C0F, 0x5C22, 0x5C38, 0x5C71, 0x5DDD, 0x5DE5, 0x5DF1, 0x5DF2, 0x5DF3, 0x5DFE, 0x5E72, 0x5EFE, 0x5F0B, 0x5F13, 0x624D, 0x4E11, 0x4E10, 0x4E0D, 0x4E2D, 0x4E30, 0x4E39, 0x4E4B, 0x5C39, 0x4E88, 0x4E91, 0x4E95, 0x4E92, 0x4E94, 0x4EA2, 0x4EC1, 0x4EC0, 0x4EC3, 0x4EC6, 0x4EC7, 0x4ECD, 0x4ECA, 0x4ECB, 0x4EC4, 0x5143, 0x5141, 0x5167, 0x516D, 0x516E, 0x516C, 0x5197, 0x51F6, 0x5206, 0x5207, 0x5208, 0x52FB, 0x52FE, 0x52FF, 0x5316, 0x5339, 0x5348, 0x5347, 0x5345, 0x535E, 0x5384, 0x53CB, 0x53CA, 0x53CD, 0x58EC, 0x5929, 0x592B, 0x592A, 0x592D, 0x5B54, 0x5C11, 0x5C24, 0x5C3A, 0x5C6F, 0x5DF4, 0x5E7B, 0x5EFF, 0x5F14, 0x5F15, 0x5FC3, 0x6208, 0x6236, 0x624B, 0x624E, 0x652F, 0x6587, 0x6597, 0x65A4, 0x65B9, 0x65E5, 0x66F0, 0x6708, 0x6728, 0x6B20, 0x6B62, 0x6B79, 0x6BCB, 0x6BD4, 0x6BDB, 0x6C0F, 0x6C34, 0x706B, 0x722A, 0x7236, 0x723B, 0x7247, 0x7259, 0x725B, 0x72AC, 0x738B, 0x4E19, + 0x4E16, 0x4E15, 0x4E14, 0x4E18, 0x4E3B, 0x4E4D, 0x4E4F, 0x4E4E, 0x4EE5, 0x4ED8, 0x4ED4, 0x4ED5, 0x4ED6, 0x4ED7, 0x4EE3, 0x4EE4, 0x4ED9, 0x4EDE, 0x5145, 0x5144, 0x5189, 0x518A, 0x51AC, 0x51F9, 0x51FA, 0x51F8, 0x520A, 0x52A0, 0x529F, 0x5305, 0x5306, 0x5317, 0x531D, 0x4EDF, 0x534A, 0x5349, 0x5361, 0x5360, 0x536F, 0x536E, 0x53BB, 0x53EF, 0x53E4, 0x53F3, 0x53EC, 0x53EE, 0x53E9, 0x53E8, 0x53FC, 0x53F8, 0x53F5, 0x53EB, 0x53E6, 0x53EA, 0x53F2, 0x53F1, 0x53F0, 0x53E5, 0x53ED, 0x53FB, 0x56DB, 0x56DA, 0x5916, 0x592E, 0x5931, 0x5974, 0x5976, 0x5B55, 0x5B83, 0x5C3C, 0x5DE8, 0x5DE7, 0x5DE6, 0x5E02, 0x5E03, 0x5E73, 0x5E7C, 0x5F01, 0x5F18, 0x5F17, 0x5FC5, 0x620A, 0x6253, 0x6254, 0x6252, 0x6251, 0x65A5, 0x65E6, 0x672E, 0x672C, 0x672A, 0x672B, 0x672D, 0x6B63, 0x6BCD, 0x6C11, 0x6C10, 0x6C38, 0x6C41, 0x6C40, 0x6C3E, 0x72AF, 0x7384, 0x7389, 0x74DC, 0x74E6, 0x7518, 0x751F, 0x7528, 0x7529, 0x7530, 0x7531, 0x7532, 0x7533, 0x758B, 0x767D, 0x76AE, 0x76BF, 0x76EE, 0x77DB, 0x77E2, 0x77F3, 0x793A, 0x79BE, 0x7A74, 0x7ACB, 0x4E1E, 0x4E1F, 0x4E52, 0x4E53, 0x4E69, 0x4E99, 0x4EA4, 0x4EA6, 0x4EA5, 0x4EFF, 0x4F09, 0x4F19, 0x4F0A, 0x4F15, 0x4F0D, 0x4F10, 0x4F11, 0x4F0F, 0x4EF2, 0x4EF6, 0x4EFB, 0x4EF0, 0x4EF3, 0x4EFD, 0x4F01, 0x4F0B, 0x5149, 0x5147, 0x5146, 0x5148, 0x5168, + 0x5171, 0x518D, 0x51B0, 0x5217, 0x5211, 0x5212, 0x520E, 0x5216, 0x52A3, 0x5308, 0x5321, 0x5320, 0x5370, 0x5371, 0x5409, 0x540F, 0x540C, 0x540A, 0x5410, 0x5401, 0x540B, 0x5404, 0x5411, 0x540D, 0x5408, 0x5403, 0x540E, 0x5406, 0x5412, 0x56E0, 0x56DE, 0x56DD, 0x5733, 0x5730, 0x5728, 0x572D, 0x572C, 0x572F, 0x5729, 0x5919, 0x591A, 0x5937, 0x5938, 0x5984, 0x5978, 0x5983, 0x597D, 0x5979, 0x5982, 0x5981, 0x5B57, 0x5B58, 0x5B87, 0x5B88, 0x5B85, 0x5B89, 0x5BFA, 0x5C16, 0x5C79, 0x5DDE, 0x5E06, 0x5E76, 0x5E74, 0x5F0F, 0x5F1B, 0x5FD9, 0x5FD6, 0x620E, 0x620C, 0x620D, 0x6210, 0x6263, 0x625B, 0x6258, 0x6536, 0x65E9, 0x65E8, 0x65EC, 0x65ED, 0x66F2, 0x66F3, 0x6709, 0x673D, 0x6734, 0x6731, 0x6735, 0x6B21, 0x6B64, 0x6B7B, 0x6C16, 0x6C5D, 0x6C57, 0x6C59, 0x6C5F, 0x6C60, 0x6C50, 0x6C55, 0x6C61, 0x6C5B, 0x6C4D, 0x6C4E, 0x7070, 0x725F, 0x725D, 0x767E, 0x7AF9, 0x7C73, 0x7CF8, 0x7F36, 0x7F8A, 0x7FBD, 0x8001, 0x8003, 0x800C, 0x8012, 0x8033, 0x807F, 0x8089, 0x808B, 0x808C, 0x81E3, 0x81EA, 0x81F3, 0x81FC, 0x820C, 0x821B, 0x821F, 0x826E, 0x8272, 0x827E, 0x866B, 0x8840, 0x884C, 0x8863, 0x897F, 0x9621, 0x4E32, 0x4EA8, 0x4F4D, 0x4F4F, 0x4F47, 0x4F57, 0x4F5E, 0x4F34, 0x4F5B, 0x4F55, 0x4F30, 0x4F50, 0x4F51, 0x4F3D, 0x4F3A, 0x4F38, 0x4F43, 0x4F54, 0x4F3C, 0x4F46, 0x4F63, + 0x4F5C, 0x4F60, 0x4F2F, 0x4F4E, 0x4F36, 0x4F59, 0x4F5D, 0x4F48, 0x4F5A, 0x514C, 0x514B, 0x514D, 0x5175, 0x51B6, 0x51B7, 0x5225, 0x5224, 0x5229, 0x522A, 0x5228, 0x52AB, 0x52A9, 0x52AA, 0x52AC, 0x5323, 0x5373, 0x5375, 0x541D, 0x542D, 0x541E, 0x543E, 0x5426, 0x544E, 0x5427, 0x5446, 0x5443, 0x5433, 0x5448, 0x5442, 0x541B, 0x5429, 0x544A, 0x5439, 0x543B, 0x5438, 0x542E, 0x5435, 0x5436, 0x5420, 0x543C, 0x5440, 0x5431, 0x542B, 0x541F, 0x542C, 0x56EA, 0x56F0, 0x56E4, 0x56EB, 0x574A, 0x5751, 0x5740, 0x574D, 0x5747, 0x574E, 0x573E, 0x5750, 0x574F, 0x573B, 0x58EF, 0x593E, 0x599D, 0x5992, 0x59A8, 0x599E, 0x59A3, 0x5999, 0x5996, 0x598D, 0x59A4, 0x5993, 0x598A, 0x59A5, 0x5B5D, 0x5B5C, 0x5B5A, 0x5B5B, 0x5B8C, 0x5B8B, 0x5B8F, 0x5C2C, 0x5C40, 0x5C41, 0x5C3F, 0x5C3E, 0x5C90, 0x5C91, 0x5C94, 0x5C8C, 0x5DEB, 0x5E0C, 0x5E8F, 0x5E87, 0x5E8A, 0x5EF7, 0x5F04, 0x5F1F, 0x5F64, 0x5F62, 0x5F77, 0x5F79, 0x5FD8, 0x5FCC, 0x5FD7, 0x5FCD, 0x5FF1, 0x5FEB, 0x5FF8, 0x5FEA, 0x6212, 0x6211, 0x6284, 0x6297, 0x6296, 0x6280, 0x6276, 0x6289, 0x626D, 0x628A, 0x627C, 0x627E, 0x6279, 0x6273, 0x6292, 0x626F, 0x6298, 0x626E, 0x6295, 0x6293, 0x6291, 0x6286, 0x6539, 0x653B, 0x6538, 0x65F1, 0x66F4, 0x675F, 0x674E, 0x674F, 0x6750, 0x6751, 0x675C, 0x6756, 0x675E, 0x6749, 0x6746, 0x6760, + 0x6753, 0x6757, 0x6B65, 0x6BCF, 0x6C42, 0x6C5E, 0x6C99, 0x6C81, 0x6C88, 0x6C89, 0x6C85, 0x6C9B, 0x6C6A, 0x6C7A, 0x6C90, 0x6C70, 0x6C8C, 0x6C68, 0x6C96, 0x6C92, 0x6C7D, 0x6C83, 0x6C72, 0x6C7E, 0x6C74, 0x6C86, 0x6C76, 0x6C8D, 0x6C94, 0x6C98, 0x6C82, 0x7076, 0x707C, 0x707D, 0x7078, 0x7262, 0x7261, 0x7260, 0x72C4, 0x72C2, 0x7396, 0x752C, 0x752B, 0x7537, 0x7538, 0x7682, 0x76EF, 0x77E3, 0x79C1, 0x79C0, 0x79BF, 0x7A76, 0x7CFB, 0x7F55, 0x8096, 0x8093, 0x809D, 0x8098, 0x809B, 0x809A, 0x80B2, 0x826F, 0x8292, 0x828B, 0x828D, 0x898B, 0x89D2, 0x8A00, 0x8C37, 0x8C46, 0x8C55, 0x8C9D, 0x8D64, 0x8D70, 0x8DB3, 0x8EAB, 0x8ECA, 0x8F9B, 0x8FB0, 0x8FC2, 0x8FC6, 0x8FC5, 0x8FC4, 0x5DE1, 0x9091, 0x90A2, 0x90AA, 0x90A6, 0x90A3, 0x9149, 0x91C6, 0x91CC, 0x9632, 0x962E, 0x9631, 0x962A, 0x962C, 0x4E26, 0x4E56, 0x4E73, 0x4E8B, 0x4E9B, 0x4E9E, 0x4EAB, 0x4EAC, 0x4F6F, 0x4F9D, 0x4F8D, 0x4F73, 0x4F7F, 0x4F6C, 0x4F9B, 0x4F8B, 0x4F86, 0x4F83, 0x4F70, 0x4F75, 0x4F88, 0x4F69, 0x4F7B, 0x4F96, 0x4F7E, 0x4F8F, 0x4F91, 0x4F7A, 0x5154, 0x5152, 0x5155, 0x5169, 0x5177, 0x5176, 0x5178, 0x51BD, 0x51FD, 0x523B, 0x5238, 0x5237, 0x523A, 0x5230, 0x522E, 0x5236, 0x5241, 0x52BE, 0x52BB, 0x5352, 0x5354, 0x5353, 0x5351, 0x5366, 0x5377, 0x5378, 0x5379, 0x53D6, 0x53D4, 0x53D7, 0x5473, 0x5475, + 0x5496, 0x5478, 0x5495, 0x5480, 0x547B, 0x5477, 0x5484, 0x5492, 0x5486, 0x547C, 0x5490, 0x5471, 0x5476, 0x548C, 0x549A, 0x5462, 0x5468, 0x548B, 0x547D, 0x548E, 0x56FA, 0x5783, 0x5777, 0x576A, 0x5769, 0x5761, 0x5766, 0x5764, 0x577C, 0x591C, 0x5949, 0x5947, 0x5948, 0x5944, 0x5954, 0x59BE, 0x59BB, 0x59D4, 0x59B9, 0x59AE, 0x59D1, 0x59C6, 0x59D0, 0x59CD, 0x59CB, 0x59D3, 0x59CA, 0x59AF, 0x59B3, 0x59D2, 0x59C5, 0x5B5F, 0x5B64, 0x5B63, 0x5B97, 0x5B9A, 0x5B98, 0x5B9C, 0x5B99, 0x5B9B, 0x5C1A, 0x5C48, 0x5C45, 0x5C46, 0x5CB7, 0x5CA1, 0x5CB8, 0x5CA9, 0x5CAB, 0x5CB1, 0x5CB3, 0x5E18, 0x5E1A, 0x5E16, 0x5E15, 0x5E1B, 0x5E11, 0x5E78, 0x5E9A, 0x5E97, 0x5E9C, 0x5E95, 0x5E96, 0x5EF6, 0x5F26, 0x5F27, 0x5F29, 0x5F80, 0x5F81, 0x5F7F, 0x5F7C, 0x5FDD, 0x5FE0, 0x5FFD, 0x5FF5, 0x5FFF, 0x600F, 0x6014, 0x602F, 0x6035, 0x6016, 0x602A, 0x6015, 0x6021, 0x6027, 0x6029, 0x602B, 0x601B, 0x6216, 0x6215, 0x623F, 0x623E, 0x6240, 0x627F, 0x62C9, 0x62CC, 0x62C4, 0x62BF, 0x62C2, 0x62B9, 0x62D2, 0x62DB, 0x62AB, 0x62D3, 0x62D4, 0x62CB, 0x62C8, 0x62A8, 0x62BD, 0x62BC, 0x62D0, 0x62D9, 0x62C7, 0x62CD, 0x62B5, 0x62DA, 0x62B1, 0x62D8, 0x62D6, 0x62D7, 0x62C6, 0x62AC, 0x62CE, 0x653E, 0x65A7, 0x65BC, 0x65FA, 0x6614, 0x6613, 0x660C, 0x6606, 0x6602, 0x660E, 0x6600, 0x660F, 0x6615, 0x660A, + 0x6607, 0x670D, 0x670B, 0x676D, 0x678B, 0x6795, 0x6771, 0x679C, 0x6773, 0x6777, 0x6787, 0x679D, 0x6797, 0x676F, 0x6770, 0x677F, 0x6789, 0x677E, 0x6790, 0x6775, 0x679A, 0x6793, 0x677C, 0x676A, 0x6772, 0x6B23, 0x6B66, 0x6B67, 0x6B7F, 0x6C13, 0x6C1B, 0x6CE3, 0x6CE8, 0x6CF3, 0x6CB1, 0x6CCC, 0x6CE5, 0x6CB3, 0x6CBD, 0x6CBE, 0x6CBC, 0x6CE2, 0x6CAB, 0x6CD5, 0x6CD3, 0x6CB8, 0x6CC4, 0x6CB9, 0x6CC1, 0x6CAE, 0x6CD7, 0x6CC5, 0x6CF1, 0x6CBF, 0x6CBB, 0x6CE1, 0x6CDB, 0x6CCA, 0x6CAC, 0x6CEF, 0x6CDC, 0x6CD6, 0x6CE0, 0x7095, 0x708E, 0x7092, 0x708A, 0x7099, 0x722C, 0x722D, 0x7238, 0x7248, 0x7267, 0x7269, 0x72C0, 0x72CE, 0x72D9, 0x72D7, 0x72D0, 0x73A9, 0x73A8, 0x739F, 0x73AB, 0x73A5, 0x753D, 0x759D, 0x7599, 0x759A, 0x7684, 0x76C2, 0x76F2, 0x76F4, 0x77E5, 0x77FD, 0x793E, 0x7940, 0x7941, 0x79C9, 0x79C8, 0x7A7A, 0x7A79, 0x7AFA, 0x7CFE, 0x7F54, 0x7F8C, 0x7F8B, 0x8005, 0x80BA, 0x80A5, 0x80A2, 0x80B1, 0x80A1, 0x80AB, 0x80A9, 0x80B4, 0x80AA, 0x80AF, 0x81E5, 0x81FE, 0x820D, 0x82B3, 0x829D, 0x8299, 0x82AD, 0x82BD, 0x829F, 0x82B9, 0x82B1, 0x82AC, 0x82A5, 0x82AF, 0x82B8, 0x82A3, 0x82B0, 0x82BE, 0x82B7, 0x864E, 0x8671, 0x521D, 0x8868, 0x8ECB, 0x8FCE, 0x8FD4, 0x8FD1, 0x90B5, 0x90B8, 0x90B1, 0x90B6, 0x91C7, 0x91D1, 0x9577, 0x9580, 0x961C, 0x9640, 0x963F, 0x963B, 0x9644, + 0x9642, 0x96B9, 0x96E8, 0x9752, 0x975E, 0x4E9F, 0x4EAD, 0x4EAE, 0x4FE1, 0x4FB5, 0x4FAF, 0x4FBF, 0x4FE0, 0x4FD1, 0x4FCF, 0x4FDD, 0x4FC3, 0x4FB6, 0x4FD8, 0x4FDF, 0x4FCA, 0x4FD7, 0x4FAE, 0x4FD0, 0x4FC4, 0x4FC2, 0x4FDA, 0x4FCE, 0x4FDE, 0x4FB7, 0x5157, 0x5192, 0x5191, 0x51A0, 0x524E, 0x5243, 0x524A, 0x524D, 0x524C, 0x524B, 0x5247, 0x52C7, 0x52C9, 0x52C3, 0x52C1, 0x530D, 0x5357, 0x537B, 0x539A, 0x53DB, 0x54AC, 0x54C0, 0x54A8, 0x54CE, 0x54C9, 0x54B8, 0x54A6, 0x54B3, 0x54C7, 0x54C2, 0x54BD, 0x54AA, 0x54C1, 0x54C4, 0x54C8, 0x54AF, 0x54AB, 0x54B1, 0x54BB, 0x54A9, 0x54A7, 0x54BF, 0x56FF, 0x5782, 0x578B, 0x57A0, 0x57A3, 0x57A2, 0x57CE, 0x57AE, 0x5793, 0x5955, 0x5951, 0x594F, 0x594E, 0x5950, 0x59DC, 0x59D8, 0x59FF, 0x59E3, 0x59E8, 0x5A03, 0x59E5, 0x59EA, 0x59DA, 0x59E6, 0x5A01, 0x59FB, 0x5B69, 0x5BA3, 0x5BA6, 0x5BA4, 0x5BA2, 0x5BA5, 0x5C01, 0x5C4E, 0x5C4F, 0x5C4D, 0x5C4B, 0x5CD9, 0x5CD2, 0x5DF7, 0x5E1D, 0x5E25, 0x5E1F, 0x5E7D, 0x5EA0, 0x5EA6, 0x5EFA, 0x5F08, 0x5F2D, 0x5F65, 0x5F88, 0x5F85, 0x5F8A, 0x5F8B, 0x5F87, 0x5F8C, 0x5F89, 0x6012, 0x601D, 0x6020, 0x6025, 0x600E, 0x6028, 0x604D, 0x6070, 0x6068, 0x6062, 0x6046, 0x6043, 0x606C, 0x606B, 0x606A, 0x6064, 0x6241, 0x62DC, 0x6316, 0x6309, 0x62FC, 0x62ED, 0x6301, 0x62EE, 0x62FD, 0x6307, 0x62F1, 0x62F7, + 0x62EF, 0x62EC, 0x62FE, 0x62F4, 0x6311, 0x6302, 0x653F, 0x6545, 0x65AB, 0x65BD, 0x65E2, 0x6625, 0x662D, 0x6620, 0x6627, 0x662F, 0x661F, 0x6628, 0x6631, 0x6624, 0x66F7, 0x67FF, 0x67D3, 0x67F1, 0x67D4, 0x67D0, 0x67EC, 0x67B6, 0x67AF, 0x67F5, 0x67E9, 0x67EF, 0x67C4, 0x67D1, 0x67B4, 0x67DA, 0x67E5, 0x67B8, 0x67CF, 0x67DE, 0x67F3, 0x67B0, 0x67D9, 0x67E2, 0x67DD, 0x67D2, 0x6B6A, 0x6B83, 0x6B86, 0x6BB5, 0x6BD2, 0x6BD7, 0x6C1F, 0x6CC9, 0x6D0B, 0x6D32, 0x6D2A, 0x6D41, 0x6D25, 0x6D0C, 0x6D31, 0x6D1E, 0x6D17, 0x6D3B, 0x6D3D, 0x6D3E, 0x6D36, 0x6D1B, 0x6CF5, 0x6D39, 0x6D27, 0x6D38, 0x6D29, 0x6D2E, 0x6D35, 0x6D0E, 0x6D2B, 0x70AB, 0x70BA, 0x70B3, 0x70AC, 0x70AF, 0x70AD, 0x70B8, 0x70AE, 0x70A4, 0x7230, 0x7272, 0x726F, 0x7274, 0x72E9, 0x72E0, 0x72E1, 0x73B7, 0x73CA, 0x73BB, 0x73B2, 0x73CD, 0x73C0, 0x73B3, 0x751A, 0x752D, 0x754F, 0x754C, 0x754E, 0x754B, 0x75AB, 0x75A4, 0x75A5, 0x75A2, 0x75A3, 0x7678, 0x7686, 0x7687, 0x7688, 0x76C8, 0x76C6, 0x76C3, 0x76C5, 0x7701, 0x76F9, 0x76F8, 0x7709, 0x770B, 0x76FE, 0x76FC, 0x7707, 0x77DC, 0x7802, 0x7814, 0x780C, 0x780D, 0x7946, 0x7949, 0x7948, 0x7947, 0x79B9, 0x79BA, 0x79D1, 0x79D2, 0x79CB, 0x7A7F, 0x7A81, 0x7AFF, 0x7AFD, 0x7C7D, 0x7D02, 0x7D05, 0x7D00, 0x7D09, 0x7D07, 0x7D04, 0x7D06, 0x7F38, 0x7F8E, 0x7FBF, 0x8004, + 0x8010, 0x800D, 0x8011, 0x8036, 0x80D6, 0x80E5, 0x80DA, 0x80C3, 0x80C4, 0x80CC, 0x80E1, 0x80DB, 0x80CE, 0x80DE, 0x80E4, 0x80DD, 0x81F4, 0x8222, 0x82E7, 0x8303, 0x8305, 0x82E3, 0x82DB, 0x82E6, 0x8304, 0x82E5, 0x8302, 0x8309, 0x82D2, 0x82D7, 0x82F1, 0x8301, 0x82DC, 0x82D4, 0x82D1, 0x82DE, 0x82D3, 0x82DF, 0x82EF, 0x8306, 0x8650, 0x8679, 0x867B, 0x867A, 0x884D, 0x886B, 0x8981, 0x89D4, 0x8A08, 0x8A02, 0x8A03, 0x8C9E, 0x8CA0, 0x8D74, 0x8D73, 0x8DB4, 0x8ECD, 0x8ECC, 0x8FF0, 0x8FE6, 0x8FE2, 0x8FEA, 0x8FE5, 0x8FED, 0x8FEB, 0x8FE4, 0x8FE8, 0x90CA, 0x90CE, 0x90C1, 0x90C3, 0x914B, 0x914A, 0x91CD, 0x9582, 0x9650, 0x964B, 0x964C, 0x964D, 0x9762, 0x9769, 0x97CB, 0x97ED, 0x97F3, 0x9801, 0x98A8, 0x98DB, 0x98DF, 0x9996, 0x9999, 0x4E58, 0x4EB3, 0x500C, 0x500D, 0x5023, 0x4FEF, 0x5026, 0x5025, 0x4FF8, 0x5029, 0x5016, 0x5006, 0x503C, 0x501F, 0x501A, 0x5012, 0x5011, 0x4FFA, 0x5000, 0x5014, 0x5028, 0x4FF1, 0x5021, 0x500B, 0x5019, 0x5018, 0x4FF3, 0x4FEE, 0x502D, 0x502A, 0x4FFE, 0x502B, 0x5009, 0x517C, 0x51A4, 0x51A5, 0x51A2, 0x51CD, 0x51CC, 0x51C6, 0x51CB, 0x5256, 0x525C, 0x5254, 0x525B, 0x525D, 0x532A, 0x537F, 0x539F, 0x539D, 0x53DF, 0x54E8, 0x5510, 0x5501, 0x5537, 0x54FC, 0x54E5, 0x54F2, 0x5506, 0x54FA, 0x5514, 0x54E9, 0x54ED, 0x54E1, 0x5509, 0x54EE, 0x54EA, + 0x54E6, 0x5527, 0x5507, 0x54FD, 0x550F, 0x5703, 0x5704, 0x57C2, 0x57D4, 0x57CB, 0x57C3, 0x5809, 0x590F, 0x5957, 0x5958, 0x595A, 0x5A11, 0x5A18, 0x5A1C, 0x5A1F, 0x5A1B, 0x5A13, 0x59EC, 0x5A20, 0x5A23, 0x5A29, 0x5A25, 0x5A0C, 0x5A09, 0x5B6B, 0x5C58, 0x5BB0, 0x5BB3, 0x5BB6, 0x5BB4, 0x5BAE, 0x5BB5, 0x5BB9, 0x5BB8, 0x5C04, 0x5C51, 0x5C55, 0x5C50, 0x5CED, 0x5CFD, 0x5CFB, 0x5CEA, 0x5CE8, 0x5CF0, 0x5CF6, 0x5D01, 0x5CF4, 0x5DEE, 0x5E2D, 0x5E2B, 0x5EAB, 0x5EAD, 0x5EA7, 0x5F31, 0x5F92, 0x5F91, 0x5F90, 0x6059, 0x6063, 0x6065, 0x6050, 0x6055, 0x606D, 0x6069, 0x606F, 0x6084, 0x609F, 0x609A, 0x608D, 0x6094, 0x608C, 0x6085, 0x6096, 0x6247, 0x62F3, 0x6308, 0x62FF, 0x634E, 0x633E, 0x632F, 0x6355, 0x6342, 0x6346, 0x634F, 0x6349, 0x633A, 0x6350, 0x633D, 0x632A, 0x632B, 0x6328, 0x634D, 0x634C, 0x6548, 0x6549, 0x6599, 0x65C1, 0x65C5, 0x6642, 0x6649, 0x664F, 0x6643, 0x6652, 0x664C, 0x6645, 0x6641, 0x66F8, 0x6714, 0x6715, 0x6717, 0x6821, 0x6838, 0x6848, 0x6846, 0x6853, 0x6839, 0x6842, 0x6854, 0x6829, 0x68B3, 0x6817, 0x684C, 0x6851, 0x683D, 0x67F4, 0x6850, 0x6840, 0x683C, 0x6843, 0x682A, 0x6845, 0x6813, 0x6818, 0x6841, 0x6B8A, 0x6B89, 0x6BB7, 0x6C23, 0x6C27, 0x6C28, 0x6C26, 0x6C24, 0x6CF0, 0x6D6A, 0x6D95, 0x6D88, 0x6D87, 0x6D66, 0x6D78, 0x6D77, 0x6D59, 0x6D93, + 0x6D6C, 0x6D89, 0x6D6E, 0x6D5A, 0x6D74, 0x6D69, 0x6D8C, 0x6D8A, 0x6D79, 0x6D85, 0x6D65, 0x6D94, 0x70CA, 0x70D8, 0x70E4, 0x70D9, 0x70C8, 0x70CF, 0x7239, 0x7279, 0x72FC, 0x72F9, 0x72FD, 0x72F8, 0x72F7, 0x7386, 0x73ED, 0x7409, 0x73EE, 0x73E0, 0x73EA, 0x73DE, 0x7554, 0x755D, 0x755C, 0x755A, 0x7559, 0x75BE, 0x75C5, 0x75C7, 0x75B2, 0x75B3, 0x75BD, 0x75BC, 0x75B9, 0x75C2, 0x75B8, 0x768B, 0x76B0, 0x76CA, 0x76CD, 0x76CE, 0x7729, 0x771F, 0x7720, 0x7728, 0x77E9, 0x7830, 0x7827, 0x7838, 0x781D, 0x7834, 0x7837, 0x7825, 0x782D, 0x7820, 0x781F, 0x7832, 0x7955, 0x7950, 0x7960, 0x795F, 0x7956, 0x795E, 0x795D, 0x7957, 0x795A, 0x79E4, 0x79E3, 0x79E7, 0x79DF, 0x79E6, 0x79E9, 0x79D8, 0x7A84, 0x7A88, 0x7AD9, 0x7B06, 0x7B11, 0x7C89, 0x7D21, 0x7D17, 0x7D0B, 0x7D0A, 0x7D20, 0x7D22, 0x7D14, 0x7D10, 0x7D15, 0x7D1A, 0x7D1C, 0x7D0D, 0x7D19, 0x7D1B, 0x7F3A, 0x7F5F, 0x7F94, 0x7FC5, 0x7FC1, 0x8006, 0x8018, 0x8015, 0x8019, 0x8017, 0x803D, 0x803F, 0x80F1, 0x8102, 0x80F0, 0x8105, 0x80ED, 0x80F4, 0x8106, 0x80F8, 0x80F3, 0x8108, 0x80FD, 0x810A, 0x80FC, 0x80EF, 0x81ED, 0x81EC, 0x8200, 0x8210, 0x822A, 0x822B, 0x8228, 0x822C, 0x82BB, 0x832B, 0x8352, 0x8354, 0x834A, 0x8338, 0x8350, 0x8349, 0x8335, 0x8334, 0x834F, 0x8332, 0x8339, 0x8336, 0x8317, 0x8340, 0x8331, 0x8328, 0x8343, + 0x8654, 0x868A, 0x86AA, 0x8693, 0x86A4, 0x86A9, 0x868C, 0x86A3, 0x869C, 0x8870, 0x8877, 0x8881, 0x8882, 0x887D, 0x8879, 0x8A18, 0x8A10, 0x8A0E, 0x8A0C, 0x8A15, 0x8A0A, 0x8A17, 0x8A13, 0x8A16, 0x8A0F, 0x8A11, 0x8C48, 0x8C7A, 0x8C79, 0x8CA1, 0x8CA2, 0x8D77, 0x8EAC, 0x8ED2, 0x8ED4, 0x8ECF, 0x8FB1, 0x9001, 0x9006, 0x8FF7, 0x9000, 0x8FFA, 0x8FF4, 0x9003, 0x8FFD, 0x9005, 0x8FF8, 0x9095, 0x90E1, 0x90DD, 0x90E2, 0x9152, 0x914D, 0x914C, 0x91D8, 0x91DD, 0x91D7, 0x91DC, 0x91D9, 0x9583, 0x9662, 0x9663, 0x9661, 0x965B, 0x965D, 0x9664, 0x9658, 0x965E, 0x96BB, 0x98E2, 0x99AC, 0x9AA8, 0x9AD8, 0x9B25, 0x9B32, 0x9B3C, 0x4E7E, 0x507A, 0x507D, 0x505C, 0x5047, 0x5043, 0x504C, 0x505A, 0x5049, 0x5065, 0x5076, 0x504E, 0x5055, 0x5075, 0x5074, 0x5077, 0x504F, 0x500F, 0x506F, 0x506D, 0x515C, 0x5195, 0x51F0, 0x526A, 0x526F, 0x52D2, 0x52D9, 0x52D8, 0x52D5, 0x5310, 0x530F, 0x5319, 0x533F, 0x5340, 0x533E, 0x53C3, 0x66FC, 0x5546, 0x556A, 0x5566, 0x5544, 0x555E, 0x5561, 0x5543, 0x554A, 0x5531, 0x5556, 0x554F, 0x5555, 0x552F, 0x5564, 0x5538, 0x552E, 0x555C, 0x552C, 0x5563, 0x5533, 0x5541, 0x5557, 0x5708, 0x570B, 0x5709, 0x57DF, 0x5805, 0x580A, 0x5806, 0x57E0, 0x57E4, 0x57FA, 0x5802, 0x5835, 0x57F7, 0x57F9, 0x5920, 0x5962, 0x5A36, 0x5A41, 0x5A49, 0x5A66, 0x5A6A, 0x5A40, + 0x5A3C, 0x5A62, 0x5A5A, 0x5A46, 0x5A4A, 0x5B70, 0x5BC7, 0x5BC5, 0x5BC4, 0x5BC2, 0x5BBF, 0x5BC6, 0x5C09, 0x5C08, 0x5C07, 0x5C60, 0x5C5C, 0x5C5D, 0x5D07, 0x5D06, 0x5D0E, 0x5D1B, 0x5D16, 0x5D22, 0x5D11, 0x5D29, 0x5D14, 0x5D19, 0x5D24, 0x5D27, 0x5D17, 0x5DE2, 0x5E38, 0x5E36, 0x5E33, 0x5E37, 0x5EB7, 0x5EB8, 0x5EB6, 0x5EB5, 0x5EBE, 0x5F35, 0x5F37, 0x5F57, 0x5F6C, 0x5F69, 0x5F6B, 0x5F97, 0x5F99, 0x5F9E, 0x5F98, 0x5FA1, 0x5FA0, 0x5F9C, 0x607F, 0x60A3, 0x6089, 0x60A0, 0x60A8, 0x60CB, 0x60B4, 0x60E6, 0x60BD, 0x60C5, 0x60BB, 0x60B5, 0x60DC, 0x60BC, 0x60D8, 0x60D5, 0x60C6, 0x60DF, 0x60B8, 0x60DA, 0x60C7, 0x621A, 0x621B, 0x6248, 0x63A0, 0x63A7, 0x6372, 0x6396, 0x63A2, 0x63A5, 0x6377, 0x6367, 0x6398, 0x63AA, 0x6371, 0x63A9, 0x6389, 0x6383, 0x639B, 0x636B, 0x63A8, 0x6384, 0x6388, 0x6399, 0x63A1, 0x63AC, 0x6392, 0x638F, 0x6380, 0x637B, 0x6369, 0x6368, 0x637A, 0x655D, 0x6556, 0x6551, 0x6559, 0x6557, 0x555F, 0x654F, 0x6558, 0x6555, 0x6554, 0x659C, 0x659B, 0x65AC, 0x65CF, 0x65CB, 0x65CC, 0x65CE, 0x665D, 0x665A, 0x6664, 0x6668, 0x6666, 0x665E, 0x66F9, 0x52D7, 0x671B, 0x6881, 0x68AF, 0x68A2, 0x6893, 0x68B5, 0x687F, 0x6876, 0x68B1, 0x68A7, 0x6897, 0x68B0, 0x6883, 0x68C4, 0x68AD, 0x6886, 0x6885, 0x6894, 0x689D, 0x68A8, 0x689F, 0x68A1, 0x6882, 0x6B32, 0x6BBA, + 0x6BEB, 0x6BEC, 0x6C2B, 0x6D8E, 0x6DBC, 0x6DF3, 0x6DD9, 0x6DB2, 0x6DE1, 0x6DCC, 0x6DE4, 0x6DFB, 0x6DFA, 0x6E05, 0x6DC7, 0x6DCB, 0x6DAF, 0x6DD1, 0x6DAE, 0x6DDE, 0x6DF9, 0x6DB8, 0x6DF7, 0x6DF5, 0x6DC5, 0x6DD2, 0x6E1A, 0x6DB5, 0x6DDA, 0x6DEB, 0x6DD8, 0x6DEA, 0x6DF1, 0x6DEE, 0x6DE8, 0x6DC6, 0x6DC4, 0x6DAA, 0x6DEC, 0x6DBF, 0x6DE6, 0x70F9, 0x7109, 0x710A, 0x70FD, 0x70EF, 0x723D, 0x727D, 0x7281, 0x731C, 0x731B, 0x7316, 0x7313, 0x7319, 0x7387, 0x7405, 0x740A, 0x7403, 0x7406, 0x73FE, 0x740D, 0x74E0, 0x74F6, 0x74F7, 0x751C, 0x7522, 0x7565, 0x7566, 0x7562, 0x7570, 0x758F, 0x75D4, 0x75D5, 0x75B5, 0x75CA, 0x75CD, 0x768E, 0x76D4, 0x76D2, 0x76DB, 0x7737, 0x773E, 0x773C, 0x7736, 0x7738, 0x773A, 0x786B, 0x7843, 0x784E, 0x7965, 0x7968, 0x796D, 0x79FB, 0x7A92, 0x7A95, 0x7B20, 0x7B28, 0x7B1B, 0x7B2C, 0x7B26, 0x7B19, 0x7B1E, 0x7B2E, 0x7C92, 0x7C97, 0x7C95, 0x7D46, 0x7D43, 0x7D71, 0x7D2E, 0x7D39, 0x7D3C, 0x7D40, 0x7D30, 0x7D33, 0x7D44, 0x7D2F, 0x7D42, 0x7D32, 0x7D31, 0x7F3D, 0x7F9E, 0x7F9A, 0x7FCC, 0x7FCE, 0x7FD2, 0x801C, 0x804A, 0x8046, 0x812F, 0x8116, 0x8123, 0x812B, 0x8129, 0x8130, 0x8124, 0x8202, 0x8235, 0x8237, 0x8236, 0x8239, 0x838E, 0x839E, 0x8398, 0x8378, 0x83A2, 0x8396, 0x83BD, 0x83AB, 0x8392, 0x838A, 0x8393, 0x8389, 0x83A0, 0x8377, 0x837B, 0x837C, + 0x8386, 0x83A7, 0x8655, 0x5F6A, 0x86C7, 0x86C0, 0x86B6, 0x86C4, 0x86B5, 0x86C6, 0x86CB, 0x86B1, 0x86AF, 0x86C9, 0x8853, 0x889E, 0x8888, 0x88AB, 0x8892, 0x8896, 0x888D, 0x888B, 0x8993, 0x898F, 0x8A2A, 0x8A1D, 0x8A23, 0x8A25, 0x8A31, 0x8A2D, 0x8A1F, 0x8A1B, 0x8A22, 0x8C49, 0x8C5A, 0x8CA9, 0x8CAC, 0x8CAB, 0x8CA8, 0x8CAA, 0x8CA7, 0x8D67, 0x8D66, 0x8DBE, 0x8DBA, 0x8EDB, 0x8EDF, 0x9019, 0x900D, 0x901A, 0x9017, 0x9023, 0x901F, 0x901D, 0x9010, 0x9015, 0x901E, 0x9020, 0x900F, 0x9022, 0x9016, 0x901B, 0x9014, 0x90E8, 0x90ED, 0x90FD, 0x9157, 0x91CE, 0x91F5, 0x91E6, 0x91E3, 0x91E7, 0x91ED, 0x91E9, 0x9589, 0x966A, 0x9675, 0x9673, 0x9678, 0x9670, 0x9674, 0x9676, 0x9677, 0x966C, 0x96C0, 0x96EA, 0x96E9, 0x7AE0, 0x7ADF, 0x9802, 0x9803, 0x9B5A, 0x9CE5, 0x9E75, 0x9E7F, 0x9EA5, 0x9EBB, 0x50A2, 0x508D, 0x5085, 0x5099, 0x5091, 0x5080, 0x5096, 0x5098, 0x509A, 0x6700, 0x51F1, 0x5272, 0x5274, 0x5275, 0x5269, 0x52DE, 0x52DD, 0x52DB, 0x535A, 0x53A5, 0x557B, 0x5580, 0x55A7, 0x557C, 0x558A, 0x559D, 0x5598, 0x5582, 0x559C, 0x55AA, 0x5594, 0x5587, 0x558B, 0x5583, 0x55B3, 0x55AE, 0x559F, 0x553E, 0x55B2, 0x559A, 0x55BB, 0x55AC, 0x55B1, 0x557E, 0x5589, 0x55AB, 0x5599, 0x570D, 0x582F, 0x582A, 0x5834, 0x5824, 0x5830, 0x5831, 0x5821, 0x581D, 0x5820, 0x58F9, 0x58FA, 0x5960, + 0x5A77, 0x5A9A, 0x5A7F, 0x5A92, 0x5A9B, 0x5AA7, 0x5B73, 0x5B71, 0x5BD2, 0x5BCC, 0x5BD3, 0x5BD0, 0x5C0A, 0x5C0B, 0x5C31, 0x5D4C, 0x5D50, 0x5D34, 0x5D47, 0x5DFD, 0x5E45, 0x5E3D, 0x5E40, 0x5E43, 0x5E7E, 0x5ECA, 0x5EC1, 0x5EC2, 0x5EC4, 0x5F3C, 0x5F6D, 0x5FA9, 0x5FAA, 0x5FA8, 0x60D1, 0x60E1, 0x60B2, 0x60B6, 0x60E0, 0x611C, 0x6123, 0x60FA, 0x6115, 0x60F0, 0x60FB, 0x60F4, 0x6168, 0x60F1, 0x610E, 0x60F6, 0x6109, 0x6100, 0x6112, 0x621F, 0x6249, 0x63A3, 0x638C, 0x63CF, 0x63C0, 0x63E9, 0x63C9, 0x63C6, 0x63CD, 0x63D2, 0x63E3, 0x63D0, 0x63E1, 0x63D6, 0x63ED, 0x63EE, 0x6376, 0x63F4, 0x63EA, 0x63DB, 0x6452, 0x63DA, 0x63F9, 0x655E, 0x6566, 0x6562, 0x6563, 0x6591, 0x6590, 0x65AF, 0x666E, 0x6670, 0x6674, 0x6676, 0x666F, 0x6691, 0x667A, 0x667E, 0x6677, 0x66FE, 0x66FF, 0x671F, 0x671D, 0x68FA, 0x68D5, 0x68E0, 0x68D8, 0x68D7, 0x6905, 0x68DF, 0x68F5, 0x68EE, 0x68E7, 0x68F9, 0x68D2, 0x68F2, 0x68E3, 0x68CB, 0x68CD, 0x690D, 0x6912, 0x690E, 0x68C9, 0x68DA, 0x696E, 0x68FB, 0x6B3E, 0x6B3A, 0x6B3D, 0x6B98, 0x6B96, 0x6BBC, 0x6BEF, 0x6C2E, 0x6C2F, 0x6C2C, 0x6E2F, 0x6E38, 0x6E54, 0x6E21, 0x6E32, 0x6E67, 0x6E4A, 0x6E20, 0x6E25, 0x6E23, 0x6E1B, 0x6E5B, 0x6E58, 0x6E24, 0x6E56, 0x6E6E, 0x6E2D, 0x6E26, 0x6E6F, 0x6E34, 0x6E4D, 0x6E3A, 0x6E2C, 0x6E43, 0x6E1D, 0x6E3E, 0x6ECB, + 0x6E89, 0x6E19, 0x6E4E, 0x6E63, 0x6E44, 0x6E72, 0x6E69, 0x6E5F, 0x7119, 0x711A, 0x7126, 0x7130, 0x7121, 0x7136, 0x716E, 0x711C, 0x724C, 0x7284, 0x7280, 0x7336, 0x7325, 0x7334, 0x7329, 0x743A, 0x742A, 0x7433, 0x7422, 0x7425, 0x7435, 0x7436, 0x7434, 0x742F, 0x741B, 0x7426, 0x7428, 0x7525, 0x7526, 0x756B, 0x756A, 0x75E2, 0x75DB, 0x75E3, 0x75D9, 0x75D8, 0x75DE, 0x75E0, 0x767B, 0x767C, 0x7696, 0x7693, 0x76B4, 0x76DC, 0x774F, 0x77ED, 0x785D, 0x786C, 0x786F, 0x7A0D, 0x7A08, 0x7A0B, 0x7A05, 0x7A00, 0x7A98, 0x7A97, 0x7A96, 0x7AE5, 0x7AE3, 0x7B49, 0x7B56, 0x7B46, 0x7B50, 0x7B52, 0x7B54, 0x7B4D, 0x7B4B, 0x7B4F, 0x7B51, 0x7C9F, 0x7CA5, 0x7D5E, 0x7D50, 0x7D68, 0x7D55, 0x7D2B, 0x7D6E, 0x7D72, 0x7D61, 0x7D66, 0x7D62, 0x7D70, 0x7D73, 0x5584, 0x7FD4, 0x7FD5, 0x800B, 0x8052, 0x8085, 0x8155, 0x8154, 0x814B, 0x8151, 0x814E, 0x8139, 0x8146, 0x813E, 0x814C, 0x8153, 0x8174, 0x8212, 0x821C, 0x83E9, 0x8403, 0x83F8, 0x840D, 0x83E0, 0x83C5, 0x840B, 0x83C1, 0x83EF, 0x83F1, 0x83F4, 0x8457, 0x840A, 0x83F0, 0x840C, 0x83CC, 0x83FD, 0x83F2, 0x83CA, 0x8438, 0x840E, 0x8404, 0x83DC, 0x8407, 0x83D4, 0x83DF, 0x865B, 0x86DF, 0x86D9, 0x86ED, 0x86D4, 0x86DB, 0x86E4, 0x86D0, 0x86DE, 0x8857, 0x88C1, 0x88C2, 0x88B1, 0x8983, 0x8996, 0x8A3B, 0x8A60, 0x8A55, 0x8A5E, 0x8A3C, 0x8A41, + 0x8A54, 0x8A5B, 0x8A50, 0x8A46, 0x8A34, 0x8A3A, 0x8A36, 0x8A56, 0x8C61, 0x8C82, 0x8CAF, 0x8CBC, 0x8CB3, 0x8CBD, 0x8CC1, 0x8CBB, 0x8CC0, 0x8CB4, 0x8CB7, 0x8CB6, 0x8CBF, 0x8CB8, 0x8D8A, 0x8D85, 0x8D81, 0x8DCE, 0x8DDD, 0x8DCB, 0x8DDA, 0x8DD1, 0x8DCC, 0x8DDB, 0x8DC6, 0x8EFB, 0x8EF8, 0x8EFC, 0x8F9C, 0x902E, 0x9035, 0x9031, 0x9038, 0x9032, 0x9036, 0x9102, 0x90F5, 0x9109, 0x90FE, 0x9163, 0x9165, 0x91CF, 0x9214, 0x9215, 0x9223, 0x9209, 0x921E, 0x920D, 0x9210, 0x9207, 0x9211, 0x9594, 0x958F, 0x958B, 0x9591, 0x9593, 0x9592, 0x958E, 0x968A, 0x968E, 0x968B, 0x967D, 0x9685, 0x9686, 0x968D, 0x9672, 0x9684, 0x96C1, 0x96C5, 0x96C4, 0x96C6, 0x96C7, 0x96EF, 0x96F2, 0x97CC, 0x9805, 0x9806, 0x9808, 0x98E7, 0x98EA, 0x98EF, 0x98E9, 0x98F2, 0x98ED, 0x99AE, 0x99AD, 0x9EC3, 0x9ECD, 0x9ED1, 0x4E82, 0x50AD, 0x50B5, 0x50B2, 0x50B3, 0x50C5, 0x50BE, 0x50AC, 0x50B7, 0x50BB, 0x50AF, 0x50C7, 0x527F, 0x5277, 0x527D, 0x52DF, 0x52E6, 0x52E4, 0x52E2, 0x52E3, 0x532F, 0x55DF, 0x55E8, 0x55D3, 0x55E6, 0x55CE, 0x55DC, 0x55C7, 0x55D1, 0x55E3, 0x55E4, 0x55EF, 0x55DA, 0x55E1, 0x55C5, 0x55C6, 0x55E5, 0x55C9, 0x5712, 0x5713, 0x585E, 0x5851, 0x5858, 0x5857, 0x585A, 0x5854, 0x586B, 0x584C, 0x586D, 0x584A, 0x5862, 0x5852, 0x584B, 0x5967, 0x5AC1, 0x5AC9, 0x5ACC, 0x5ABE, 0x5ABD, 0x5ABC, + 0x5AB3, 0x5AC2, 0x5AB2, 0x5D69, 0x5D6F, 0x5E4C, 0x5E79, 0x5EC9, 0x5EC8, 0x5F12, 0x5F59, 0x5FAC, 0x5FAE, 0x611A, 0x610F, 0x6148, 0x611F, 0x60F3, 0x611B, 0x60F9, 0x6101, 0x6108, 0x614E, 0x614C, 0x6144, 0x614D, 0x613E, 0x6134, 0x6127, 0x610D, 0x6106, 0x6137, 0x6221, 0x6222, 0x6413, 0x643E, 0x641E, 0x642A, 0x642D, 0x643D, 0x642C, 0x640F, 0x641C, 0x6414, 0x640D, 0x6436, 0x6416, 0x6417, 0x6406, 0x656C, 0x659F, 0x65B0, 0x6697, 0x6689, 0x6687, 0x6688, 0x6696, 0x6684, 0x6698, 0x668D, 0x6703, 0x6994, 0x696D, 0x695A, 0x6977, 0x6960, 0x6954, 0x6975, 0x6930, 0x6982, 0x694A, 0x6968, 0x696B, 0x695E, 0x6953, 0x6979, 0x6986, 0x695D, 0x6963, 0x695B, 0x6B47, 0x6B72, 0x6BC0, 0x6BBF, 0x6BD3, 0x6BFD, 0x6EA2, 0x6EAF, 0x6ED3, 0x6EB6, 0x6EC2, 0x6E90, 0x6E9D, 0x6EC7, 0x6EC5, 0x6EA5, 0x6E98, 0x6EBC, 0x6EBA, 0x6EAB, 0x6ED1, 0x6E96, 0x6E9C, 0x6EC4, 0x6ED4, 0x6EAA, 0x6EA7, 0x6EB4, 0x714E, 0x7159, 0x7169, 0x7164, 0x7149, 0x7167, 0x715C, 0x716C, 0x7166, 0x714C, 0x7165, 0x715E, 0x7146, 0x7168, 0x7156, 0x723A, 0x7252, 0x7337, 0x7345, 0x733F, 0x733E, 0x746F, 0x745A, 0x7455, 0x745F, 0x745E, 0x7441, 0x743F, 0x7459, 0x745B, 0x745C, 0x7576, 0x7578, 0x7600, 0x75F0, 0x7601, 0x75F2, 0x75F1, 0x75FA, 0x75FF, 0x75F4, 0x75F3, 0x76DE, 0x76DF, 0x775B, 0x776B, 0x7766, 0x775E, 0x7763, + 0x7779, 0x776A, 0x776C, 0x775C, 0x7765, 0x7768, 0x7762, 0x77EE, 0x788E, 0x78B0, 0x7897, 0x7898, 0x788C, 0x7889, 0x787C, 0x7891, 0x7893, 0x787F, 0x797A, 0x797F, 0x7981, 0x842C, 0x79BD, 0x7A1C, 0x7A1A, 0x7A20, 0x7A14, 0x7A1F, 0x7A1E, 0x7A9F, 0x7AA0, 0x7B77, 0x7BC0, 0x7B60, 0x7B6E, 0x7B67, 0x7CB1, 0x7CB3, 0x7CB5, 0x7D93, 0x7D79, 0x7D91, 0x7D81, 0x7D8F, 0x7D5B, 0x7F6E, 0x7F69, 0x7F6A, 0x7F72, 0x7FA9, 0x7FA8, 0x7FA4, 0x8056, 0x8058, 0x8086, 0x8084, 0x8171, 0x8170, 0x8178, 0x8165, 0x816E, 0x8173, 0x816B, 0x8179, 0x817A, 0x8166, 0x8205, 0x8247, 0x8482, 0x8477, 0x843D, 0x8431, 0x8475, 0x8466, 0x846B, 0x8449, 0x846C, 0x845B, 0x843C, 0x8435, 0x8461, 0x8463, 0x8469, 0x846D, 0x8446, 0x865E, 0x865C, 0x865F, 0x86F9, 0x8713, 0x8708, 0x8707, 0x8700, 0x86FE, 0x86FB, 0x8702, 0x8703, 0x8706, 0x870A, 0x8859, 0x88DF, 0x88D4, 0x88D9, 0x88DC, 0x88D8, 0x88DD, 0x88E1, 0x88CA, 0x88D5, 0x88D2, 0x899C, 0x89E3, 0x8A6B, 0x8A72, 0x8A73, 0x8A66, 0x8A69, 0x8A70, 0x8A87, 0x8A7C, 0x8A63, 0x8AA0, 0x8A71, 0x8A85, 0x8A6D, 0x8A62, 0x8A6E, 0x8A6C, 0x8A79, 0x8A7B, 0x8A3E, 0x8A68, 0x8C62, 0x8C8A, 0x8C89, 0x8CCA, 0x8CC7, 0x8CC8, 0x8CC4, 0x8CB2, 0x8CC3, 0x8CC2, 0x8CC5, 0x8DE1, 0x8DDF, 0x8DE8, 0x8DEF, 0x8DF3, 0x8DFA, 0x8DEA, 0x8DE4, 0x8DE6, 0x8EB2, 0x8F03, 0x8F09, 0x8EFE, 0x8F0A, + 0x8F9F, 0x8FB2, 0x904B, 0x904A, 0x9053, 0x9042, 0x9054, 0x903C, 0x9055, 0x9050, 0x9047, 0x904F, 0x904E, 0x904D, 0x9051, 0x903E, 0x9041, 0x9112, 0x9117, 0x916C, 0x916A, 0x9169, 0x91C9, 0x9237, 0x9257, 0x9238, 0x923D, 0x9240, 0x923E, 0x925B, 0x924B, 0x9264, 0x9251, 0x9234, 0x9249, 0x924D, 0x9245, 0x9239, 0x923F, 0x925A, 0x9598, 0x9698, 0x9694, 0x9695, 0x96CD, 0x96CB, 0x96C9, 0x96CA, 0x96F7, 0x96FB, 0x96F9, 0x96F6, 0x9756, 0x9774, 0x9776, 0x9810, 0x9811, 0x9813, 0x980A, 0x9812, 0x980C, 0x98FC, 0x98F4, 0x98FD, 0x98FE, 0x99B3, 0x99B1, 0x99B4, 0x9AE1, 0x9CE9, 0x9E82, 0x9F0E, 0x9F13, 0x9F20, 0x50E7, 0x50EE, 0x50E5, 0x50D6, 0x50ED, 0x50DA, 0x50D5, 0x50CF, 0x50D1, 0x50F1, 0x50CE, 0x50E9, 0x5162, 0x51F3, 0x5283, 0x5282, 0x5331, 0x53AD, 0x55FE, 0x5600, 0x561B, 0x5617, 0x55FD, 0x5614, 0x5606, 0x5609, 0x560D, 0x560E, 0x55F7, 0x5616, 0x561F, 0x5608, 0x5610, 0x55F6, 0x5718, 0x5716, 0x5875, 0x587E, 0x5883, 0x5893, 0x588A, 0x5879, 0x5885, 0x587D, 0x58FD, 0x5925, 0x5922, 0x5924, 0x596A, 0x5969, 0x5AE1, 0x5AE6, 0x5AE9, 0x5AD7, 0x5AD6, 0x5AD8, 0x5AE3, 0x5B75, 0x5BDE, 0x5BE7, 0x5BE1, 0x5BE5, 0x5BE6, 0x5BE8, 0x5BE2, 0x5BE4, 0x5BDF, 0x5C0D, 0x5C62, 0x5D84, 0x5D87, 0x5E5B, 0x5E63, 0x5E55, 0x5E57, 0x5E54, 0x5ED3, 0x5ED6, 0x5F0A, 0x5F46, 0x5F70, 0x5FB9, 0x6147, + 0x613F, 0x614B, 0x6177, 0x6162, 0x6163, 0x615F, 0x615A, 0x6158, 0x6175, 0x622A, 0x6487, 0x6458, 0x6454, 0x64A4, 0x6478, 0x645F, 0x647A, 0x6451, 0x6467, 0x6434, 0x646D, 0x647B, 0x6572, 0x65A1, 0x65D7, 0x65D6, 0x66A2, 0x66A8, 0x669D, 0x699C, 0x69A8, 0x6995, 0x69C1, 0x69AE, 0x69D3, 0x69CB, 0x699B, 0x69B7, 0x69BB, 0x69AB, 0x69B4, 0x69D0, 0x69CD, 0x69AD, 0x69CC, 0x69A6, 0x69C3, 0x69A3, 0x6B49, 0x6B4C, 0x6C33, 0x6F33, 0x6F14, 0x6EFE, 0x6F13, 0x6EF4, 0x6F29, 0x6F3E, 0x6F20, 0x6F2C, 0x6F0F, 0x6F02, 0x6F22, 0x6EFF, 0x6EEF, 0x6F06, 0x6F31, 0x6F38, 0x6F32, 0x6F23, 0x6F15, 0x6F2B, 0x6F2F, 0x6F88, 0x6F2A, 0x6EEC, 0x6F01, 0x6EF2, 0x6ECC, 0x6EF7, 0x7194, 0x7199, 0x717D, 0x718A, 0x7184, 0x7192, 0x723E, 0x7292, 0x7296, 0x7344, 0x7350, 0x7464, 0x7463, 0x746A, 0x7470, 0x746D, 0x7504, 0x7591, 0x7627, 0x760D, 0x760B, 0x7609, 0x7613, 0x76E1, 0x76E3, 0x7784, 0x777D, 0x777F, 0x7761, 0x78C1, 0x789F, 0x78A7, 0x78B3, 0x78A9, 0x78A3, 0x798E, 0x798F, 0x798D, 0x7A2E, 0x7A31, 0x7AAA, 0x7AA9, 0x7AED, 0x7AEF, 0x7BA1, 0x7B95, 0x7B8B, 0x7B75, 0x7B97, 0x7B9D, 0x7B94, 0x7B8F, 0x7BB8, 0x7B87, 0x7B84, 0x7CB9, 0x7CBD, 0x7CBE, 0x7DBB, 0x7DB0, 0x7D9C, 0x7DBD, 0x7DBE, 0x7DA0, 0x7DCA, 0x7DB4, 0x7DB2, 0x7DB1, 0x7DBA, 0x7DA2, 0x7DBF, 0x7DB5, 0x7DB8, 0x7DAD, 0x7DD2, 0x7DC7, 0x7DAC, + 0x7F70, 0x7FE0, 0x7FE1, 0x7FDF, 0x805E, 0x805A, 0x8087, 0x8150, 0x8180, 0x818F, 0x8188, 0x818A, 0x817F, 0x8182, 0x81E7, 0x81FA, 0x8207, 0x8214, 0x821E, 0x824B, 0x84C9, 0x84BF, 0x84C6, 0x84C4, 0x8499, 0x849E, 0x84B2, 0x849C, 0x84CB, 0x84B8, 0x84C0, 0x84D3, 0x8490, 0x84BC, 0x84D1, 0x84CA, 0x873F, 0x871C, 0x873B, 0x8722, 0x8725, 0x8734, 0x8718, 0x8755, 0x8737, 0x8729, 0x88F3, 0x8902, 0x88F4, 0x88F9, 0x88F8, 0x88FD, 0x88E8, 0x891A, 0x88EF, 0x8AA6, 0x8A8C, 0x8A9E, 0x8AA3, 0x8A8D, 0x8AA1, 0x8A93, 0x8AA4, 0x8AAA, 0x8AA5, 0x8AA8, 0x8A98, 0x8A91, 0x8A9A, 0x8AA7, 0x8C6A, 0x8C8D, 0x8C8C, 0x8CD3, 0x8CD1, 0x8CD2, 0x8D6B, 0x8D99, 0x8D95, 0x8DFC, 0x8F14, 0x8F12, 0x8F15, 0x8F13, 0x8FA3, 0x9060, 0x9058, 0x905C, 0x9063, 0x9059, 0x905E, 0x9062, 0x905D, 0x905B, 0x9119, 0x9118, 0x911E, 0x9175, 0x9178, 0x9177, 0x9174, 0x9278, 0x9280, 0x9285, 0x9298, 0x9296, 0x927B, 0x9293, 0x929C, 0x92A8, 0x927C, 0x9291, 0x95A1, 0x95A8, 0x95A9, 0x95A3, 0x95A5, 0x95A4, 0x9699, 0x969C, 0x969B, 0x96CC, 0x96D2, 0x9700, 0x977C, 0x9785, 0x97F6, 0x9817, 0x9818, 0x98AF, 0x98B1, 0x9903, 0x9905, 0x990C, 0x9909, 0x99C1, 0x9AAF, 0x9AB0, 0x9AE6, 0x9B41, 0x9B42, 0x9CF4, 0x9CF6, 0x9CF3, 0x9EBC, 0x9F3B, 0x9F4A, 0x5104, 0x5100, 0x50FB, 0x50F5, 0x50F9, 0x5102, 0x5108, 0x5109, 0x5105, 0x51DC, + 0x5287, 0x5288, 0x5289, 0x528D, 0x528A, 0x52F0, 0x53B2, 0x562E, 0x563B, 0x5639, 0x5632, 0x563F, 0x5634, 0x5629, 0x5653, 0x564E, 0x5657, 0x5674, 0x5636, 0x562F, 0x5630, 0x5880, 0x589F, 0x589E, 0x58B3, 0x589C, 0x58AE, 0x58A9, 0x58A6, 0x596D, 0x5B09, 0x5AFB, 0x5B0B, 0x5AF5, 0x5B0C, 0x5B08, 0x5BEE, 0x5BEC, 0x5BE9, 0x5BEB, 0x5C64, 0x5C65, 0x5D9D, 0x5D94, 0x5E62, 0x5E5F, 0x5E61, 0x5EE2, 0x5EDA, 0x5EDF, 0x5EDD, 0x5EE3, 0x5EE0, 0x5F48, 0x5F71, 0x5FB7, 0x5FB5, 0x6176, 0x6167, 0x616E, 0x615D, 0x6155, 0x6182, 0x617C, 0x6170, 0x616B, 0x617E, 0x61A7, 0x6190, 0x61AB, 0x618E, 0x61AC, 0x619A, 0x61A4, 0x6194, 0x61AE, 0x622E, 0x6469, 0x646F, 0x6479, 0x649E, 0x64B2, 0x6488, 0x6490, 0x64B0, 0x64A5, 0x6493, 0x6495, 0x64A9, 0x6492, 0x64AE, 0x64AD, 0x64AB, 0x649A, 0x64AC, 0x6499, 0x64A2, 0x64B3, 0x6575, 0x6577, 0x6578, 0x66AE, 0x66AB, 0x66B4, 0x66B1, 0x6A23, 0x6A1F, 0x69E8, 0x6A01, 0x6A1E, 0x6A19, 0x69FD, 0x6A21, 0x6A13, 0x6A0A, 0x69F3, 0x6A02, 0x6A05, 0x69ED, 0x6A11, 0x6B50, 0x6B4E, 0x6BA4, 0x6BC5, 0x6BC6, 0x6F3F, 0x6F7C, 0x6F84, 0x6F51, 0x6F66, 0x6F54, 0x6F86, 0x6F6D, 0x6F5B, 0x6F78, 0x6F6E, 0x6F8E, 0x6F7A, 0x6F70, 0x6F64, 0x6F97, 0x6F58, 0x6ED5, 0x6F6F, 0x6F60, 0x6F5F, 0x719F, 0x71AC, 0x71B1, 0x71A8, 0x7256, 0x729B, 0x734E, 0x7357, 0x7469, 0x748B, 0x7483, + 0x747E, 0x7480, 0x757F, 0x7620, 0x7629, 0x761F, 0x7624, 0x7626, 0x7621, 0x7622, 0x769A, 0x76BA, 0x76E4, 0x778E, 0x7787, 0x778C, 0x7791, 0x778B, 0x78CB, 0x78C5, 0x78BA, 0x78CA, 0x78BE, 0x78D5, 0x78BC, 0x78D0, 0x7A3F, 0x7A3C, 0x7A40, 0x7A3D, 0x7A37, 0x7A3B, 0x7AAF, 0x7AAE, 0x7BAD, 0x7BB1, 0x7BC4, 0x7BB4, 0x7BC6, 0x7BC7, 0x7BC1, 0x7BA0, 0x7BCC, 0x7CCA, 0x7DE0, 0x7DF4, 0x7DEF, 0x7DFB, 0x7DD8, 0x7DEC, 0x7DDD, 0x7DE8, 0x7DE3, 0x7DDA, 0x7DDE, 0x7DE9, 0x7D9E, 0x7DD9, 0x7DF2, 0x7DF9, 0x7F75, 0x7F77, 0x7FAF, 0x7FE9, 0x8026, 0x819B, 0x819C, 0x819D, 0x81A0, 0x819A, 0x8198, 0x8517, 0x853D, 0x851A, 0x84EE, 0x852C, 0x852D, 0x8513, 0x8511, 0x8523, 0x8521, 0x8514, 0x84EC, 0x8525, 0x84FF, 0x8506, 0x8782, 0x8774, 0x8776, 0x8760, 0x8766, 0x8778, 0x8768, 0x8759, 0x8757, 0x874C, 0x8753, 0x885B, 0x885D, 0x8910, 0x8907, 0x8912, 0x8913, 0x8915, 0x890A, 0x8ABC, 0x8AD2, 0x8AC7, 0x8AC4, 0x8A95, 0x8ACB, 0x8AF8, 0x8AB2, 0x8AC9, 0x8AC2, 0x8ABF, 0x8AB0, 0x8AD6, 0x8ACD, 0x8AB6, 0x8AB9, 0x8ADB, 0x8C4C, 0x8C4E, 0x8C6C, 0x8CE0, 0x8CDE, 0x8CE6, 0x8CE4, 0x8CEC, 0x8CED, 0x8CE2, 0x8CE3, 0x8CDC, 0x8CEA, 0x8CE1, 0x8D6D, 0x8D9F, 0x8DA3, 0x8E2B, 0x8E10, 0x8E1D, 0x8E22, 0x8E0F, 0x8E29, 0x8E1F, 0x8E21, 0x8E1E, 0x8EBA, 0x8F1D, 0x8F1B, 0x8F1F, 0x8F29, 0x8F26, 0x8F2A, 0x8F1C, 0x8F1E, + 0x8F25, 0x9069, 0x906E, 0x9068, 0x906D, 0x9077, 0x9130, 0x912D, 0x9127, 0x9131, 0x9187, 0x9189, 0x918B, 0x9183, 0x92C5, 0x92BB, 0x92B7, 0x92EA, 0x92AC, 0x92E4, 0x92C1, 0x92B3, 0x92BC, 0x92D2, 0x92C7, 0x92F0, 0x92B2, 0x95AD, 0x95B1, 0x9704, 0x9706, 0x9707, 0x9709, 0x9760, 0x978D, 0x978B, 0x978F, 0x9821, 0x982B, 0x981C, 0x98B3, 0x990A, 0x9913, 0x9912, 0x9918, 0x99DD, 0x99D0, 0x99DF, 0x99DB, 0x99D1, 0x99D5, 0x99D2, 0x99D9, 0x9AB7, 0x9AEE, 0x9AEF, 0x9B27, 0x9B45, 0x9B44, 0x9B77, 0x9B6F, 0x9D06, 0x9D09, 0x9D03, 0x9EA9, 0x9EBE, 0x9ECE, 0x58A8, 0x9F52, 0x5112, 0x5118, 0x5114, 0x5110, 0x5115, 0x5180, 0x51AA, 0x51DD, 0x5291, 0x5293, 0x52F3, 0x5659, 0x566B, 0x5679, 0x5669, 0x5664, 0x5678, 0x566A, 0x5668, 0x5665, 0x5671, 0x566F, 0x566C, 0x5662, 0x5676, 0x58C1, 0x58BE, 0x58C7, 0x58C5, 0x596E, 0x5B1D, 0x5B34, 0x5B78, 0x5BF0, 0x5C0E, 0x5F4A, 0x61B2, 0x6191, 0x61A9, 0x618A, 0x61CD, 0x61B6, 0x61BE, 0x61CA, 0x61C8, 0x6230, 0x64C5, 0x64C1, 0x64CB, 0x64BB, 0x64BC, 0x64DA, 0x64C4, 0x64C7, 0x64C2, 0x64CD, 0x64BF, 0x64D2, 0x64D4, 0x64BE, 0x6574, 0x66C6, 0x66C9, 0x66B9, 0x66C4, 0x66C7, 0x66B8, 0x6A3D, 0x6A38, 0x6A3A, 0x6A59, 0x6A6B, 0x6A58, 0x6A39, 0x6A44, 0x6A62, 0x6A61, 0x6A4B, 0x6A47, 0x6A35, 0x6A5F, 0x6A48, 0x6B59, 0x6B77, 0x6C05, 0x6FC2, 0x6FB1, 0x6FA1, + 0x6FC3, 0x6FA4, 0x6FC1, 0x6FA7, 0x6FB3, 0x6FC0, 0x6FB9, 0x6FB6, 0x6FA6, 0x6FA0, 0x6FB4, 0x71BE, 0x71C9, 0x71D0, 0x71D2, 0x71C8, 0x71D5, 0x71B9, 0x71CE, 0x71D9, 0x71DC, 0x71C3, 0x71C4, 0x7368, 0x749C, 0x74A3, 0x7498, 0x749F, 0x749E, 0x74E2, 0x750C, 0x750D, 0x7634, 0x7638, 0x763A, 0x76E7, 0x76E5, 0x77A0, 0x779E, 0x779F, 0x77A5, 0x78E8, 0x78DA, 0x78EC, 0x78E7, 0x79A6, 0x7A4D, 0x7A4E, 0x7A46, 0x7A4C, 0x7A4B, 0x7ABA, 0x7BD9, 0x7C11, 0x7BC9, 0x7BE4, 0x7BDB, 0x7BE1, 0x7BE9, 0x7BE6, 0x7CD5, 0x7CD6, 0x7E0A, 0x7E11, 0x7E08, 0x7E1B, 0x7E23, 0x7E1E, 0x7E1D, 0x7E09, 0x7E10, 0x7F79, 0x7FB2, 0x7FF0, 0x7FF1, 0x7FEE, 0x8028, 0x81B3, 0x81A9, 0x81A8, 0x81FB, 0x8208, 0x8258, 0x8259, 0x854A, 0x8559, 0x8548, 0x8568, 0x8569, 0x8543, 0x8549, 0x856D, 0x856A, 0x855E, 0x8783, 0x879F, 0x879E, 0x87A2, 0x878D, 0x8861, 0x892A, 0x8932, 0x8925, 0x892B, 0x8921, 0x89AA, 0x89A6, 0x8AE6, 0x8AFA, 0x8AEB, 0x8AF1, 0x8B00, 0x8ADC, 0x8AE7, 0x8AEE, 0x8AFE, 0x8B01, 0x8B02, 0x8AF7, 0x8AED, 0x8AF3, 0x8AF6, 0x8AFC, 0x8C6B, 0x8C6D, 0x8C93, 0x8CF4, 0x8E44, 0x8E31, 0x8E34, 0x8E42, 0x8E39, 0x8E35, 0x8F3B, 0x8F2F, 0x8F38, 0x8F33, 0x8FA8, 0x8FA6, 0x9075, 0x9074, 0x9078, 0x9072, 0x907C, 0x907A, 0x9134, 0x9192, 0x9320, 0x9336, 0x92F8, 0x9333, 0x932F, 0x9322, 0x92FC, 0x932B, 0x9304, 0x931A, + 0x9310, 0x9326, 0x9321, 0x9315, 0x932E, 0x9319, 0x95BB, 0x96A7, 0x96A8, 0x96AA, 0x96D5, 0x970E, 0x9711, 0x9716, 0x970D, 0x9713, 0x970F, 0x975B, 0x975C, 0x9766, 0x9798, 0x9830, 0x9838, 0x983B, 0x9837, 0x982D, 0x9839, 0x9824, 0x9910, 0x9928, 0x991E, 0x991B, 0x9921, 0x991A, 0x99ED, 0x99E2, 0x99F1, 0x9AB8, 0x9ABC, 0x9AFB, 0x9AED, 0x9B28, 0x9B91, 0x9D15, 0x9D23, 0x9D26, 0x9D28, 0x9D12, 0x9D1B, 0x9ED8, 0x9ED4, 0x9F8D, 0x9F9C, 0x512A, 0x511F, 0x5121, 0x5132, 0x52F5, 0x568E, 0x5680, 0x5690, 0x5685, 0x5687, 0x568F, 0x58D5, 0x58D3, 0x58D1, 0x58CE, 0x5B30, 0x5B2A, 0x5B24, 0x5B7A, 0x5C37, 0x5C68, 0x5DBC, 0x5DBA, 0x5DBD, 0x5DB8, 0x5E6B, 0x5F4C, 0x5FBD, 0x61C9, 0x61C2, 0x61C7, 0x61E6, 0x61CB, 0x6232, 0x6234, 0x64CE, 0x64CA, 0x64D8, 0x64E0, 0x64F0, 0x64E6, 0x64EC, 0x64F1, 0x64E2, 0x64ED, 0x6582, 0x6583, 0x66D9, 0x66D6, 0x6A80, 0x6A94, 0x6A84, 0x6AA2, 0x6A9C, 0x6ADB, 0x6AA3, 0x6A7E, 0x6A97, 0x6A90, 0x6AA0, 0x6B5C, 0x6BAE, 0x6BDA, 0x6C08, 0x6FD8, 0x6FF1, 0x6FDF, 0x6FE0, 0x6FDB, 0x6FE4, 0x6FEB, 0x6FEF, 0x6F80, 0x6FEC, 0x6FE1, 0x6FE9, 0x6FD5, 0x6FEE, 0x6FF0, 0x71E7, 0x71DF, 0x71EE, 0x71E6, 0x71E5, 0x71ED, 0x71EC, 0x71F4, 0x71E0, 0x7235, 0x7246, 0x7370, 0x7372, 0x74A9, 0x74B0, 0x74A6, 0x74A8, 0x7646, 0x7642, 0x764C, 0x76EA, 0x77B3, 0x77AA, 0x77B0, 0x77AC, + 0x77A7, 0x77AD, 0x77EF, 0x78F7, 0x78FA, 0x78F4, 0x78EF, 0x7901, 0x79A7, 0x79AA, 0x7A57, 0x7ABF, 0x7C07, 0x7C0D, 0x7BFE, 0x7BF7, 0x7C0C, 0x7BE0, 0x7CE0, 0x7CDC, 0x7CDE, 0x7CE2, 0x7CDF, 0x7CD9, 0x7CDD, 0x7E2E, 0x7E3E, 0x7E46, 0x7E37, 0x7E32, 0x7E43, 0x7E2B, 0x7E3D, 0x7E31, 0x7E45, 0x7E41, 0x7E34, 0x7E39, 0x7E48, 0x7E35, 0x7E3F, 0x7E2F, 0x7F44, 0x7FF3, 0x7FFC, 0x8071, 0x8072, 0x8070, 0x806F, 0x8073, 0x81C6, 0x81C3, 0x81BA, 0x81C2, 0x81C0, 0x81BF, 0x81BD, 0x81C9, 0x81BE, 0x81E8, 0x8209, 0x8271, 0x85AA, 0x8584, 0x857E, 0x859C, 0x8591, 0x8594, 0x85AF, 0x859B, 0x8587, 0x85A8, 0x858A, 0x8667, 0x87C0, 0x87D1, 0x87B3, 0x87D2, 0x87C6, 0x87AB, 0x87BB, 0x87BA, 0x87C8, 0x87CB, 0x893B, 0x8936, 0x8944, 0x8938, 0x893D, 0x89AC, 0x8B0E, 0x8B17, 0x8B19, 0x8B1B, 0x8B0A, 0x8B20, 0x8B1D, 0x8B04, 0x8B10, 0x8C41, 0x8C3F, 0x8C73, 0x8CFA, 0x8CFD, 0x8CFC, 0x8CF8, 0x8CFB, 0x8DA8, 0x8E49, 0x8E4B, 0x8E48, 0x8E4A, 0x8F44, 0x8F3E, 0x8F42, 0x8F45, 0x8F3F, 0x907F, 0x907D, 0x9084, 0x9081, 0x9082, 0x9080, 0x9139, 0x91A3, 0x919E, 0x919C, 0x934D, 0x9382, 0x9328, 0x9375, 0x934A, 0x9365, 0x934B, 0x9318, 0x937E, 0x936C, 0x935B, 0x9370, 0x935A, 0x9354, 0x95CA, 0x95CB, 0x95CC, 0x95C8, 0x95C6, 0x96B1, 0x96B8, 0x96D6, 0x971C, 0x971E, 0x97A0, 0x97D3, 0x9846, 0x98B6, 0x9935, 0x9A01, + 0x99FF, 0x9BAE, 0x9BAB, 0x9BAA, 0x9BAD, 0x9D3B, 0x9D3F, 0x9E8B, 0x9ECF, 0x9EDE, 0x9EDC, 0x9EDD, 0x9EDB, 0x9F3E, 0x9F4B, 0x53E2, 0x5695, 0x56AE, 0x58D9, 0x58D8, 0x5B38, 0x5F5D, 0x61E3, 0x6233, 0x64F4, 0x64F2, 0x64FE, 0x6506, 0x64FA, 0x64FB, 0x64F7, 0x65B7, 0x66DC, 0x6726, 0x6AB3, 0x6AAC, 0x6AC3, 0x6ABB, 0x6AB8, 0x6AC2, 0x6AAE, 0x6AAF, 0x6B5F, 0x6B78, 0x6BAF, 0x7009, 0x700B, 0x6FFE, 0x7006, 0x6FFA, 0x7011, 0x700F, 0x71FB, 0x71FC, 0x71FE, 0x71F8, 0x7377, 0x7375, 0x74A7, 0x74BF, 0x7515, 0x7656, 0x7658, 0x7652, 0x77BD, 0x77BF, 0x77BB, 0x77BC, 0x790E, 0x79AE, 0x7A61, 0x7A62, 0x7A60, 0x7AC4, 0x7AC5, 0x7C2B, 0x7C27, 0x7C2A, 0x7C1E, 0x7C23, 0x7C21, 0x7CE7, 0x7E54, 0x7E55, 0x7E5E, 0x7E5A, 0x7E61, 0x7E52, 0x7E59, 0x7F48, 0x7FF9, 0x7FFB, 0x8077, 0x8076, 0x81CD, 0x81CF, 0x820A, 0x85CF, 0x85A9, 0x85CD, 0x85D0, 0x85C9, 0x85B0, 0x85BA, 0x85B9, 0x85A6, 0x87EF, 0x87EC, 0x87F2, 0x87E0, 0x8986, 0x89B2, 0x89F4, 0x8B28, 0x8B39, 0x8B2C, 0x8B2B, 0x8C50, 0x8D05, 0x8E59, 0x8E63, 0x8E66, 0x8E64, 0x8E5F, 0x8E55, 0x8EC0, 0x8F49, 0x8F4D, 0x9087, 0x9083, 0x9088, 0x91AB, 0x91AC, 0x91D0, 0x9394, 0x938A, 0x9396, 0x93A2, 0x93B3, 0x93AE, 0x93AC, 0x93B0, 0x9398, 0x939A, 0x9397, 0x95D4, 0x95D6, 0x95D0, 0x95D5, 0x96E2, 0x96DC, 0x96D9, 0x96DB, 0x96DE, 0x9724, 0x97A3, 0x97A6, + 0x97AD, 0x97F9, 0x984D, 0x984F, 0x984C, 0x984E, 0x9853, 0x98BA, 0x993E, 0x993F, 0x993D, 0x992E, 0x99A5, 0x9A0E, 0x9AC1, 0x9B03, 0x9B06, 0x9B4F, 0x9B4E, 0x9B4D, 0x9BCA, 0x9BC9, 0x9BFD, 0x9BC8, 0x9BC0, 0x9D51, 0x9D5D, 0x9D60, 0x9EE0, 0x9F15, 0x9F2C, 0x5133, 0x56A5, 0x58DE, 0x58DF, 0x58E2, 0x5BF5, 0x9F90, 0x5EEC, 0x61F2, 0x61F7, 0x61F6, 0x61F5, 0x6500, 0x650F, 0x66E0, 0x66DD, 0x6AE5, 0x6ADD, 0x6ADA, 0x6AD3, 0x701B, 0x701F, 0x7028, 0x701A, 0x701D, 0x7015, 0x7018, 0x7206, 0x720D, 0x7258, 0x72A2, 0x7378, 0x737A, 0x74BD, 0x74CA, 0x74E3, 0x7587, 0x7586, 0x765F, 0x7661, 0x77C7, 0x7919, 0x79B1, 0x7A6B, 0x7A69, 0x7C3E, 0x7C3F, 0x7C38, 0x7C3D, 0x7C37, 0x7C40, 0x7E6B, 0x7E6D, 0x7E79, 0x7E69, 0x7E6A, 0x7F85, 0x7E73, 0x7FB6, 0x7FB9, 0x7FB8, 0x81D8, 0x85E9, 0x85DD, 0x85EA, 0x85D5, 0x85E4, 0x85E5, 0x85F7, 0x87FB, 0x8805, 0x880D, 0x87F9, 0x87FE, 0x8960, 0x895F, 0x8956, 0x895E, 0x8B41, 0x8B5C, 0x8B58, 0x8B49, 0x8B5A, 0x8B4E, 0x8B4F, 0x8B46, 0x8B59, 0x8D08, 0x8D0A, 0x8E7C, 0x8E72, 0x8E87, 0x8E76, 0x8E6C, 0x8E7A, 0x8E74, 0x8F54, 0x8F4E, 0x8FAD, 0x908A, 0x908B, 0x91B1, 0x91AE, 0x93E1, 0x93D1, 0x93DF, 0x93C3, 0x93C8, 0x93DC, 0x93DD, 0x93D6, 0x93E2, 0x93CD, 0x93D8, 0x93E4, 0x93D7, 0x93E8, 0x95DC, 0x96B4, 0x96E3, 0x972A, 0x9727, 0x9761, 0x97DC, 0x97FB, 0x985E, + 0x9858, 0x985B, 0x98BC, 0x9945, 0x9949, 0x9A16, 0x9A19, 0x9B0D, 0x9BE8, 0x9BE7, 0x9BD6, 0x9BDB, 0x9D89, 0x9D61, 0x9D72, 0x9D6A, 0x9D6C, 0x9E92, 0x9E97, 0x9E93, 0x9EB4, 0x52F8, 0x56A8, 0x56B7, 0x56B6, 0x56B4, 0x56BC, 0x58E4, 0x5B40, 0x5B43, 0x5B7D, 0x5BF6, 0x5DC9, 0x61F8, 0x61FA, 0x6518, 0x6514, 0x6519, 0x66E6, 0x6727, 0x6AEC, 0x703E, 0x7030, 0x7032, 0x7210, 0x737B, 0x74CF, 0x7662, 0x7665, 0x7926, 0x792A, 0x792C, 0x792B, 0x7AC7, 0x7AF6, 0x7C4C, 0x7C43, 0x7C4D, 0x7CEF, 0x7CF0, 0x8FAE, 0x7E7D, 0x7E7C, 0x7E82, 0x7F4C, 0x8000, 0x81DA, 0x8266, 0x85FB, 0x85F9, 0x8611, 0x85FA, 0x8606, 0x860B, 0x8607, 0x860A, 0x8814, 0x8815, 0x8964, 0x89BA, 0x89F8, 0x8B70, 0x8B6C, 0x8B66, 0x8B6F, 0x8B5F, 0x8B6B, 0x8D0F, 0x8D0D, 0x8E89, 0x8E81, 0x8E85, 0x8E82, 0x91B4, 0x91CB, 0x9418, 0x9403, 0x93FD, 0x95E1, 0x9730, 0x98C4, 0x9952, 0x9951, 0x99A8, 0x9A2B, 0x9A30, 0x9A37, 0x9A35, 0x9C13, 0x9C0D, 0x9E79, 0x9EB5, 0x9EE8, 0x9F2F, 0x9F5F, 0x9F63, 0x9F61, 0x5137, 0x5138, 0x56C1, 0x56C0, 0x56C2, 0x5914, 0x5C6C, 0x5DCD, 0x61FC, 0x61FE, 0x651D, 0x651C, 0x6595, 0x66E9, 0x6AFB, 0x6B04, 0x6AFA, 0x6BB2, 0x704C, 0x721B, 0x72A7, 0x74D6, 0x74D4, 0x7669, 0x77D3, 0x7C50, 0x7E8F, 0x7E8C, 0x7FBC, 0x8617, 0x862D, 0x861A, 0x8823, 0x8822, 0x8821, 0x881F, 0x896A, 0x896C, 0x89BD, 0x8B74, + 0x8B77, 0x8B7D, 0x8D13, 0x8E8A, 0x8E8D, 0x8E8B, 0x8F5F, 0x8FAF, 0x91BA, 0x942E, 0x9433, 0x9435, 0x943A, 0x9438, 0x9432, 0x942B, 0x95E2, 0x9738, 0x9739, 0x9732, 0x97FF, 0x9867, 0x9865, 0x9957, 0x9A45, 0x9A43, 0x9A40, 0x9A3E, 0x9ACF, 0x9B54, 0x9B51, 0x9C2D, 0x9C25, 0x9DAF, 0x9DB4, 0x9DC2, 0x9DB8, 0x9E9D, 0x9EEF, 0x9F19, 0x9F5C, 0x9F66, 0x9F67, 0x513C, 0x513B, 0x56C8, 0x56CA, 0x56C9, 0x5B7F, 0x5DD4, 0x5DD2, 0x5F4E, 0x61FF, 0x6524, 0x6B0A, 0x6B61, 0x7051, 0x7058, 0x7380, 0x74E4, 0x758A, 0x766E, 0x766C, 0x79B3, 0x7C60, 0x7C5F, 0x807E, 0x807D, 0x81DF, 0x8972, 0x896F, 0x89FC, 0x8B80, 0x8D16, 0x8D17, 0x8E91, 0x8E93, 0x8F61, 0x9148, 0x9444, 0x9451, 0x9452, 0x973D, 0x973E, 0x97C3, 0x97C1, 0x986B, 0x9955, 0x9A55, 0x9A4D, 0x9AD2, 0x9B1A, 0x9C49, 0x9C31, 0x9C3E, 0x9C3B, 0x9DD3, 0x9DD7, 0x9F34, 0x9F6C, 0x9F6A, 0x9F94, 0x56CC, 0x5DD6, 0x6200, 0x6523, 0x652B, 0x652A, 0x66EC, 0x6B10, 0x74DA, 0x7ACA, 0x7C64, 0x7C63, 0x7C65, 0x7E93, 0x7E96, 0x7E94, 0x81E2, 0x8638, 0x863F, 0x8831, 0x8B8A, 0x9090, 0x908F, 0x9463, 0x9460, 0x9464, 0x9768, 0x986F, 0x995C, 0x9A5A, 0x9A5B, 0x9A57, 0x9AD3, 0x9AD4, 0x9AD1, 0x9C54, 0x9C57, 0x9C56, 0x9DE5, 0x9E9F, 0x9EF4, 0x56D1, 0x58E9, 0x652C, 0x705E, 0x7671, 0x7672, 0x77D7, 0x7F50, 0x7F88, 0x8836, 0x8839, 0x8862, 0x8B93, 0x8B92, + 0x8B96, 0x8277, 0x8D1B, 0x91C0, 0x946A, 0x9742, 0x9748, 0x9744, 0x97C6, 0x9870, 0x9A5F, 0x9B22, 0x9B58, 0x9C5F, 0x9DF9, 0x9DFA, 0x9E7C, 0x9E7D, 0x9F07, 0x9F77, 0x9F72, 0x5EF3, 0x6B16, 0x7063, 0x7C6C, 0x7C6E, 0x883B, 0x89C0, 0x8EA1, 0x91C1, 0x9472, 0x9470, 0x9871, 0x995E, 0x9AD6, 0x9B23, 0x9ECC, 0x7064, 0x77DA, 0x8B9A, 0x9477, 0x97C9, 0x9A62, 0x9A65, 0x7E9C, 0x8B9C, 0x8EAA, 0x91C5, 0x947D, 0x947E, 0x947C, 0x9C77, 0x9C78, 0x9EF7, 0x8C54, 0x947F, 0x9E1A, 0x7228, 0x9A6A, 0x9B31, 0x9E1B, 0x9E1E, 0x7C72, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x4E42, 0x4E5C, 0x51F5, 0x531A, 0x5382, 0x4E07, 0x4E0C, 0x4E47, 0x4E8D, 0x56D7, 0xFA0C, 0x5C6E, 0x5F73, 0x4E0F, 0x5187, 0x4E0E, 0x4E2E, 0x4E93, 0x4EC2, 0x4EC9, 0x4EC8, 0x5198, 0x52FC, 0x536C, 0x53B9, 0x5720, 0x5903, 0x592C, 0x5C10, 0x5DFF, 0x65E1, 0x6BB3, 0x6BCC, 0x6C14, 0x723F, 0x4E31, 0x4E3C, 0x4EE8, 0x4EDC, 0x4EE9, 0x4EE1, 0x4EDD, 0x4EDA, 0x520C, 0x531C, 0x534C, 0x5722, 0x5723, 0x5917, 0x592F, 0x5B81, 0x5B84, 0x5C12, 0x5C3B, 0x5C74, 0x5C73, 0x5E04, 0x5E80, 0x5E82, 0x5FC9, 0x6209, 0x6250, 0x6C15, 0x6C36, 0x6C43, 0x6C3F, 0x6C3B, 0x72AE, 0x72B0, 0x738A, 0x79B8, 0x808A, 0x961E, 0x4F0E, 0x4F18, 0x4F2C, 0x4EF5, 0x4F14, 0x4EF1, 0x4F00, 0x4EF7, 0x4F08, 0x4F1D, 0x4F02, 0x4F05, 0x4F22, 0x4F13, 0x4F04, 0x4EF4, 0x4F12, 0x51B1, 0x5213, 0x5209, 0x5210, 0x52A6, 0x5322, 0x531F, 0x534D, 0x538A, 0x5407, 0x56E1, 0x56DF, 0x572E, 0x572A, 0x5734, 0x593C, 0x5980, 0x597C, 0x5985, 0x597B, 0x597E, 0x5977, 0x597F, 0x5B56, 0x5C15, 0x5C25, 0x5C7C, 0x5C7A, 0x5C7B, 0x5C7E, 0x5DDF, 0x5E75, 0x5E84, 0x5F02, 0x5F1A, 0x5F74, 0x5FD5, 0x5FD4, 0x5FCF, 0x625C, 0x625E, 0x6264, 0x6261, 0x6266, 0x6262, 0x6259, 0x6260, 0x625A, 0x6265, 0x65EF, 0x65EE, 0x673E, 0x6739, 0x6738, 0x673B, 0x673A, 0x673F, 0x673C, 0x6733, 0x6C18, 0x6C46, 0x6C52, 0x6C5C, 0x6C4F, 0x6C4A, 0x6C54, 0x6C4B, + 0x6C4C, 0x7071, 0x725E, 0x72B4, 0x72B5, 0x738E, 0x752A, 0x767F, 0x7A75, 0x7F51, 0x8278, 0x827C, 0x8280, 0x827D, 0x827F, 0x864D, 0x897E, 0x9099, 0x9097, 0x9098, 0x909B, 0x9094, 0x9622, 0x9624, 0x9620, 0x9623, 0x4F56, 0x4F3B, 0x4F62, 0x4F49, 0x4F53, 0x4F64, 0x4F3E, 0x4F67, 0x4F52, 0x4F5F, 0x4F41, 0x4F58, 0x4F2D, 0x4F33, 0x4F3F, 0x4F61, 0x518F, 0x51B9, 0x521C, 0x521E, 0x5221, 0x52AD, 0x52AE, 0x5309, 0x5363, 0x5372, 0x538E, 0x538F, 0x5430, 0x5437, 0x542A, 0x5454, 0x5445, 0x5419, 0x541C, 0x5425, 0x5418, 0x543D, 0x544F, 0x5441, 0x5428, 0x5424, 0x5447, 0x56EE, 0x56E7, 0x56E5, 0x5741, 0x5745, 0x574C, 0x5749, 0x574B, 0x5752, 0x5906, 0x5940, 0x59A6, 0x5998, 0x59A0, 0x5997, 0x598E, 0x59A2, 0x5990, 0x598F, 0x59A7, 0x59A1, 0x5B8E, 0x5B92, 0x5C28, 0x5C2A, 0x5C8D, 0x5C8F, 0x5C88, 0x5C8B, 0x5C89, 0x5C92, 0x5C8A, 0x5C86, 0x5C93, 0x5C95, 0x5DE0, 0x5E0A, 0x5E0E, 0x5E8B, 0x5E89, 0x5E8C, 0x5E88, 0x5E8D, 0x5F05, 0x5F1D, 0x5F78, 0x5F76, 0x5FD2, 0x5FD1, 0x5FD0, 0x5FED, 0x5FE8, 0x5FEE, 0x5FF3, 0x5FE1, 0x5FE4, 0x5FE3, 0x5FFA, 0x5FEF, 0x5FF7, 0x5FFB, 0x6000, 0x5FF4, 0x623A, 0x6283, 0x628C, 0x628E, 0x628F, 0x6294, 0x6287, 0x6271, 0x627B, 0x627A, 0x6270, 0x6281, 0x6288, 0x6277, 0x627D, 0x6272, 0x6274, 0x6537, 0x65F0, 0x65F4, 0x65F3, 0x65F2, 0x65F5, 0x6745, 0x6747, + 0x6759, 0x6755, 0x674C, 0x6748, 0x675D, 0x674D, 0x675A, 0x674B, 0x6BD0, 0x6C19, 0x6C1A, 0x6C78, 0x6C67, 0x6C6B, 0x6C84, 0x6C8B, 0x6C8F, 0x6C71, 0x6C6F, 0x6C69, 0x6C9A, 0x6C6D, 0x6C87, 0x6C95, 0x6C9C, 0x6C66, 0x6C73, 0x6C65, 0x6C7B, 0x6C8E, 0x7074, 0x707A, 0x7263, 0x72BF, 0x72BD, 0x72C3, 0x72C6, 0x72C1, 0x72BA, 0x72C5, 0x7395, 0x7397, 0x7393, 0x7394, 0x7392, 0x753A, 0x7539, 0x7594, 0x7595, 0x7681, 0x793D, 0x8034, 0x8095, 0x8099, 0x8090, 0x8092, 0x809C, 0x8290, 0x828F, 0x8285, 0x828E, 0x8291, 0x8293, 0x828A, 0x8283, 0x8284, 0x8C78, 0x8FC9, 0x8FBF, 0x909F, 0x90A1, 0x90A5, 0x909E, 0x90A7, 0x90A0, 0x9630, 0x9628, 0x962F, 0x962D, 0x4E33, 0x4F98, 0x4F7C, 0x4F85, 0x4F7D, 0x4F80, 0x4F87, 0x4F76, 0x4F74, 0x4F89, 0x4F84, 0x4F77, 0x4F4C, 0x4F97, 0x4F6A, 0x4F9A, 0x4F79, 0x4F81, 0x4F78, 0x4F90, 0x4F9C, 0x4F94, 0x4F9E, 0x4F92, 0x4F82, 0x4F95, 0x4F6B, 0x4F6E, 0x519E, 0x51BC, 0x51BE, 0x5235, 0x5232, 0x5233, 0x5246, 0x5231, 0x52BC, 0x530A, 0x530B, 0x533C, 0x5392, 0x5394, 0x5487, 0x547F, 0x5481, 0x5491, 0x5482, 0x5488, 0x546B, 0x547A, 0x547E, 0x5465, 0x546C, 0x5474, 0x5466, 0x548D, 0x546F, 0x5461, 0x5460, 0x5498, 0x5463, 0x5467, 0x5464, 0x56F7, 0x56F9, 0x576F, 0x5772, 0x576D, 0x576B, 0x5771, 0x5770, 0x5776, 0x5780, 0x5775, 0x577B, 0x5773, 0x5774, 0x5762, + 0x5768, 0x577D, 0x590C, 0x5945, 0x59B5, 0x59BA, 0x59CF, 0x59CE, 0x59B2, 0x59CC, 0x59C1, 0x59B6, 0x59BC, 0x59C3, 0x59D6, 0x59B1, 0x59BD, 0x59C0, 0x59C8, 0x59B4, 0x59C7, 0x5B62, 0x5B65, 0x5B93, 0x5B95, 0x5C44, 0x5C47, 0x5CAE, 0x5CA4, 0x5CA0, 0x5CB5, 0x5CAF, 0x5CA8, 0x5CAC, 0x5C9F, 0x5CA3, 0x5CAD, 0x5CA2, 0x5CAA, 0x5CA7, 0x5C9D, 0x5CA5, 0x5CB6, 0x5CB0, 0x5CA6, 0x5E17, 0x5E14, 0x5E19, 0x5F28, 0x5F22, 0x5F23, 0x5F24, 0x5F54, 0x5F82, 0x5F7E, 0x5F7D, 0x5FDE, 0x5FE5, 0x602D, 0x6026, 0x6019, 0x6032, 0x600B, 0x6034, 0x600A, 0x6017, 0x6033, 0x601A, 0x601E, 0x602C, 0x6022, 0x600D, 0x6010, 0x602E, 0x6013, 0x6011, 0x600C, 0x6009, 0x601C, 0x6214, 0x623D, 0x62AD, 0x62B4, 0x62D1, 0x62BE, 0x62AA, 0x62B6, 0x62CA, 0x62AE, 0x62B3, 0x62AF, 0x62BB, 0x62A9, 0x62B0, 0x62B8, 0x653D, 0x65A8, 0x65BB, 0x6609, 0x65FC, 0x6604, 0x6612, 0x6608, 0x65FB, 0x6603, 0x660B, 0x660D, 0x6605, 0x65FD, 0x6611, 0x6610, 0x66F6, 0x670A, 0x6785, 0x676C, 0x678E, 0x6792, 0x6776, 0x677B, 0x6798, 0x6786, 0x6784, 0x6774, 0x678D, 0x678C, 0x677A, 0x679F, 0x6791, 0x6799, 0x6783, 0x677D, 0x6781, 0x6778, 0x6779, 0x6794, 0x6B25, 0x6B80, 0x6B7E, 0x6BDE, 0x6C1D, 0x6C93, 0x6CEC, 0x6CEB, 0x6CEE, 0x6CD9, 0x6CB6, 0x6CD4, 0x6CAD, 0x6CE7, 0x6CB7, 0x6CD0, 0x6CC2, 0x6CBA, 0x6CC3, 0x6CC6, 0x6CED, 0x6CF2, + 0x6CD2, 0x6CDD, 0x6CB4, 0x6C8A, 0x6C9D, 0x6C80, 0x6CDE, 0x6CC0, 0x6D30, 0x6CCD, 0x6CC7, 0x6CB0, 0x6CF9, 0x6CCF, 0x6CE9, 0x6CD1, 0x7094, 0x7098, 0x7085, 0x7093, 0x7086, 0x7084, 0x7091, 0x7096, 0x7082, 0x709A, 0x7083, 0x726A, 0x72D6, 0x72CB, 0x72D8, 0x72C9, 0x72DC, 0x72D2, 0x72D4, 0x72DA, 0x72CC, 0x72D1, 0x73A4, 0x73A1, 0x73AD, 0x73A6, 0x73A2, 0x73A0, 0x73AC, 0x739D, 0x74DD, 0x74E8, 0x753F, 0x7540, 0x753E, 0x758C, 0x7598, 0x76AF, 0x76F3, 0x76F1, 0x76F0, 0x76F5, 0x77F8, 0x77FC, 0x77F9, 0x77FB, 0x77FA, 0x77F7, 0x7942, 0x793F, 0x79C5, 0x7A78, 0x7A7B, 0x7AFB, 0x7C75, 0x7CFD, 0x8035, 0x808F, 0x80AE, 0x80A3, 0x80B8, 0x80B5, 0x80AD, 0x8220, 0x82A0, 0x82C0, 0x82AB, 0x829A, 0x8298, 0x829B, 0x82B5, 0x82A7, 0x82AE, 0x82BC, 0x829E, 0x82BA, 0x82B4, 0x82A8, 0x82A1, 0x82A9, 0x82C2, 0x82A4, 0x82C3, 0x82B6, 0x82A2, 0x8670, 0x866F, 0x866D, 0x866E, 0x8C56, 0x8FD2, 0x8FCB, 0x8FD3, 0x8FCD, 0x8FD6, 0x8FD5, 0x8FD7, 0x90B2, 0x90B4, 0x90AF, 0x90B3, 0x90B0, 0x9639, 0x963D, 0x963C, 0x963A, 0x9643, 0x4FCD, 0x4FC5, 0x4FD3, 0x4FB2, 0x4FC9, 0x4FCB, 0x4FC1, 0x4FD4, 0x4FDC, 0x4FD9, 0x4FBB, 0x4FB3, 0x4FDB, 0x4FC7, 0x4FD6, 0x4FBA, 0x4FC0, 0x4FB9, 0x4FEC, 0x5244, 0x5249, 0x52C0, 0x52C2, 0x533D, 0x537C, 0x5397, 0x5396, 0x5399, 0x5398, 0x54BA, 0x54A1, 0x54AD, 0x54A5, 0x54CF, + 0x54C3, 0x830D, 0x54B7, 0x54AE, 0x54D6, 0x54B6, 0x54C5, 0x54C6, 0x54A0, 0x5470, 0x54BC, 0x54A2, 0x54BE, 0x5472, 0x54DE, 0x54B0, 0x57B5, 0x579E, 0x579F, 0x57A4, 0x578C, 0x5797, 0x579D, 0x579B, 0x5794, 0x5798, 0x578F, 0x5799, 0x57A5, 0x579A, 0x5795, 0x58F4, 0x590D, 0x5953, 0x59E1, 0x59DE, 0x59EE, 0x5A00, 0x59F1, 0x59DD, 0x59FA, 0x59FD, 0x59FC, 0x59F6, 0x59E4, 0x59F2, 0x59F7, 0x59DB, 0x59E9, 0x59F3, 0x59F5, 0x59E0, 0x59FE, 0x59F4, 0x59ED, 0x5BA8, 0x5C4C, 0x5CD0, 0x5CD8, 0x5CCC, 0x5CD7, 0x5CCB, 0x5CDB, 0x5CDE, 0x5CDA, 0x5CC9, 0x5CC7, 0x5CCA, 0x5CD6, 0x5CD3, 0x5CD4, 0x5CCF, 0x5CC8, 0x5CC6, 0x5CCE, 0x5CDF, 0x5CF8, 0x5DF9, 0x5E21, 0x5E22, 0x5E23, 0x5E20, 0x5E24, 0x5EB0, 0x5EA4, 0x5EA2, 0x5E9B, 0x5EA3, 0x5EA5, 0x5F07, 0x5F2E, 0x5F56, 0x5F86, 0x6037, 0x6039, 0x6054, 0x6072, 0x605E, 0x6045, 0x6053, 0x6047, 0x6049, 0x605B, 0x604C, 0x6040, 0x6042, 0x605F, 0x6024, 0x6044, 0x6058, 0x6066, 0x606E, 0x6242, 0x6243, 0x62CF, 0x630D, 0x630B, 0x62F5, 0x630E, 0x6303, 0x62EB, 0x62F9, 0x630F, 0x630C, 0x62F8, 0x62F6, 0x6300, 0x6313, 0x6314, 0x62FA, 0x6315, 0x62FB, 0x62F0, 0x6541, 0x6543, 0x65AA, 0x65BF, 0x6636, 0x6621, 0x6632, 0x6635, 0x661C, 0x6626, 0x6622, 0x6633, 0x662B, 0x663A, 0x661D, 0x6634, 0x6639, 0x662E, 0x670F, 0x6710, 0x67C1, 0x67F2, 0x67C8, 0x67BA, + 0x67DC, 0x67BB, 0x67F8, 0x67D8, 0x67C0, 0x67B7, 0x67C5, 0x67EB, 0x67E4, 0x67DF, 0x67B5, 0x67CD, 0x67B3, 0x67F7, 0x67F6, 0x67EE, 0x67E3, 0x67C2, 0x67B9, 0x67CE, 0x67E7, 0x67F0, 0x67B2, 0x67FC, 0x67C6, 0x67ED, 0x67CC, 0x67AE, 0x67E6, 0x67DB, 0x67FA, 0x67C9, 0x67CA, 0x67C3, 0x67EA, 0x67CB, 0x6B28, 0x6B82, 0x6B84, 0x6BB6, 0x6BD6, 0x6BD8, 0x6BE0, 0x6C20, 0x6C21, 0x6D28, 0x6D34, 0x6D2D, 0x6D1F, 0x6D3C, 0x6D3F, 0x6D12, 0x6D0A, 0x6CDA, 0x6D33, 0x6D04, 0x6D19, 0x6D3A, 0x6D1A, 0x6D11, 0x6D00, 0x6D1D, 0x6D42, 0x6D01, 0x6D18, 0x6D37, 0x6D03, 0x6D0F, 0x6D40, 0x6D07, 0x6D20, 0x6D2C, 0x6D08, 0x6D22, 0x6D09, 0x6D10, 0x70B7, 0x709F, 0x70BE, 0x70B1, 0x70B0, 0x70A1, 0x70B4, 0x70B5, 0x70A9, 0x7241, 0x7249, 0x724A, 0x726C, 0x7270, 0x7273, 0x726E, 0x72CA, 0x72E4, 0x72E8, 0x72EB, 0x72DF, 0x72EA, 0x72E6, 0x72E3, 0x7385, 0x73CC, 0x73C2, 0x73C8, 0x73C5, 0x73B9, 0x73B6, 0x73B5, 0x73B4, 0x73EB, 0x73BF, 0x73C7, 0x73BE, 0x73C3, 0x73C6, 0x73B8, 0x73CB, 0x74EC, 0x74EE, 0x752E, 0x7547, 0x7548, 0x75A7, 0x75AA, 0x7679, 0x76C4, 0x7708, 0x7703, 0x7704, 0x7705, 0x770A, 0x76F7, 0x76FB, 0x76FA, 0x77E7, 0x77E8, 0x7806, 0x7811, 0x7812, 0x7805, 0x7810, 0x780F, 0x780E, 0x7809, 0x7803, 0x7813, 0x794A, 0x794C, 0x794B, 0x7945, 0x7944, 0x79D5, 0x79CD, 0x79CF, 0x79D6, 0x79CE, 0x7A80, + 0x7A7E, 0x7AD1, 0x7B00, 0x7B01, 0x7C7A, 0x7C78, 0x7C79, 0x7C7F, 0x7C80, 0x7C81, 0x7D03, 0x7D08, 0x7D01, 0x7F58, 0x7F91, 0x7F8D, 0x7FBE, 0x8007, 0x800E, 0x800F, 0x8014, 0x8037, 0x80D8, 0x80C7, 0x80E0, 0x80D1, 0x80C8, 0x80C2, 0x80D0, 0x80C5, 0x80E3, 0x80D9, 0x80DC, 0x80CA, 0x80D5, 0x80C9, 0x80CF, 0x80D7, 0x80E6, 0x80CD, 0x81FF, 0x8221, 0x8294, 0x82D9, 0x82FE, 0x82F9, 0x8307, 0x82E8, 0x8300, 0x82D5, 0x833A, 0x82EB, 0x82D6, 0x82F4, 0x82EC, 0x82E1, 0x82F2, 0x82F5, 0x830C, 0x82FB, 0x82F6, 0x82F0, 0x82EA, 0x82E4, 0x82E0, 0x82FA, 0x82F3, 0x82ED, 0x8677, 0x8674, 0x867C, 0x8673, 0x8841, 0x884E, 0x8867, 0x886A, 0x8869, 0x89D3, 0x8A04, 0x8A07, 0x8D72, 0x8FE3, 0x8FE1, 0x8FEE, 0x8FE0, 0x90F1, 0x90BD, 0x90BF, 0x90D5, 0x90C5, 0x90BE, 0x90C7, 0x90CB, 0x90C8, 0x91D4, 0x91D3, 0x9654, 0x964F, 0x9651, 0x9653, 0x964A, 0x964E, 0x501E, 0x5005, 0x5007, 0x5013, 0x5022, 0x5030, 0x501B, 0x4FF5, 0x4FF4, 0x5033, 0x5037, 0x502C, 0x4FF6, 0x4FF7, 0x5017, 0x501C, 0x5020, 0x5027, 0x5035, 0x502F, 0x5031, 0x500E, 0x515A, 0x5194, 0x5193, 0x51CA, 0x51C4, 0x51C5, 0x51C8, 0x51CE, 0x5261, 0x525A, 0x5252, 0x525E, 0x525F, 0x5255, 0x5262, 0x52CD, 0x530E, 0x539E, 0x5526, 0x54E2, 0x5517, 0x5512, 0x54E7, 0x54F3, 0x54E4, 0x551A, 0x54FF, 0x5504, 0x5508, 0x54EB, 0x5511, 0x5505, 0x54F1, + 0x550A, 0x54FB, 0x54F7, 0x54F8, 0x54E0, 0x550E, 0x5503, 0x550B, 0x5701, 0x5702, 0x57CC, 0x5832, 0x57D5, 0x57D2, 0x57BA, 0x57C6, 0x57BD, 0x57BC, 0x57B8, 0x57B6, 0x57BF, 0x57C7, 0x57D0, 0x57B9, 0x57C1, 0x590E, 0x594A, 0x5A19, 0x5A16, 0x5A2D, 0x5A2E, 0x5A15, 0x5A0F, 0x5A17, 0x5A0A, 0x5A1E, 0x5A33, 0x5B6C, 0x5BA7, 0x5BAD, 0x5BAC, 0x5C03, 0x5C56, 0x5C54, 0x5CEC, 0x5CFF, 0x5CEE, 0x5CF1, 0x5CF7, 0x5D00, 0x5CF9, 0x5E29, 0x5E28, 0x5EA8, 0x5EAE, 0x5EAA, 0x5EAC, 0x5F33, 0x5F30, 0x5F67, 0x605D, 0x605A, 0x6067, 0x6041, 0x60A2, 0x6088, 0x6080, 0x6092, 0x6081, 0x609D, 0x6083, 0x6095, 0x609B, 0x6097, 0x6087, 0x609C, 0x608E, 0x6219, 0x6246, 0x62F2, 0x6310, 0x6356, 0x632C, 0x6344, 0x6345, 0x6336, 0x6343, 0x63E4, 0x6339, 0x634B, 0x634A, 0x633C, 0x6329, 0x6341, 0x6334, 0x6358, 0x6354, 0x6359, 0x632D, 0x6347, 0x6333, 0x635A, 0x6351, 0x6338, 0x6357, 0x6340, 0x6348, 0x654A, 0x6546, 0x65C6, 0x65C3, 0x65C4, 0x65C2, 0x664A, 0x665F, 0x6647, 0x6651, 0x6712, 0x6713, 0x681F, 0x681A, 0x6849, 0x6832, 0x6833, 0x683B, 0x684B, 0x684F, 0x6816, 0x6831, 0x681C, 0x6835, 0x682B, 0x682D, 0x682F, 0x684E, 0x6844, 0x6834, 0x681D, 0x6812, 0x6814, 0x6826, 0x6828, 0x682E, 0x684D, 0x683A, 0x6825, 0x6820, 0x6B2C, 0x6B2F, 0x6B2D, 0x6B31, 0x6B34, 0x6B6D, 0x8082, 0x6B88, 0x6BE6, 0x6BE4, + 0x6BE8, 0x6BE3, 0x6BE2, 0x6BE7, 0x6C25, 0x6D7A, 0x6D63, 0x6D64, 0x6D76, 0x6D0D, 0x6D61, 0x6D92, 0x6D58, 0x6D62, 0x6D6D, 0x6D6F, 0x6D91, 0x6D8D, 0x6DEF, 0x6D7F, 0x6D86, 0x6D5E, 0x6D67, 0x6D60, 0x6D97, 0x6D70, 0x6D7C, 0x6D5F, 0x6D82, 0x6D98, 0x6D2F, 0x6D68, 0x6D8B, 0x6D7E, 0x6D80, 0x6D84, 0x6D16, 0x6D83, 0x6D7B, 0x6D7D, 0x6D75, 0x6D90, 0x70DC, 0x70D3, 0x70D1, 0x70DD, 0x70CB, 0x7F39, 0x70E2, 0x70D7, 0x70D2, 0x70DE, 0x70E0, 0x70D4, 0x70CD, 0x70C5, 0x70C6, 0x70C7, 0x70DA, 0x70CE, 0x70E1, 0x7242, 0x7278, 0x7277, 0x7276, 0x7300, 0x72FA, 0x72F4, 0x72FE, 0x72F6, 0x72F3, 0x72FB, 0x7301, 0x73D3, 0x73D9, 0x73E5, 0x73D6, 0x73BC, 0x73E7, 0x73E3, 0x73E9, 0x73DC, 0x73D2, 0x73DB, 0x73D4, 0x73DD, 0x73DA, 0x73D7, 0x73D8, 0x73E8, 0x74DE, 0x74DF, 0x74F4, 0x74F5, 0x7521, 0x755B, 0x755F, 0x75B0, 0x75C1, 0x75BB, 0x75C4, 0x75C0, 0x75BF, 0x75B6, 0x75BA, 0x768A, 0x76C9, 0x771D, 0x771B, 0x7710, 0x7713, 0x7712, 0x7723, 0x7711, 0x7715, 0x7719, 0x771A, 0x7722, 0x7727, 0x7823, 0x782C, 0x7822, 0x7835, 0x782F, 0x7828, 0x782E, 0x782B, 0x7821, 0x7829, 0x7833, 0x782A, 0x7831, 0x7954, 0x795B, 0x794F, 0x795C, 0x7953, 0x7952, 0x7951, 0x79EB, 0x79EC, 0x79E0, 0x79EE, 0x79ED, 0x79EA, 0x79DC, 0x79DE, 0x79DD, 0x7A86, 0x7A89, 0x7A85, 0x7A8B, 0x7A8C, 0x7A8A, 0x7A87, 0x7AD8, 0x7B10, + 0x7B04, 0x7B13, 0x7B05, 0x7B0F, 0x7B08, 0x7B0A, 0x7B0E, 0x7B09, 0x7B12, 0x7C84, 0x7C91, 0x7C8A, 0x7C8C, 0x7C88, 0x7C8D, 0x7C85, 0x7D1E, 0x7D1D, 0x7D11, 0x7D0E, 0x7D18, 0x7D16, 0x7D13, 0x7D1F, 0x7D12, 0x7D0F, 0x7D0C, 0x7F5C, 0x7F61, 0x7F5E, 0x7F60, 0x7F5D, 0x7F5B, 0x7F96, 0x7F92, 0x7FC3, 0x7FC2, 0x7FC0, 0x8016, 0x803E, 0x8039, 0x80FA, 0x80F2, 0x80F9, 0x80F5, 0x8101, 0x80FB, 0x8100, 0x8201, 0x822F, 0x8225, 0x8333, 0x832D, 0x8344, 0x8319, 0x8351, 0x8325, 0x8356, 0x833F, 0x8341, 0x8326, 0x831C, 0x8322, 0x8342, 0x834E, 0x831B, 0x832A, 0x8308, 0x833C, 0x834D, 0x8316, 0x8324, 0x8320, 0x8337, 0x832F, 0x8329, 0x8347, 0x8345, 0x834C, 0x8353, 0x831E, 0x832C, 0x834B, 0x8327, 0x8348, 0x8653, 0x8652, 0x86A2, 0x86A8, 0x8696, 0x868D, 0x8691, 0x869E, 0x8687, 0x8697, 0x8686, 0x868B, 0x869A, 0x8685, 0x86A5, 0x8699, 0x86A1, 0x86A7, 0x8695, 0x8698, 0x868E, 0x869D, 0x8690, 0x8694, 0x8843, 0x8844, 0x886D, 0x8875, 0x8876, 0x8872, 0x8880, 0x8871, 0x887F, 0x886F, 0x8883, 0x887E, 0x8874, 0x887C, 0x8A12, 0x8C47, 0x8C57, 0x8C7B, 0x8CA4, 0x8CA3, 0x8D76, 0x8D78, 0x8DB5, 0x8DB7, 0x8DB6, 0x8ED1, 0x8ED3, 0x8FFE, 0x8FF5, 0x9002, 0x8FFF, 0x8FFB, 0x9004, 0x8FFC, 0x8FF6, 0x90D6, 0x90E0, 0x90D9, 0x90DA, 0x90E3, 0x90DF, 0x90E5, 0x90D8, 0x90DB, 0x90D7, 0x90DC, 0x90E4, 0x9150, + 0x914E, 0x914F, 0x91D5, 0x91E2, 0x91DA, 0x965C, 0x965F, 0x96BC, 0x98E3, 0x9ADF, 0x9B2F, 0x4E7F, 0x5070, 0x506A, 0x5061, 0x505E, 0x5060, 0x5053, 0x504B, 0x505D, 0x5072, 0x5048, 0x504D, 0x5041, 0x505B, 0x504A, 0x5062, 0x5015, 0x5045, 0x505F, 0x5069, 0x506B, 0x5063, 0x5064, 0x5046, 0x5040, 0x506E, 0x5073, 0x5057, 0x5051, 0x51D0, 0x526B, 0x526D, 0x526C, 0x526E, 0x52D6, 0x52D3, 0x532D, 0x539C, 0x5575, 0x5576, 0x553C, 0x554D, 0x5550, 0x5534, 0x552A, 0x5551, 0x5562, 0x5536, 0x5535, 0x5530, 0x5552, 0x5545, 0x550C, 0x5532, 0x5565, 0x554E, 0x5539, 0x5548, 0x552D, 0x553B, 0x5540, 0x554B, 0x570A, 0x5707, 0x57FB, 0x5814, 0x57E2, 0x57F6, 0x57DC, 0x57F4, 0x5800, 0x57ED, 0x57FD, 0x5808, 0x57F8, 0x580B, 0x57F3, 0x57CF, 0x5807, 0x57EE, 0x57E3, 0x57F2, 0x57E5, 0x57EC, 0x57E1, 0x580E, 0x57FC, 0x5810, 0x57E7, 0x5801, 0x580C, 0x57F1, 0x57E9, 0x57F0, 0x580D, 0x5804, 0x595C, 0x5A60, 0x5A58, 0x5A55, 0x5A67, 0x5A5E, 0x5A38, 0x5A35, 0x5A6D, 0x5A50, 0x5A5F, 0x5A65, 0x5A6C, 0x5A53, 0x5A64, 0x5A57, 0x5A43, 0x5A5D, 0x5A52, 0x5A44, 0x5A5B, 0x5A48, 0x5A8E, 0x5A3E, 0x5A4D, 0x5A39, 0x5A4C, 0x5A70, 0x5A69, 0x5A47, 0x5A51, 0x5A56, 0x5A42, 0x5A5C, 0x5B72, 0x5B6E, 0x5BC1, 0x5BC0, 0x5C59, 0x5D1E, 0x5D0B, 0x5D1D, 0x5D1A, 0x5D20, 0x5D0C, 0x5D28, 0x5D0D, 0x5D26, 0x5D25, 0x5D0F, + 0x5D30, 0x5D12, 0x5D23, 0x5D1F, 0x5D2E, 0x5E3E, 0x5E34, 0x5EB1, 0x5EB4, 0x5EB9, 0x5EB2, 0x5EB3, 0x5F36, 0x5F38, 0x5F9B, 0x5F96, 0x5F9F, 0x608A, 0x6090, 0x6086, 0x60BE, 0x60B0, 0x60BA, 0x60D3, 0x60D4, 0x60CF, 0x60E4, 0x60D9, 0x60DD, 0x60C8, 0x60B1, 0x60DB, 0x60B7, 0x60CA, 0x60BF, 0x60C3, 0x60CD, 0x60C0, 0x6332, 0x6365, 0x638A, 0x6382, 0x637D, 0x63BD, 0x639E, 0x63AD, 0x639D, 0x6397, 0x63AB, 0x638E, 0x636F, 0x6387, 0x6390, 0x636E, 0x63AF, 0x6375, 0x639C, 0x636D, 0x63AE, 0x637C, 0x63A4, 0x633B, 0x639F, 0x6378, 0x6385, 0x6381, 0x6391, 0x638D, 0x6370, 0x6553, 0x65CD, 0x6665, 0x6661, 0x665B, 0x6659, 0x665C, 0x6662, 0x6718, 0x6879, 0x6887, 0x6890, 0x689C, 0x686D, 0x686E, 0x68AE, 0x68AB, 0x6956, 0x686F, 0x68A3, 0x68AC, 0x68A9, 0x6875, 0x6874, 0x68B2, 0x688F, 0x6877, 0x6892, 0x687C, 0x686B, 0x6872, 0x68AA, 0x6880, 0x6871, 0x687E, 0x689B, 0x6896, 0x688B, 0x68A0, 0x6889, 0x68A4, 0x6878, 0x687B, 0x6891, 0x688C, 0x688A, 0x687D, 0x6B36, 0x6B33, 0x6B37, 0x6B38, 0x6B91, 0x6B8F, 0x6B8D, 0x6B8E, 0x6B8C, 0x6C2A, 0x6DC0, 0x6DAB, 0x6DB4, 0x6DB3, 0x6E74, 0x6DAC, 0x6DE9, 0x6DE2, 0x6DB7, 0x6DF6, 0x6DD4, 0x6E00, 0x6DC8, 0x6DE0, 0x6DDF, 0x6DD6, 0x6DBE, 0x6DE5, 0x6DDC, 0x6DDD, 0x6DDB, 0x6DF4, 0x6DCA, 0x6DBD, 0x6DED, 0x6DF0, 0x6DBA, 0x6DD5, 0x6DC2, 0x6DCF, 0x6DC9, + 0x6DD0, 0x6DF2, 0x6DD3, 0x6DFD, 0x6DD7, 0x6DCD, 0x6DE3, 0x6DBB, 0x70FA, 0x710D, 0x70F7, 0x7117, 0x70F4, 0x710C, 0x70F0, 0x7104, 0x70F3, 0x7110, 0x70FC, 0x70FF, 0x7106, 0x7113, 0x7100, 0x70F8, 0x70F6, 0x710B, 0x7102, 0x710E, 0x727E, 0x727B, 0x727C, 0x727F, 0x731D, 0x7317, 0x7307, 0x7311, 0x7318, 0x730A, 0x7308, 0x72FF, 0x730F, 0x731E, 0x7388, 0x73F6, 0x73F8, 0x73F5, 0x7404, 0x7401, 0x73FD, 0x7407, 0x7400, 0x73FA, 0x73FC, 0x73FF, 0x740C, 0x740B, 0x73F4, 0x7408, 0x7564, 0x7563, 0x75CE, 0x75D2, 0x75CF, 0x75CB, 0x75CC, 0x75D1, 0x75D0, 0x768F, 0x7689, 0x76D3, 0x7739, 0x772F, 0x772D, 0x7731, 0x7732, 0x7734, 0x7733, 0x773D, 0x7725, 0x773B, 0x7735, 0x7848, 0x7852, 0x7849, 0x784D, 0x784A, 0x784C, 0x7826, 0x7845, 0x7850, 0x7964, 0x7967, 0x7969, 0x796A, 0x7963, 0x796B, 0x7961, 0x79BB, 0x79FA, 0x79F8, 0x79F6, 0x79F7, 0x7A8F, 0x7A94, 0x7A90, 0x7B35, 0x7B47, 0x7B34, 0x7B25, 0x7B30, 0x7B22, 0x7B24, 0x7B33, 0x7B18, 0x7B2A, 0x7B1D, 0x7B31, 0x7B2B, 0x7B2D, 0x7B2F, 0x7B32, 0x7B38, 0x7B1A, 0x7B23, 0x7C94, 0x7C98, 0x7C96, 0x7CA3, 0x7D35, 0x7D3D, 0x7D38, 0x7D36, 0x7D3A, 0x7D45, 0x7D2C, 0x7D29, 0x7D41, 0x7D47, 0x7D3E, 0x7D3F, 0x7D4A, 0x7D3B, 0x7D28, 0x7F63, 0x7F95, 0x7F9C, 0x7F9D, 0x7F9B, 0x7FCA, 0x7FCB, 0x7FCD, 0x7FD0, 0x7FD1, 0x7FC7, 0x7FCF, 0x7FC9, 0x801F, + 0x801E, 0x801B, 0x8047, 0x8043, 0x8048, 0x8118, 0x8125, 0x8119, 0x811B, 0x812D, 0x811F, 0x812C, 0x811E, 0x8121, 0x8115, 0x8127, 0x811D, 0x8122, 0x8211, 0x8238, 0x8233, 0x823A, 0x8234, 0x8232, 0x8274, 0x8390, 0x83A3, 0x83A8, 0x838D, 0x837A, 0x8373, 0x83A4, 0x8374, 0x838F, 0x8381, 0x8395, 0x8399, 0x8375, 0x8394, 0x83A9, 0x837D, 0x8383, 0x838C, 0x839D, 0x839B, 0x83AA, 0x838B, 0x837E, 0x83A5, 0x83AF, 0x8388, 0x8397, 0x83B0, 0x837F, 0x83A6, 0x8387, 0x83AE, 0x8376, 0x839A, 0x8659, 0x8656, 0x86BF, 0x86B7, 0x86C2, 0x86C1, 0x86C5, 0x86BA, 0x86B0, 0x86C8, 0x86B9, 0x86B3, 0x86B8, 0x86CC, 0x86B4, 0x86BB, 0x86BC, 0x86C3, 0x86BD, 0x86BE, 0x8852, 0x8889, 0x8895, 0x88A8, 0x88A2, 0x88AA, 0x889A, 0x8891, 0x88A1, 0x889F, 0x8898, 0x88A7, 0x8899, 0x889B, 0x8897, 0x88A4, 0x88AC, 0x888C, 0x8893, 0x888E, 0x8982, 0x89D6, 0x89D9, 0x89D5, 0x8A30, 0x8A27, 0x8A2C, 0x8A1E, 0x8C39, 0x8C3B, 0x8C5C, 0x8C5D, 0x8C7D, 0x8CA5, 0x8D7D, 0x8D7B, 0x8D79, 0x8DBC, 0x8DC2, 0x8DB9, 0x8DBF, 0x8DC1, 0x8ED8, 0x8EDE, 0x8EDD, 0x8EDC, 0x8ED7, 0x8EE0, 0x8EE1, 0x9024, 0x900B, 0x9011, 0x901C, 0x900C, 0x9021, 0x90EF, 0x90EA, 0x90F0, 0x90F4, 0x90F2, 0x90F3, 0x90D4, 0x90EB, 0x90EC, 0x90E9, 0x9156, 0x9158, 0x915A, 0x9153, 0x9155, 0x91EC, 0x91F4, 0x91F1, 0x91F3, 0x91F8, 0x91E4, 0x91F9, 0x91EA, + 0x91EB, 0x91F7, 0x91E8, 0x91EE, 0x957A, 0x9586, 0x9588, 0x967C, 0x966D, 0x966B, 0x9671, 0x966F, 0x96BF, 0x976A, 0x9804, 0x98E5, 0x9997, 0x509B, 0x5095, 0x5094, 0x509E, 0x508B, 0x50A3, 0x5083, 0x508C, 0x508E, 0x509D, 0x5068, 0x509C, 0x5092, 0x5082, 0x5087, 0x515F, 0x51D4, 0x5312, 0x5311, 0x53A4, 0x53A7, 0x5591, 0x55A8, 0x55A5, 0x55AD, 0x5577, 0x5645, 0x55A2, 0x5593, 0x5588, 0x558F, 0x55B5, 0x5581, 0x55A3, 0x5592, 0x55A4, 0x557D, 0x558C, 0x55A6, 0x557F, 0x5595, 0x55A1, 0x558E, 0x570C, 0x5829, 0x5837, 0x5819, 0x581E, 0x5827, 0x5823, 0x5828, 0x57F5, 0x5848, 0x5825, 0x581C, 0x581B, 0x5833, 0x583F, 0x5836, 0x582E, 0x5839, 0x5838, 0x582D, 0x582C, 0x583B, 0x5961, 0x5AAF, 0x5A94, 0x5A9F, 0x5A7A, 0x5AA2, 0x5A9E, 0x5A78, 0x5AA6, 0x5A7C, 0x5AA5, 0x5AAC, 0x5A95, 0x5AAE, 0x5A37, 0x5A84, 0x5A8A, 0x5A97, 0x5A83, 0x5A8B, 0x5AA9, 0x5A7B, 0x5A7D, 0x5A8C, 0x5A9C, 0x5A8F, 0x5A93, 0x5A9D, 0x5BEA, 0x5BCD, 0x5BCB, 0x5BD4, 0x5BD1, 0x5BCA, 0x5BCE, 0x5C0C, 0x5C30, 0x5D37, 0x5D43, 0x5D6B, 0x5D41, 0x5D4B, 0x5D3F, 0x5D35, 0x5D51, 0x5D4E, 0x5D55, 0x5D33, 0x5D3A, 0x5D52, 0x5D3D, 0x5D31, 0x5D59, 0x5D42, 0x5D39, 0x5D49, 0x5D38, 0x5D3C, 0x5D32, 0x5D36, 0x5D40, 0x5D45, 0x5E44, 0x5E41, 0x5F58, 0x5FA6, 0x5FA5, 0x5FAB, 0x60C9, 0x60B9, 0x60CC, 0x60E2, 0x60CE, 0x60C4, 0x6114, + 0x60F2, 0x610A, 0x6116, 0x6105, 0x60F5, 0x6113, 0x60F8, 0x60FC, 0x60FE, 0x60C1, 0x6103, 0x6118, 0x611D, 0x6110, 0x60FF, 0x6104, 0x610B, 0x624A, 0x6394, 0x63B1, 0x63B0, 0x63CE, 0x63E5, 0x63E8, 0x63EF, 0x63C3, 0x649D, 0x63F3, 0x63CA, 0x63E0, 0x63F6, 0x63D5, 0x63F2, 0x63F5, 0x6461, 0x63DF, 0x63BE, 0x63DD, 0x63DC, 0x63C4, 0x63D8, 0x63D3, 0x63C2, 0x63C7, 0x63CC, 0x63CB, 0x63C8, 0x63F0, 0x63D7, 0x63D9, 0x6532, 0x6567, 0x656A, 0x6564, 0x655C, 0x6568, 0x6565, 0x658C, 0x659D, 0x659E, 0x65AE, 0x65D0, 0x65D2, 0x667C, 0x666C, 0x667B, 0x6680, 0x6671, 0x6679, 0x666A, 0x6672, 0x6701, 0x690C, 0x68D3, 0x6904, 0x68DC, 0x692A, 0x68EC, 0x68EA, 0x68F1, 0x690F, 0x68D6, 0x68F7, 0x68EB, 0x68E4, 0x68F6, 0x6913, 0x6910, 0x68F3, 0x68E1, 0x6907, 0x68CC, 0x6908, 0x6970, 0x68B4, 0x6911, 0x68EF, 0x68C6, 0x6914, 0x68F8, 0x68D0, 0x68FD, 0x68FC, 0x68E8, 0x690B, 0x690A, 0x6917, 0x68CE, 0x68C8, 0x68DD, 0x68DE, 0x68E6, 0x68F4, 0x68D1, 0x6906, 0x68D4, 0x68E9, 0x6915, 0x6925, 0x68C7, 0x6B39, 0x6B3B, 0x6B3F, 0x6B3C, 0x6B94, 0x6B97, 0x6B99, 0x6B95, 0x6BBD, 0x6BF0, 0x6BF2, 0x6BF3, 0x6C30, 0x6DFC, 0x6E46, 0x6E47, 0x6E1F, 0x6E49, 0x6E88, 0x6E3C, 0x6E3D, 0x6E45, 0x6E62, 0x6E2B, 0x6E3F, 0x6E41, 0x6E5D, 0x6E73, 0x6E1C, 0x6E33, 0x6E4B, 0x6E40, 0x6E51, 0x6E3B, 0x6E03, 0x6E2E, 0x6E5E, + 0x6E68, 0x6E5C, 0x6E61, 0x6E31, 0x6E28, 0x6E60, 0x6E71, 0x6E6B, 0x6E39, 0x6E22, 0x6E30, 0x6E53, 0x6E65, 0x6E27, 0x6E78, 0x6E64, 0x6E77, 0x6E55, 0x6E79, 0x6E52, 0x6E66, 0x6E35, 0x6E36, 0x6E5A, 0x7120, 0x711E, 0x712F, 0x70FB, 0x712E, 0x7131, 0x7123, 0x7125, 0x7122, 0x7132, 0x711F, 0x7128, 0x713A, 0x711B, 0x724B, 0x725A, 0x7288, 0x7289, 0x7286, 0x7285, 0x728B, 0x7312, 0x730B, 0x7330, 0x7322, 0x7331, 0x7333, 0x7327, 0x7332, 0x732D, 0x7326, 0x7323, 0x7335, 0x730C, 0x742E, 0x742C, 0x7430, 0x742B, 0x7416, 0x741A, 0x7421, 0x742D, 0x7431, 0x7424, 0x7423, 0x741D, 0x7429, 0x7420, 0x7432, 0x74FB, 0x752F, 0x756F, 0x756C, 0x75E7, 0x75DA, 0x75E1, 0x75E6, 0x75DD, 0x75DF, 0x75E4, 0x75D7, 0x7695, 0x7692, 0x76DA, 0x7746, 0x7747, 0x7744, 0x774D, 0x7745, 0x774A, 0x774E, 0x774B, 0x774C, 0x77DE, 0x77EC, 0x7860, 0x7864, 0x7865, 0x785C, 0x786D, 0x7871, 0x786A, 0x786E, 0x7870, 0x7869, 0x7868, 0x785E, 0x7862, 0x7974, 0x7973, 0x7972, 0x7970, 0x7A02, 0x7A0A, 0x7A03, 0x7A0C, 0x7A04, 0x7A99, 0x7AE6, 0x7AE4, 0x7B4A, 0x7B3B, 0x7B44, 0x7B48, 0x7B4C, 0x7B4E, 0x7B40, 0x7B58, 0x7B45, 0x7CA2, 0x7C9E, 0x7CA8, 0x7CA1, 0x7D58, 0x7D6F, 0x7D63, 0x7D53, 0x7D56, 0x7D67, 0x7D6A, 0x7D4F, 0x7D6D, 0x7D5C, 0x7D6B, 0x7D52, 0x7D54, 0x7D69, 0x7D51, 0x7D5F, 0x7D4E, 0x7F3E, 0x7F3F, 0x7F65, + 0x7F66, 0x7FA2, 0x7FA0, 0x7FA1, 0x7FD7, 0x8051, 0x804F, 0x8050, 0x80FE, 0x80D4, 0x8143, 0x814A, 0x8152, 0x814F, 0x8147, 0x813D, 0x814D, 0x813A, 0x81E6, 0x81EE, 0x81F7, 0x81F8, 0x81F9, 0x8204, 0x823C, 0x823D, 0x823F, 0x8275, 0x833B, 0x83CF, 0x83F9, 0x8423, 0x83C0, 0x83E8, 0x8412, 0x83E7, 0x83E4, 0x83FC, 0x83F6, 0x8410, 0x83C6, 0x83C8, 0x83EB, 0x83E3, 0x83BF, 0x8401, 0x83DD, 0x83E5, 0x83D8, 0x83FF, 0x83E1, 0x83CB, 0x83CE, 0x83D6, 0x83F5, 0x83C9, 0x8409, 0x840F, 0x83DE, 0x8411, 0x8406, 0x83C2, 0x83F3, 0x83D5, 0x83FA, 0x83C7, 0x83D1, 0x83EA, 0x8413, 0x83C3, 0x83EC, 0x83EE, 0x83C4, 0x83FB, 0x83D7, 0x83E2, 0x841B, 0x83DB, 0x83FE, 0x86D8, 0x86E2, 0x86E6, 0x86D3, 0x86E3, 0x86DA, 0x86EA, 0x86DD, 0x86EB, 0x86DC, 0x86EC, 0x86E9, 0x86D7, 0x86E8, 0x86D1, 0x8848, 0x8856, 0x8855, 0x88BA, 0x88D7, 0x88B9, 0x88B8, 0x88C0, 0x88BE, 0x88B6, 0x88BC, 0x88B7, 0x88BD, 0x88B2, 0x8901, 0x88C9, 0x8995, 0x8998, 0x8997, 0x89DD, 0x89DA, 0x89DB, 0x8A4E, 0x8A4D, 0x8A39, 0x8A59, 0x8A40, 0x8A57, 0x8A58, 0x8A44, 0x8A45, 0x8A52, 0x8A48, 0x8A51, 0x8A4A, 0x8A4C, 0x8A4F, 0x8C5F, 0x8C81, 0x8C80, 0x8CBA, 0x8CBE, 0x8CB0, 0x8CB9, 0x8CB5, 0x8D84, 0x8D80, 0x8D89, 0x8DD8, 0x8DD3, 0x8DCD, 0x8DC7, 0x8DD6, 0x8DDC, 0x8DCF, 0x8DD5, 0x8DD9, 0x8DC8, 0x8DD7, 0x8DC5, 0x8EEF, 0x8EF7, 0x8EFA, + 0x8EF9, 0x8EE6, 0x8EEE, 0x8EE5, 0x8EF5, 0x8EE7, 0x8EE8, 0x8EF6, 0x8EEB, 0x8EF1, 0x8EEC, 0x8EF4, 0x8EE9, 0x902D, 0x9034, 0x902F, 0x9106, 0x912C, 0x9104, 0x90FF, 0x90FC, 0x9108, 0x90F9, 0x90FB, 0x9101, 0x9100, 0x9107, 0x9105, 0x9103, 0x9161, 0x9164, 0x915F, 0x9162, 0x9160, 0x9201, 0x920A, 0x9225, 0x9203, 0x921A, 0x9226, 0x920F, 0x920C, 0x9200, 0x9212, 0x91FF, 0x91FD, 0x9206, 0x9204, 0x9227, 0x9202, 0x921C, 0x9224, 0x9219, 0x9217, 0x9205, 0x9216, 0x957B, 0x958D, 0x958C, 0x9590, 0x9687, 0x967E, 0x9688, 0x9689, 0x9683, 0x9680, 0x96C2, 0x96C8, 0x96C3, 0x96F1, 0x96F0, 0x976C, 0x9770, 0x976E, 0x9807, 0x98A9, 0x98EB, 0x9CE6, 0x9EF9, 0x4E83, 0x4E84, 0x4EB6, 0x50BD, 0x50BF, 0x50C6, 0x50AE, 0x50C4, 0x50CA, 0x50B4, 0x50C8, 0x50C2, 0x50B0, 0x50C1, 0x50BA, 0x50B1, 0x50CB, 0x50C9, 0x50B6, 0x50B8, 0x51D7, 0x527A, 0x5278, 0x527B, 0x527C, 0x55C3, 0x55DB, 0x55CC, 0x55D0, 0x55CB, 0x55CA, 0x55DD, 0x55C0, 0x55D4, 0x55C4, 0x55E9, 0x55BF, 0x55D2, 0x558D, 0x55CF, 0x55D5, 0x55E2, 0x55D6, 0x55C8, 0x55F2, 0x55CD, 0x55D9, 0x55C2, 0x5714, 0x5853, 0x5868, 0x5864, 0x584F, 0x584D, 0x5849, 0x586F, 0x5855, 0x584E, 0x585D, 0x5859, 0x5865, 0x585B, 0x583D, 0x5863, 0x5871, 0x58FC, 0x5AC7, 0x5AC4, 0x5ACB, 0x5ABA, 0x5AB8, 0x5AB1, 0x5AB5, 0x5AB0, 0x5ABF, 0x5AC8, 0x5ABB, 0x5AC6, + 0x5AB7, 0x5AC0, 0x5ACA, 0x5AB4, 0x5AB6, 0x5ACD, 0x5AB9, 0x5A90, 0x5BD6, 0x5BD8, 0x5BD9, 0x5C1F, 0x5C33, 0x5D71, 0x5D63, 0x5D4A, 0x5D65, 0x5D72, 0x5D6C, 0x5D5E, 0x5D68, 0x5D67, 0x5D62, 0x5DF0, 0x5E4F, 0x5E4E, 0x5E4A, 0x5E4D, 0x5E4B, 0x5EC5, 0x5ECC, 0x5EC6, 0x5ECB, 0x5EC7, 0x5F40, 0x5FAF, 0x5FAD, 0x60F7, 0x6149, 0x614A, 0x612B, 0x6145, 0x6136, 0x6132, 0x612E, 0x6146, 0x612F, 0x614F, 0x6129, 0x6140, 0x6220, 0x9168, 0x6223, 0x6225, 0x6224, 0x63C5, 0x63F1, 0x63EB, 0x6410, 0x6412, 0x6409, 0x6420, 0x6424, 0x6433, 0x6443, 0x641F, 0x6415, 0x6418, 0x6439, 0x6437, 0x6422, 0x6423, 0x640C, 0x6426, 0x6430, 0x6428, 0x6441, 0x6435, 0x642F, 0x640A, 0x641A, 0x6440, 0x6425, 0x6427, 0x640B, 0x63E7, 0x641B, 0x642E, 0x6421, 0x640E, 0x656F, 0x6592, 0x65D3, 0x6686, 0x668C, 0x6695, 0x6690, 0x668B, 0x668A, 0x6699, 0x6694, 0x6678, 0x6720, 0x6966, 0x695F, 0x6938, 0x694E, 0x6962, 0x6971, 0x693F, 0x6945, 0x696A, 0x6939, 0x6942, 0x6957, 0x6959, 0x697A, 0x6948, 0x6949, 0x6935, 0x696C, 0x6933, 0x693D, 0x6965, 0x68F0, 0x6978, 0x6934, 0x6969, 0x6940, 0x696F, 0x6944, 0x6976, 0x6958, 0x6941, 0x6974, 0x694C, 0x693B, 0x694B, 0x6937, 0x695C, 0x694F, 0x6951, 0x6932, 0x6952, 0x692F, 0x697B, 0x693C, 0x6B46, 0x6B45, 0x6B43, 0x6B42, 0x6B48, 0x6B41, 0x6B9B, 0xFA0D, 0x6BFB, 0x6BFC, + 0x6BF9, 0x6BF7, 0x6BF8, 0x6E9B, 0x6ED6, 0x6EC8, 0x6E8F, 0x6EC0, 0x6E9F, 0x6E93, 0x6E94, 0x6EA0, 0x6EB1, 0x6EB9, 0x6EC6, 0x6ED2, 0x6EBD, 0x6EC1, 0x6E9E, 0x6EC9, 0x6EB7, 0x6EB0, 0x6ECD, 0x6EA6, 0x6ECF, 0x6EB2, 0x6EBE, 0x6EC3, 0x6EDC, 0x6ED8, 0x6E99, 0x6E92, 0x6E8E, 0x6E8D, 0x6EA4, 0x6EA1, 0x6EBF, 0x6EB3, 0x6ED0, 0x6ECA, 0x6E97, 0x6EAE, 0x6EA3, 0x7147, 0x7154, 0x7152, 0x7163, 0x7160, 0x7141, 0x715D, 0x7162, 0x7172, 0x7178, 0x716A, 0x7161, 0x7142, 0x7158, 0x7143, 0x714B, 0x7170, 0x715F, 0x7150, 0x7153, 0x7144, 0x714D, 0x715A, 0x724F, 0x728D, 0x728C, 0x7291, 0x7290, 0x728E, 0x733C, 0x7342, 0x733B, 0x733A, 0x7340, 0x734A, 0x7349, 0x7444, 0x744A, 0x744B, 0x7452, 0x7451, 0x7457, 0x7440, 0x744F, 0x7450, 0x744E, 0x7442, 0x7446, 0x744D, 0x7454, 0x74E1, 0x74FF, 0x74FE, 0x74FD, 0x751D, 0x7579, 0x7577, 0x6983, 0x75EF, 0x760F, 0x7603, 0x75F7, 0x75FE, 0x75FC, 0x75F9, 0x75F8, 0x7610, 0x75FB, 0x75F6, 0x75ED, 0x75F5, 0x75FD, 0x7699, 0x76B5, 0x76DD, 0x7755, 0x775F, 0x7760, 0x7752, 0x7756, 0x775A, 0x7769, 0x7767, 0x7754, 0x7759, 0x776D, 0x77E0, 0x7887, 0x789A, 0x7894, 0x788F, 0x7884, 0x7895, 0x7885, 0x7886, 0x78A1, 0x7883, 0x7879, 0x7899, 0x7880, 0x7896, 0x787B, 0x797C, 0x7982, 0x797D, 0x7979, 0x7A11, 0x7A18, 0x7A19, 0x7A12, 0x7A17, 0x7A15, 0x7A22, 0x7A13, + 0x7A1B, 0x7A10, 0x7AA3, 0x7AA2, 0x7A9E, 0x7AEB, 0x7B66, 0x7B64, 0x7B6D, 0x7B74, 0x7B69, 0x7B72, 0x7B65, 0x7B73, 0x7B71, 0x7B70, 0x7B61, 0x7B78, 0x7B76, 0x7B63, 0x7CB2, 0x7CB4, 0x7CAF, 0x7D88, 0x7D86, 0x7D80, 0x7D8D, 0x7D7F, 0x7D85, 0x7D7A, 0x7D8E, 0x7D7B, 0x7D83, 0x7D7C, 0x7D8C, 0x7D94, 0x7D84, 0x7D7D, 0x7D92, 0x7F6D, 0x7F6B, 0x7F67, 0x7F68, 0x7F6C, 0x7FA6, 0x7FA5, 0x7FA7, 0x7FDB, 0x7FDC, 0x8021, 0x8164, 0x8160, 0x8177, 0x815C, 0x8169, 0x815B, 0x8162, 0x8172, 0x6721, 0x815E, 0x8176, 0x8167, 0x816F, 0x8144, 0x8161, 0x821D, 0x8249, 0x8244, 0x8240, 0x8242, 0x8245, 0x84F1, 0x843F, 0x8456, 0x8476, 0x8479, 0x848F, 0x848D, 0x8465, 0x8451, 0x8440, 0x8486, 0x8467, 0x8430, 0x844D, 0x847D, 0x845A, 0x8459, 0x8474, 0x8473, 0x845D, 0x8507, 0x845E, 0x8437, 0x843A, 0x8434, 0x847A, 0x8443, 0x8478, 0x8432, 0x8445, 0x8429, 0x83D9, 0x844B, 0x842F, 0x8442, 0x842D, 0x845F, 0x8470, 0x8439, 0x844E, 0x844C, 0x8452, 0x846F, 0x84C5, 0x848E, 0x843B, 0x8447, 0x8436, 0x8433, 0x8468, 0x847E, 0x8444, 0x842B, 0x8460, 0x8454, 0x846E, 0x8450, 0x870B, 0x8704, 0x86F7, 0x870C, 0x86FA, 0x86D6, 0x86F5, 0x874D, 0x86F8, 0x870E, 0x8709, 0x8701, 0x86F6, 0x870D, 0x8705, 0x88D6, 0x88CB, 0x88CD, 0x88CE, 0x88DE, 0x88DB, 0x88DA, 0x88CC, 0x88D0, 0x8985, 0x899B, 0x89DF, 0x89E5, 0x89E4, + 0x89E1, 0x89E0, 0x89E2, 0x89DC, 0x89E6, 0x8A76, 0x8A86, 0x8A7F, 0x8A61, 0x8A3F, 0x8A77, 0x8A82, 0x8A84, 0x8A75, 0x8A83, 0x8A81, 0x8A74, 0x8A7A, 0x8C3C, 0x8C4B, 0x8C4A, 0x8C65, 0x8C64, 0x8C66, 0x8C86, 0x8C84, 0x8C85, 0x8CCC, 0x8D68, 0x8D69, 0x8D91, 0x8D8C, 0x8D8E, 0x8D8F, 0x8D8D, 0x8D93, 0x8D94, 0x8D90, 0x8D92, 0x8DF0, 0x8DE0, 0x8DEC, 0x8DF1, 0x8DEE, 0x8DD0, 0x8DE9, 0x8DE3, 0x8DE2, 0x8DE7, 0x8DF2, 0x8DEB, 0x8DF4, 0x8F06, 0x8EFF, 0x8F01, 0x8F00, 0x8F05, 0x8F07, 0x8F08, 0x8F02, 0x8F0B, 0x9052, 0x903F, 0x9044, 0x9049, 0x903D, 0x9110, 0x910D, 0x910F, 0x9111, 0x9116, 0x9114, 0x910B, 0x910E, 0x916E, 0x916F, 0x9248, 0x9252, 0x9230, 0x923A, 0x9266, 0x9233, 0x9265, 0x925E, 0x9283, 0x922E, 0x924A, 0x9246, 0x926D, 0x926C, 0x924F, 0x9260, 0x9267, 0x926F, 0x9236, 0x9261, 0x9270, 0x9231, 0x9254, 0x9263, 0x9250, 0x9272, 0x924E, 0x9253, 0x924C, 0x9256, 0x9232, 0x959F, 0x959C, 0x959E, 0x959B, 0x9692, 0x9693, 0x9691, 0x9697, 0x96CE, 0x96FA, 0x96FD, 0x96F8, 0x96F5, 0x9773, 0x9777, 0x9778, 0x9772, 0x980F, 0x980D, 0x980E, 0x98AC, 0x98F6, 0x98F9, 0x99AF, 0x99B2, 0x99B0, 0x99B5, 0x9AAD, 0x9AAB, 0x9B5B, 0x9CEA, 0x9CED, 0x9CE7, 0x9E80, 0x9EFD, 0x50E6, 0x50D4, 0x50D7, 0x50E8, 0x50F3, 0x50DB, 0x50EA, 0x50DD, 0x50E4, 0x50D3, 0x50EC, 0x50F0, 0x50EF, 0x50E3, 0x50E0, + 0x51D8, 0x5280, 0x5281, 0x52E9, 0x52EB, 0x5330, 0x53AC, 0x5627, 0x5615, 0x560C, 0x5612, 0x55FC, 0x560F, 0x561C, 0x5601, 0x5613, 0x5602, 0x55FA, 0x561D, 0x5604, 0x55FF, 0x55F9, 0x5889, 0x587C, 0x5890, 0x5898, 0x5886, 0x5881, 0x587F, 0x5874, 0x588B, 0x587A, 0x5887, 0x5891, 0x588E, 0x5876, 0x5882, 0x5888, 0x587B, 0x5894, 0x588F, 0x58FE, 0x596B, 0x5ADC, 0x5AEE, 0x5AE5, 0x5AD5, 0x5AEA, 0x5ADA, 0x5AED, 0x5AEB, 0x5AF3, 0x5AE2, 0x5AE0, 0x5ADB, 0x5AEC, 0x5ADE, 0x5ADD, 0x5AD9, 0x5AE8, 0x5ADF, 0x5B77, 0x5BE0, 0x5BE3, 0x5C63, 0x5D82, 0x5D80, 0x5D7D, 0x5D86, 0x5D7A, 0x5D81, 0x5D77, 0x5D8A, 0x5D89, 0x5D88, 0x5D7E, 0x5D7C, 0x5D8D, 0x5D79, 0x5D7F, 0x5E58, 0x5E59, 0x5E53, 0x5ED8, 0x5ED1, 0x5ED7, 0x5ECE, 0x5EDC, 0x5ED5, 0x5ED9, 0x5ED2, 0x5ED4, 0x5F44, 0x5F43, 0x5F6F, 0x5FB6, 0x612C, 0x6128, 0x6141, 0x615E, 0x6171, 0x6173, 0x6152, 0x6153, 0x6172, 0x616C, 0x6180, 0x6174, 0x6154, 0x617A, 0x615B, 0x6165, 0x613B, 0x616A, 0x6161, 0x6156, 0x6229, 0x6227, 0x622B, 0x642B, 0x644D, 0x645B, 0x645D, 0x6474, 0x6476, 0x6472, 0x6473, 0x647D, 0x6475, 0x6466, 0x64A6, 0x644E, 0x6482, 0x645E, 0x645C, 0x644B, 0x6453, 0x6460, 0x6450, 0x647F, 0x643F, 0x646C, 0x646B, 0x6459, 0x6465, 0x6477, 0x6573, 0x65A0, 0x66A1, 0x66A0, 0x669F, 0x6705, 0x6704, 0x6722, 0x69B1, 0x69B6, 0x69C9, + 0x69A0, 0x69CE, 0x6996, 0x69B0, 0x69AC, 0x69BC, 0x6991, 0x6999, 0x698E, 0x69A7, 0x698D, 0x69A9, 0x69BE, 0x69AF, 0x69BF, 0x69C4, 0x69BD, 0x69A4, 0x69D4, 0x69B9, 0x69CA, 0x699A, 0x69CF, 0x69B3, 0x6993, 0x69AA, 0x69A1, 0x699E, 0x69D9, 0x6997, 0x6990, 0x69C2, 0x69B5, 0x69A5, 0x69C6, 0x6B4A, 0x6B4D, 0x6B4B, 0x6B9E, 0x6B9F, 0x6BA0, 0x6BC3, 0x6BC4, 0x6BFE, 0x6ECE, 0x6EF5, 0x6EF1, 0x6F03, 0x6F25, 0x6EF8, 0x6F37, 0x6EFB, 0x6F2E, 0x6F09, 0x6F4E, 0x6F19, 0x6F1A, 0x6F27, 0x6F18, 0x6F3B, 0x6F12, 0x6EED, 0x6F0A, 0x6F36, 0x6F73, 0x6EF9, 0x6EEE, 0x6F2D, 0x6F40, 0x6F30, 0x6F3C, 0x6F35, 0x6EEB, 0x6F07, 0x6F0E, 0x6F43, 0x6F05, 0x6EFD, 0x6EF6, 0x6F39, 0x6F1C, 0x6EFC, 0x6F3A, 0x6F1F, 0x6F0D, 0x6F1E, 0x6F08, 0x6F21, 0x7187, 0x7190, 0x7189, 0x7180, 0x7185, 0x7182, 0x718F, 0x717B, 0x7186, 0x7181, 0x7197, 0x7244, 0x7253, 0x7297, 0x7295, 0x7293, 0x7343, 0x734D, 0x7351, 0x734C, 0x7462, 0x7473, 0x7471, 0x7475, 0x7472, 0x7467, 0x746E, 0x7500, 0x7502, 0x7503, 0x757D, 0x7590, 0x7616, 0x7608, 0x760C, 0x7615, 0x7611, 0x760A, 0x7614, 0x76B8, 0x7781, 0x777C, 0x7785, 0x7782, 0x776E, 0x7780, 0x776F, 0x777E, 0x7783, 0x78B2, 0x78AA, 0x78B4, 0x78AD, 0x78A8, 0x787E, 0x78AB, 0x789E, 0x78A5, 0x78A0, 0x78AC, 0x78A2, 0x78A4, 0x7998, 0x798A, 0x798B, 0x7996, 0x7995, 0x7994, 0x7993, + 0x7997, 0x7988, 0x7992, 0x7990, 0x7A2B, 0x7A4A, 0x7A30, 0x7A2F, 0x7A28, 0x7A26, 0x7AA8, 0x7AAB, 0x7AAC, 0x7AEE, 0x7B88, 0x7B9C, 0x7B8A, 0x7B91, 0x7B90, 0x7B96, 0x7B8D, 0x7B8C, 0x7B9B, 0x7B8E, 0x7B85, 0x7B98, 0x5284, 0x7B99, 0x7BA4, 0x7B82, 0x7CBB, 0x7CBF, 0x7CBC, 0x7CBA, 0x7DA7, 0x7DB7, 0x7DC2, 0x7DA3, 0x7DAA, 0x7DC1, 0x7DC0, 0x7DC5, 0x7D9D, 0x7DCE, 0x7DC4, 0x7DC6, 0x7DCB, 0x7DCC, 0x7DAF, 0x7DB9, 0x7D96, 0x7DBC, 0x7D9F, 0x7DA6, 0x7DAE, 0x7DA9, 0x7DA1, 0x7DC9, 0x7F73, 0x7FE2, 0x7FE3, 0x7FE5, 0x7FDE, 0x8024, 0x805D, 0x805C, 0x8189, 0x8186, 0x8183, 0x8187, 0x818D, 0x818C, 0x818B, 0x8215, 0x8497, 0x84A4, 0x84A1, 0x849F, 0x84BA, 0x84CE, 0x84C2, 0x84AC, 0x84AE, 0x84AB, 0x84B9, 0x84B4, 0x84C1, 0x84CD, 0x84AA, 0x849A, 0x84B1, 0x84D0, 0x849D, 0x84A7, 0x84BB, 0x84A2, 0x8494, 0x84C7, 0x84CC, 0x849B, 0x84A9, 0x84AF, 0x84A8, 0x84D6, 0x8498, 0x84B6, 0x84CF, 0x84A0, 0x84D7, 0x84D4, 0x84D2, 0x84DB, 0x84B0, 0x8491, 0x8661, 0x8733, 0x8723, 0x8728, 0x876B, 0x8740, 0x872E, 0x871E, 0x8721, 0x8719, 0x871B, 0x8743, 0x872C, 0x8741, 0x873E, 0x8746, 0x8720, 0x8732, 0x872A, 0x872D, 0x873C, 0x8712, 0x873A, 0x8731, 0x8735, 0x8742, 0x8726, 0x8727, 0x8738, 0x8724, 0x871A, 0x8730, 0x8711, 0x88F7, 0x88E7, 0x88F1, 0x88F2, 0x88FA, 0x88FE, 0x88EE, 0x88FC, 0x88F6, 0x88FB, + 0x88F0, 0x88EC, 0x88EB, 0x899D, 0x89A1, 0x899F, 0x899E, 0x89E9, 0x89EB, 0x89E8, 0x8AAB, 0x8A99, 0x8A8B, 0x8A92, 0x8A8F, 0x8A96, 0x8C3D, 0x8C68, 0x8C69, 0x8CD5, 0x8CCF, 0x8CD7, 0x8D96, 0x8E09, 0x8E02, 0x8DFF, 0x8E0D, 0x8DFD, 0x8E0A, 0x8E03, 0x8E07, 0x8E06, 0x8E05, 0x8DFE, 0x8E00, 0x8E04, 0x8F10, 0x8F11, 0x8F0E, 0x8F0D, 0x9123, 0x911C, 0x9120, 0x9122, 0x911F, 0x911D, 0x911A, 0x9124, 0x9121, 0x911B, 0x917A, 0x9172, 0x9179, 0x9173, 0x92A5, 0x92A4, 0x9276, 0x929B, 0x927A, 0x92A0, 0x9294, 0x92AA, 0x928D, 0x92A6, 0x929A, 0x92AB, 0x9279, 0x9297, 0x927F, 0x92A3, 0x92EE, 0x928E, 0x9282, 0x9295, 0x92A2, 0x927D, 0x9288, 0x92A1, 0x928A, 0x9286, 0x928C, 0x9299, 0x92A7, 0x927E, 0x9287, 0x92A9, 0x929D, 0x928B, 0x922D, 0x969E, 0x96A1, 0x96FF, 0x9758, 0x977D, 0x977A, 0x977E, 0x9783, 0x9780, 0x9782, 0x977B, 0x9784, 0x9781, 0x977F, 0x97CE, 0x97CD, 0x9816, 0x98AD, 0x98AE, 0x9902, 0x9900, 0x9907, 0x999D, 0x999C, 0x99C3, 0x99B9, 0x99BB, 0x99BA, 0x99C2, 0x99BD, 0x99C7, 0x9AB1, 0x9AE3, 0x9AE7, 0x9B3E, 0x9B3F, 0x9B60, 0x9B61, 0x9B5F, 0x9CF1, 0x9CF2, 0x9CF5, 0x9EA7, 0x50FF, 0x5103, 0x5130, 0x50F8, 0x5106, 0x5107, 0x50F6, 0x50FE, 0x510B, 0x510C, 0x50FD, 0x510A, 0x528B, 0x528C, 0x52F1, 0x52EF, 0x5648, 0x5642, 0x564C, 0x5635, 0x5641, 0x564A, 0x5649, 0x5646, 0x5658, + 0x565A, 0x5640, 0x5633, 0x563D, 0x562C, 0x563E, 0x5638, 0x562A, 0x563A, 0x571A, 0x58AB, 0x589D, 0x58B1, 0x58A0, 0x58A3, 0x58AF, 0x58AC, 0x58A5, 0x58A1, 0x58FF, 0x5AFF, 0x5AF4, 0x5AFD, 0x5AF7, 0x5AF6, 0x5B03, 0x5AF8, 0x5B02, 0x5AF9, 0x5B01, 0x5B07, 0x5B05, 0x5B0F, 0x5C67, 0x5D99, 0x5D97, 0x5D9F, 0x5D92, 0x5DA2, 0x5D93, 0x5D95, 0x5DA0, 0x5D9C, 0x5DA1, 0x5D9A, 0x5D9E, 0x5E69, 0x5E5D, 0x5E60, 0x5E5C, 0x7DF3, 0x5EDB, 0x5EDE, 0x5EE1, 0x5F49, 0x5FB2, 0x618B, 0x6183, 0x6179, 0x61B1, 0x61B0, 0x61A2, 0x6189, 0x619B, 0x6193, 0x61AF, 0x61AD, 0x619F, 0x6192, 0x61AA, 0x61A1, 0x618D, 0x6166, 0x61B3, 0x622D, 0x646E, 0x6470, 0x6496, 0x64A0, 0x6485, 0x6497, 0x649C, 0x648F, 0x648B, 0x648A, 0x648C, 0x64A3, 0x649F, 0x6468, 0x64B1, 0x6498, 0x6576, 0x657A, 0x6579, 0x657B, 0x65B2, 0x65B3, 0x66B5, 0x66B0, 0x66A9, 0x66B2, 0x66B7, 0x66AA, 0x66AF, 0x6A00, 0x6A06, 0x6A17, 0x69E5, 0x69F8, 0x6A15, 0x69F1, 0x69E4, 0x6A20, 0x69FF, 0x69EC, 0x69E2, 0x6A1B, 0x6A1D, 0x69FE, 0x6A27, 0x69F2, 0x69EE, 0x6A14, 0x69F7, 0x69E7, 0x6A40, 0x6A08, 0x69E6, 0x69FB, 0x6A0D, 0x69FC, 0x69EB, 0x6A09, 0x6A04, 0x6A18, 0x6A25, 0x6A0F, 0x69F6, 0x6A26, 0x6A07, 0x69F4, 0x6A16, 0x6B51, 0x6BA5, 0x6BA3, 0x6BA2, 0x6BA6, 0x6C01, 0x6C00, 0x6BFF, 0x6C02, 0x6F41, 0x6F26, 0x6F7E, 0x6F87, 0x6FC6, 0x6F92, + 0x6F8D, 0x6F89, 0x6F8C, 0x6F62, 0x6F4F, 0x6F85, 0x6F5A, 0x6F96, 0x6F76, 0x6F6C, 0x6F82, 0x6F55, 0x6F72, 0x6F52, 0x6F50, 0x6F57, 0x6F94, 0x6F93, 0x6F5D, 0x6F00, 0x6F61, 0x6F6B, 0x6F7D, 0x6F67, 0x6F90, 0x6F53, 0x6F8B, 0x6F69, 0x6F7F, 0x6F95, 0x6F63, 0x6F77, 0x6F6A, 0x6F7B, 0x71B2, 0x71AF, 0x719B, 0x71B0, 0x71A0, 0x719A, 0x71A9, 0x71B5, 0x719D, 0x71A5, 0x719E, 0x71A4, 0x71A1, 0x71AA, 0x719C, 0x71A7, 0x71B3, 0x7298, 0x729A, 0x7358, 0x7352, 0x735E, 0x735F, 0x7360, 0x735D, 0x735B, 0x7361, 0x735A, 0x7359, 0x7362, 0x7487, 0x7489, 0x748A, 0x7486, 0x7481, 0x747D, 0x7485, 0x7488, 0x747C, 0x7479, 0x7508, 0x7507, 0x757E, 0x7625, 0x761E, 0x7619, 0x761D, 0x761C, 0x7623, 0x761A, 0x7628, 0x761B, 0x769C, 0x769D, 0x769E, 0x769B, 0x778D, 0x778F, 0x7789, 0x7788, 0x78CD, 0x78BB, 0x78CF, 0x78CC, 0x78D1, 0x78CE, 0x78D4, 0x78C8, 0x78C3, 0x78C4, 0x78C9, 0x799A, 0x79A1, 0x79A0, 0x799C, 0x79A2, 0x799B, 0x6B76, 0x7A39, 0x7AB2, 0x7AB4, 0x7AB3, 0x7BB7, 0x7BCB, 0x7BBE, 0x7BAC, 0x7BCE, 0x7BAF, 0x7BB9, 0x7BCA, 0x7BB5, 0x7CC5, 0x7CC8, 0x7CCC, 0x7CCB, 0x7DF7, 0x7DDB, 0x7DEA, 0x7DE7, 0x7DD7, 0x7DE1, 0x7E03, 0x7DFA, 0x7DE6, 0x7DF6, 0x7DF1, 0x7DF0, 0x7DEE, 0x7DDF, 0x7F76, 0x7FAC, 0x7FB0, 0x7FAD, 0x7FED, 0x7FEB, 0x7FEA, 0x7FEC, 0x7FE6, 0x7FE8, 0x8064, 0x8067, 0x81A3, 0x819F, + 0x819E, 0x8195, 0x81A2, 0x8199, 0x8197, 0x8216, 0x824F, 0x8253, 0x8252, 0x8250, 0x824E, 0x8251, 0x8524, 0x853B, 0x850F, 0x8500, 0x8529, 0x850E, 0x8509, 0x850D, 0x851F, 0x850A, 0x8527, 0x851C, 0x84FB, 0x852B, 0x84FA, 0x8508, 0x850C, 0x84F4, 0x852A, 0x84F2, 0x8515, 0x84F7, 0x84EB, 0x84F3, 0x84FC, 0x8512, 0x84EA, 0x84E9, 0x8516, 0x84FE, 0x8528, 0x851D, 0x852E, 0x8502, 0x84FD, 0x851E, 0x84F6, 0x8531, 0x8526, 0x84E7, 0x84E8, 0x84F0, 0x84EF, 0x84F9, 0x8518, 0x8520, 0x8530, 0x850B, 0x8519, 0x852F, 0x8662, 0x8756, 0x8763, 0x8764, 0x8777, 0x87E1, 0x8773, 0x8758, 0x8754, 0x875B, 0x8752, 0x8761, 0x875A, 0x8751, 0x875E, 0x876D, 0x876A, 0x8750, 0x874E, 0x875F, 0x875D, 0x876F, 0x876C, 0x877A, 0x876E, 0x875C, 0x8765, 0x874F, 0x877B, 0x8775, 0x8762, 0x8767, 0x8769, 0x885A, 0x8905, 0x890C, 0x8914, 0x890B, 0x8917, 0x8918, 0x8919, 0x8906, 0x8916, 0x8911, 0x890E, 0x8909, 0x89A2, 0x89A4, 0x89A3, 0x89ED, 0x89F0, 0x89EC, 0x8ACF, 0x8AC6, 0x8AB8, 0x8AD3, 0x8AD1, 0x8AD4, 0x8AD5, 0x8ABB, 0x8AD7, 0x8ABE, 0x8AC0, 0x8AC5, 0x8AD8, 0x8AC3, 0x8ABA, 0x8ABD, 0x8AD9, 0x8C3E, 0x8C4D, 0x8C8F, 0x8CE5, 0x8CDF, 0x8CD9, 0x8CE8, 0x8CDA, 0x8CDD, 0x8CE7, 0x8DA0, 0x8D9C, 0x8DA1, 0x8D9B, 0x8E20, 0x8E23, 0x8E25, 0x8E24, 0x8E2E, 0x8E15, 0x8E1B, 0x8E16, 0x8E11, 0x8E19, 0x8E26, 0x8E27, + 0x8E14, 0x8E12, 0x8E18, 0x8E13, 0x8E1C, 0x8E17, 0x8E1A, 0x8F2C, 0x8F24, 0x8F18, 0x8F1A, 0x8F20, 0x8F23, 0x8F16, 0x8F17, 0x9073, 0x9070, 0x906F, 0x9067, 0x906B, 0x912F, 0x912B, 0x9129, 0x912A, 0x9132, 0x9126, 0x912E, 0x9185, 0x9186, 0x918A, 0x9181, 0x9182, 0x9184, 0x9180, 0x92D0, 0x92C3, 0x92C4, 0x92C0, 0x92D9, 0x92B6, 0x92CF, 0x92F1, 0x92DF, 0x92D8, 0x92E9, 0x92D7, 0x92DD, 0x92CC, 0x92EF, 0x92C2, 0x92E8, 0x92CA, 0x92C8, 0x92CE, 0x92E6, 0x92CD, 0x92D5, 0x92C9, 0x92E0, 0x92DE, 0x92E7, 0x92D1, 0x92D3, 0x92B5, 0x92E1, 0x92C6, 0x92B4, 0x957C, 0x95AC, 0x95AB, 0x95AE, 0x95B0, 0x96A4, 0x96A2, 0x96D3, 0x9705, 0x9708, 0x9702, 0x975A, 0x978A, 0x978E, 0x9788, 0x97D0, 0x97CF, 0x981E, 0x981D, 0x9826, 0x9829, 0x9828, 0x9820, 0x981B, 0x9827, 0x98B2, 0x9908, 0x98FA, 0x9911, 0x9914, 0x9916, 0x9917, 0x9915, 0x99DC, 0x99CD, 0x99CF, 0x99D3, 0x99D4, 0x99CE, 0x99C9, 0x99D6, 0x99D8, 0x99CB, 0x99D7, 0x99CC, 0x9AB3, 0x9AEC, 0x9AEB, 0x9AF3, 0x9AF2, 0x9AF1, 0x9B46, 0x9B43, 0x9B67, 0x9B74, 0x9B71, 0x9B66, 0x9B76, 0x9B75, 0x9B70, 0x9B68, 0x9B64, 0x9B6C, 0x9CFC, 0x9CFA, 0x9CFD, 0x9CFF, 0x9CF7, 0x9D07, 0x9D00, 0x9CF9, 0x9CFB, 0x9D08, 0x9D05, 0x9D04, 0x9E83, 0x9ED3, 0x9F0F, 0x9F10, 0x511C, 0x5113, 0x5117, 0x511A, 0x5111, 0x51DE, 0x5334, 0x53E1, 0x5670, 0x5660, 0x566E, + 0x5673, 0x5666, 0x5663, 0x566D, 0x5672, 0x565E, 0x5677, 0x571C, 0x571B, 0x58C8, 0x58BD, 0x58C9, 0x58BF, 0x58BA, 0x58C2, 0x58BC, 0x58C6, 0x5B17, 0x5B19, 0x5B1B, 0x5B21, 0x5B14, 0x5B13, 0x5B10, 0x5B16, 0x5B28, 0x5B1A, 0x5B20, 0x5B1E, 0x5BEF, 0x5DAC, 0x5DB1, 0x5DA9, 0x5DA7, 0x5DB5, 0x5DB0, 0x5DAE, 0x5DAA, 0x5DA8, 0x5DB2, 0x5DAD, 0x5DAF, 0x5DB4, 0x5E67, 0x5E68, 0x5E66, 0x5E6F, 0x5EE9, 0x5EE7, 0x5EE6, 0x5EE8, 0x5EE5, 0x5F4B, 0x5FBC, 0x619D, 0x61A8, 0x6196, 0x61C5, 0x61B4, 0x61C6, 0x61C1, 0x61CC, 0x61BA, 0x61BF, 0x61B8, 0x618C, 0x64D7, 0x64D6, 0x64D0, 0x64CF, 0x64C9, 0x64BD, 0x6489, 0x64C3, 0x64DB, 0x64F3, 0x64D9, 0x6533, 0x657F, 0x657C, 0x65A2, 0x66C8, 0x66BE, 0x66C0, 0x66CA, 0x66CB, 0x66CF, 0x66BD, 0x66BB, 0x66BA, 0x66CC, 0x6723, 0x6A34, 0x6A66, 0x6A49, 0x6A67, 0x6A32, 0x6A68, 0x6A3E, 0x6A5D, 0x6A6D, 0x6A76, 0x6A5B, 0x6A51, 0x6A28, 0x6A5A, 0x6A3B, 0x6A3F, 0x6A41, 0x6A6A, 0x6A64, 0x6A50, 0x6A4F, 0x6A54, 0x6A6F, 0x6A69, 0x6A60, 0x6A3C, 0x6A5E, 0x6A56, 0x6A55, 0x6A4D, 0x6A4E, 0x6A46, 0x6B55, 0x6B54, 0x6B56, 0x6BA7, 0x6BAA, 0x6BAB, 0x6BC8, 0x6BC7, 0x6C04, 0x6C03, 0x6C06, 0x6FAD, 0x6FCB, 0x6FA3, 0x6FC7, 0x6FBC, 0x6FCE, 0x6FC8, 0x6F5E, 0x6FC4, 0x6FBD, 0x6F9E, 0x6FCA, 0x6FA8, 0x7004, 0x6FA5, 0x6FAE, 0x6FBA, 0x6FAC, 0x6FAA, 0x6FCF, 0x6FBF, 0x6FB8, + 0x6FA2, 0x6FC9, 0x6FAB, 0x6FCD, 0x6FAF, 0x6FB2, 0x6FB0, 0x71C5, 0x71C2, 0x71BF, 0x71B8, 0x71D6, 0x71C0, 0x71C1, 0x71CB, 0x71D4, 0x71CA, 0x71C7, 0x71CF, 0x71BD, 0x71D8, 0x71BC, 0x71C6, 0x71DA, 0x71DB, 0x729D, 0x729E, 0x7369, 0x7366, 0x7367, 0x736C, 0x7365, 0x736B, 0x736A, 0x747F, 0x749A, 0x74A0, 0x7494, 0x7492, 0x7495, 0x74A1, 0x750B, 0x7580, 0x762F, 0x762D, 0x7631, 0x763D, 0x7633, 0x763C, 0x7635, 0x7632, 0x7630, 0x76BB, 0x76E6, 0x779A, 0x779D, 0x77A1, 0x779C, 0x779B, 0x77A2, 0x77A3, 0x7795, 0x7799, 0x7797, 0x78DD, 0x78E9, 0x78E5, 0x78EA, 0x78DE, 0x78E3, 0x78DB, 0x78E1, 0x78E2, 0x78ED, 0x78DF, 0x78E0, 0x79A4, 0x7A44, 0x7A48, 0x7A47, 0x7AB6, 0x7AB8, 0x7AB5, 0x7AB1, 0x7AB7, 0x7BDE, 0x7BE3, 0x7BE7, 0x7BDD, 0x7BD5, 0x7BE5, 0x7BDA, 0x7BE8, 0x7BF9, 0x7BD4, 0x7BEA, 0x7BE2, 0x7BDC, 0x7BEB, 0x7BD8, 0x7BDF, 0x7CD2, 0x7CD4, 0x7CD7, 0x7CD0, 0x7CD1, 0x7E12, 0x7E21, 0x7E17, 0x7E0C, 0x7E1F, 0x7E20, 0x7E13, 0x7E0E, 0x7E1C, 0x7E15, 0x7E1A, 0x7E22, 0x7E0B, 0x7E0F, 0x7E16, 0x7E0D, 0x7E14, 0x7E25, 0x7E24, 0x7F43, 0x7F7B, 0x7F7C, 0x7F7A, 0x7FB1, 0x7FEF, 0x802A, 0x8029, 0x806C, 0x81B1, 0x81A6, 0x81AE, 0x81B9, 0x81B5, 0x81AB, 0x81B0, 0x81AC, 0x81B4, 0x81B2, 0x81B7, 0x81A7, 0x81F2, 0x8255, 0x8256, 0x8257, 0x8556, 0x8545, 0x856B, 0x854D, 0x8553, 0x8561, 0x8558, + 0x8540, 0x8546, 0x8564, 0x8541, 0x8562, 0x8544, 0x8551, 0x8547, 0x8563, 0x853E, 0x855B, 0x8571, 0x854E, 0x856E, 0x8575, 0x8555, 0x8567, 0x8560, 0x858C, 0x8566, 0x855D, 0x8554, 0x8565, 0x856C, 0x8663, 0x8665, 0x8664, 0x879B, 0x878F, 0x8797, 0x8793, 0x8792, 0x8788, 0x8781, 0x8796, 0x8798, 0x8779, 0x8787, 0x87A3, 0x8785, 0x8790, 0x8791, 0x879D, 0x8784, 0x8794, 0x879C, 0x879A, 0x8789, 0x891E, 0x8926, 0x8930, 0x892D, 0x892E, 0x8927, 0x8931, 0x8922, 0x8929, 0x8923, 0x892F, 0x892C, 0x891F, 0x89F1, 0x8AE0, 0x8AE2, 0x8AF2, 0x8AF4, 0x8AF5, 0x8ADD, 0x8B14, 0x8AE4, 0x8ADF, 0x8AF0, 0x8AC8, 0x8ADE, 0x8AE1, 0x8AE8, 0x8AFF, 0x8AEF, 0x8AFB, 0x8C91, 0x8C92, 0x8C90, 0x8CF5, 0x8CEE, 0x8CF1, 0x8CF0, 0x8CF3, 0x8D6C, 0x8D6E, 0x8DA5, 0x8DA7, 0x8E33, 0x8E3E, 0x8E38, 0x8E40, 0x8E45, 0x8E36, 0x8E3C, 0x8E3D, 0x8E41, 0x8E30, 0x8E3F, 0x8EBD, 0x8F36, 0x8F2E, 0x8F35, 0x8F32, 0x8F39, 0x8F37, 0x8F34, 0x9076, 0x9079, 0x907B, 0x9086, 0x90FA, 0x9133, 0x9135, 0x9136, 0x9193, 0x9190, 0x9191, 0x918D, 0x918F, 0x9327, 0x931E, 0x9308, 0x931F, 0x9306, 0x930F, 0x937A, 0x9338, 0x933C, 0x931B, 0x9323, 0x9312, 0x9301, 0x9346, 0x932D, 0x930E, 0x930D, 0x92CB, 0x931D, 0x92FA, 0x9325, 0x9313, 0x92F9, 0x92F7, 0x9334, 0x9302, 0x9324, 0x92FF, 0x9329, 0x9339, 0x9335, 0x932A, 0x9314, 0x930C, + 0x930B, 0x92FE, 0x9309, 0x9300, 0x92FB, 0x9316, 0x95BC, 0x95CD, 0x95BE, 0x95B9, 0x95BA, 0x95B6, 0x95BF, 0x95B5, 0x95BD, 0x96A9, 0x96D4, 0x970B, 0x9712, 0x9710, 0x9799, 0x9797, 0x9794, 0x97F0, 0x97F8, 0x9835, 0x982F, 0x9832, 0x9924, 0x991F, 0x9927, 0x9929, 0x999E, 0x99EE, 0x99EC, 0x99E5, 0x99E4, 0x99F0, 0x99E3, 0x99EA, 0x99E9, 0x99E7, 0x9AB9, 0x9ABF, 0x9AB4, 0x9ABB, 0x9AF6, 0x9AFA, 0x9AF9, 0x9AF7, 0x9B33, 0x9B80, 0x9B85, 0x9B87, 0x9B7C, 0x9B7E, 0x9B7B, 0x9B82, 0x9B93, 0x9B92, 0x9B90, 0x9B7A, 0x9B95, 0x9B7D, 0x9B88, 0x9D25, 0x9D17, 0x9D20, 0x9D1E, 0x9D14, 0x9D29, 0x9D1D, 0x9D18, 0x9D22, 0x9D10, 0x9D19, 0x9D1F, 0x9E88, 0x9E86, 0x9E87, 0x9EAE, 0x9EAD, 0x9ED5, 0x9ED6, 0x9EFA, 0x9F12, 0x9F3D, 0x5126, 0x5125, 0x5122, 0x5124, 0x5120, 0x5129, 0x52F4, 0x5693, 0x568C, 0x568D, 0x5686, 0x5684, 0x5683, 0x567E, 0x5682, 0x567F, 0x5681, 0x58D6, 0x58D4, 0x58CF, 0x58D2, 0x5B2D, 0x5B25, 0x5B32, 0x5B23, 0x5B2C, 0x5B27, 0x5B26, 0x5B2F, 0x5B2E, 0x5B7B, 0x5BF1, 0x5BF2, 0x5DB7, 0x5E6C, 0x5E6A, 0x5FBE, 0x5FBB, 0x61C3, 0x61B5, 0x61BC, 0x61E7, 0x61E0, 0x61E5, 0x61E4, 0x61E8, 0x61DE, 0x64EF, 0x64E9, 0x64E3, 0x64EB, 0x64E4, 0x64E8, 0x6581, 0x6580, 0x65B6, 0x65DA, 0x66D2, 0x6A8D, 0x6A96, 0x6A81, 0x6AA5, 0x6A89, 0x6A9F, 0x6A9B, 0x6AA1, 0x6A9E, 0x6A87, 0x6A93, 0x6A8E, + 0x6A95, 0x6A83, 0x6AA8, 0x6AA4, 0x6A91, 0x6A7F, 0x6AA6, 0x6A9A, 0x6A85, 0x6A8C, 0x6A92, 0x6B5B, 0x6BAD, 0x6C09, 0x6FCC, 0x6FA9, 0x6FF4, 0x6FD4, 0x6FE3, 0x6FDC, 0x6FED, 0x6FE7, 0x6FE6, 0x6FDE, 0x6FF2, 0x6FDD, 0x6FE2, 0x6FE8, 0x71E1, 0x71F1, 0x71E8, 0x71F2, 0x71E4, 0x71F0, 0x71E2, 0x7373, 0x736E, 0x736F, 0x7497, 0x74B2, 0x74AB, 0x7490, 0x74AA, 0x74AD, 0x74B1, 0x74A5, 0x74AF, 0x7510, 0x7511, 0x7512, 0x750F, 0x7584, 0x7643, 0x7648, 0x7649, 0x7647, 0x76A4, 0x76E9, 0x77B5, 0x77AB, 0x77B2, 0x77B7, 0x77B6, 0x77B4, 0x77B1, 0x77A8, 0x77F0, 0x78F3, 0x78FD, 0x7902, 0x78FB, 0x78FC, 0x78F2, 0x7905, 0x78F9, 0x78FE, 0x7904, 0x79AB, 0x79A8, 0x7A5C, 0x7A5B, 0x7A56, 0x7A58, 0x7A54, 0x7A5A, 0x7ABE, 0x7AC0, 0x7AC1, 0x7C05, 0x7C0F, 0x7BF2, 0x7C00, 0x7BFF, 0x7BFB, 0x7C0E, 0x7BF4, 0x7C0B, 0x7BF3, 0x7C02, 0x7C09, 0x7C03, 0x7C01, 0x7BF8, 0x7BFD, 0x7C06, 0x7BF0, 0x7BF1, 0x7C10, 0x7C0A, 0x7CE8, 0x7E2D, 0x7E3C, 0x7E42, 0x7E33, 0x9848, 0x7E38, 0x7E2A, 0x7E49, 0x7E40, 0x7E47, 0x7E29, 0x7E4C, 0x7E30, 0x7E3B, 0x7E36, 0x7E44, 0x7E3A, 0x7F45, 0x7F7F, 0x7F7E, 0x7F7D, 0x7FF4, 0x7FF2, 0x802C, 0x81BB, 0x81C4, 0x81CC, 0x81CA, 0x81C5, 0x81C7, 0x81BC, 0x81E9, 0x825B, 0x825A, 0x825C, 0x8583, 0x8580, 0x858F, 0x85A7, 0x8595, 0x85A0, 0x858B, 0x85A3, 0x857B, 0x85A4, 0x859A, 0x859E, + 0x8577, 0x857C, 0x8589, 0x85A1, 0x857A, 0x8578, 0x8557, 0x858E, 0x8596, 0x8586, 0x858D, 0x8599, 0x859D, 0x8581, 0x85A2, 0x8582, 0x8588, 0x8585, 0x8579, 0x8576, 0x8598, 0x8590, 0x859F, 0x8668, 0x87BE, 0x87AA, 0x87AD, 0x87C5, 0x87B0, 0x87AC, 0x87B9, 0x87B5, 0x87BC, 0x87AE, 0x87C9, 0x87C3, 0x87C2, 0x87CC, 0x87B7, 0x87AF, 0x87C4, 0x87CA, 0x87B4, 0x87B6, 0x87BF, 0x87B8, 0x87BD, 0x87DE, 0x87B2, 0x8935, 0x8933, 0x893C, 0x893E, 0x8941, 0x8952, 0x8937, 0x8942, 0x89AD, 0x89AF, 0x89AE, 0x89F2, 0x89F3, 0x8B1E, 0x8B18, 0x8B16, 0x8B11, 0x8B05, 0x8B0B, 0x8B22, 0x8B0F, 0x8B12, 0x8B15, 0x8B07, 0x8B0D, 0x8B08, 0x8B06, 0x8B1C, 0x8B13, 0x8B1A, 0x8C4F, 0x8C70, 0x8C72, 0x8C71, 0x8C6F, 0x8C95, 0x8C94, 0x8CF9, 0x8D6F, 0x8E4E, 0x8E4D, 0x8E53, 0x8E50, 0x8E4C, 0x8E47, 0x8F43, 0x8F40, 0x9085, 0x907E, 0x9138, 0x919A, 0x91A2, 0x919B, 0x9199, 0x919F, 0x91A1, 0x919D, 0x91A0, 0x93A1, 0x9383, 0x93AF, 0x9364, 0x9356, 0x9347, 0x937C, 0x9358, 0x935C, 0x9376, 0x9349, 0x9350, 0x9351, 0x9360, 0x936D, 0x938F, 0x934C, 0x936A, 0x9379, 0x9357, 0x9355, 0x9352, 0x934F, 0x9371, 0x9377, 0x937B, 0x9361, 0x935E, 0x9363, 0x9367, 0x9380, 0x934E, 0x9359, 0x95C7, 0x95C0, 0x95C9, 0x95C3, 0x95C5, 0x95B7, 0x96AE, 0x96B0, 0x96AC, 0x9720, 0x971F, 0x9718, 0x971D, 0x9719, 0x979A, 0x97A1, 0x979C, + 0x979E, 0x979D, 0x97D5, 0x97D4, 0x97F1, 0x9841, 0x9844, 0x984A, 0x9849, 0x9845, 0x9843, 0x9925, 0x992B, 0x992C, 0x992A, 0x9933, 0x9932, 0x992F, 0x992D, 0x9931, 0x9930, 0x9998, 0x99A3, 0x99A1, 0x9A02, 0x99FA, 0x99F4, 0x99F7, 0x99F9, 0x99F8, 0x99F6, 0x99FB, 0x99FD, 0x99FE, 0x99FC, 0x9A03, 0x9ABE, 0x9AFE, 0x9AFD, 0x9B01, 0x9AFC, 0x9B48, 0x9B9A, 0x9BA8, 0x9B9E, 0x9B9B, 0x9BA6, 0x9BA1, 0x9BA5, 0x9BA4, 0x9B86, 0x9BA2, 0x9BA0, 0x9BAF, 0x9D33, 0x9D41, 0x9D67, 0x9D36, 0x9D2E, 0x9D2F, 0x9D31, 0x9D38, 0x9D30, 0x9D45, 0x9D42, 0x9D43, 0x9D3E, 0x9D37, 0x9D40, 0x9D3D, 0x7FF5, 0x9D2D, 0x9E8A, 0x9E89, 0x9E8D, 0x9EB0, 0x9EC8, 0x9EDA, 0x9EFB, 0x9EFF, 0x9F24, 0x9F23, 0x9F22, 0x9F54, 0x9FA0, 0x5131, 0x512D, 0x512E, 0x5698, 0x569C, 0x5697, 0x569A, 0x569D, 0x5699, 0x5970, 0x5B3C, 0x5C69, 0x5C6A, 0x5DC0, 0x5E6D, 0x5E6E, 0x61D8, 0x61DF, 0x61ED, 0x61EE, 0x61F1, 0x61EA, 0x61F0, 0x61EB, 0x61D6, 0x61E9, 0x64FF, 0x6504, 0x64FD, 0x64F8, 0x6501, 0x6503, 0x64FC, 0x6594, 0x65DB, 0x66DA, 0x66DB, 0x66D8, 0x6AC5, 0x6AB9, 0x6ABD, 0x6AE1, 0x6AC6, 0x6ABA, 0x6AB6, 0x6AB7, 0x6AC7, 0x6AB4, 0x6AAD, 0x6B5E, 0x6BC9, 0x6C0B, 0x7007, 0x700C, 0x700D, 0x7001, 0x7005, 0x7014, 0x700E, 0x6FFF, 0x7000, 0x6FFB, 0x7026, 0x6FFC, 0x6FF7, 0x700A, 0x7201, 0x71FF, 0x71F9, 0x7203, 0x71FD, 0x7376, + 0x74B8, 0x74C0, 0x74B5, 0x74C1, 0x74BE, 0x74B6, 0x74BB, 0x74C2, 0x7514, 0x7513, 0x765C, 0x7664, 0x7659, 0x7650, 0x7653, 0x7657, 0x765A, 0x76A6, 0x76BD, 0x76EC, 0x77C2, 0x77BA, 0x78FF, 0x790C, 0x7913, 0x7914, 0x7909, 0x7910, 0x7912, 0x7911, 0x79AD, 0x79AC, 0x7A5F, 0x7C1C, 0x7C29, 0x7C19, 0x7C20, 0x7C1F, 0x7C2D, 0x7C1D, 0x7C26, 0x7C28, 0x7C22, 0x7C25, 0x7C30, 0x7E5C, 0x7E50, 0x7E56, 0x7E63, 0x7E58, 0x7E62, 0x7E5F, 0x7E51, 0x7E60, 0x7E57, 0x7E53, 0x7FB5, 0x7FB3, 0x7FF7, 0x7FF8, 0x8075, 0x81D1, 0x81D2, 0x81D0, 0x825F, 0x825E, 0x85B4, 0x85C6, 0x85C0, 0x85C3, 0x85C2, 0x85B3, 0x85B5, 0x85BD, 0x85C7, 0x85C4, 0x85BF, 0x85CB, 0x85CE, 0x85C8, 0x85C5, 0x85B1, 0x85B6, 0x85D2, 0x8624, 0x85B8, 0x85B7, 0x85BE, 0x8669, 0x87E7, 0x87E6, 0x87E2, 0x87DB, 0x87EB, 0x87EA, 0x87E5, 0x87DF, 0x87F3, 0x87E4, 0x87D4, 0x87DC, 0x87D3, 0x87ED, 0x87D8, 0x87E3, 0x87A4, 0x87D7, 0x87D9, 0x8801, 0x87F4, 0x87E8, 0x87DD, 0x8953, 0x894B, 0x894F, 0x894C, 0x8946, 0x8950, 0x8951, 0x8949, 0x8B2A, 0x8B27, 0x8B23, 0x8B33, 0x8B30, 0x8B35, 0x8B47, 0x8B2F, 0x8B3C, 0x8B3E, 0x8B31, 0x8B25, 0x8B37, 0x8B26, 0x8B36, 0x8B2E, 0x8B24, 0x8B3B, 0x8B3D, 0x8B3A, 0x8C42, 0x8C75, 0x8C99, 0x8C98, 0x8C97, 0x8CFE, 0x8D04, 0x8D02, 0x8D00, 0x8E5C, 0x8E62, 0x8E60, 0x8E57, 0x8E56, 0x8E5E, 0x8E65, 0x8E67, + 0x8E5B, 0x8E5A, 0x8E61, 0x8E5D, 0x8E69, 0x8E54, 0x8F46, 0x8F47, 0x8F48, 0x8F4B, 0x9128, 0x913A, 0x913B, 0x913E, 0x91A8, 0x91A5, 0x91A7, 0x91AF, 0x91AA, 0x93B5, 0x938C, 0x9392, 0x93B7, 0x939B, 0x939D, 0x9389, 0x93A7, 0x938E, 0x93AA, 0x939E, 0x93A6, 0x9395, 0x9388, 0x9399, 0x939F, 0x938D, 0x93B1, 0x9391, 0x93B2, 0x93A4, 0x93A8, 0x93B4, 0x93A3, 0x93A5, 0x95D2, 0x95D3, 0x95D1, 0x96B3, 0x96D7, 0x96DA, 0x5DC2, 0x96DF, 0x96D8, 0x96DD, 0x9723, 0x9722, 0x9725, 0x97AC, 0x97AE, 0x97A8, 0x97AB, 0x97A4, 0x97AA, 0x97A2, 0x97A5, 0x97D7, 0x97D9, 0x97D6, 0x97D8, 0x97FA, 0x9850, 0x9851, 0x9852, 0x98B8, 0x9941, 0x993C, 0x993A, 0x9A0F, 0x9A0B, 0x9A09, 0x9A0D, 0x9A04, 0x9A11, 0x9A0A, 0x9A05, 0x9A07, 0x9A06, 0x9AC0, 0x9ADC, 0x9B08, 0x9B04, 0x9B05, 0x9B29, 0x9B35, 0x9B4A, 0x9B4C, 0x9B4B, 0x9BC7, 0x9BC6, 0x9BC3, 0x9BBF, 0x9BC1, 0x9BB5, 0x9BB8, 0x9BD3, 0x9BB6, 0x9BC4, 0x9BB9, 0x9BBD, 0x9D5C, 0x9D53, 0x9D4F, 0x9D4A, 0x9D5B, 0x9D4B, 0x9D59, 0x9D56, 0x9D4C, 0x9D57, 0x9D52, 0x9D54, 0x9D5F, 0x9D58, 0x9D5A, 0x9E8E, 0x9E8C, 0x9EDF, 0x9F01, 0x9F00, 0x9F16, 0x9F25, 0x9F2B, 0x9F2A, 0x9F29, 0x9F28, 0x9F4C, 0x9F55, 0x5134, 0x5135, 0x5296, 0x52F7, 0x53B4, 0x56AB, 0x56AD, 0x56A6, 0x56A7, 0x56AA, 0x56AC, 0x58DA, 0x58DD, 0x58DB, 0x5912, 0x5B3D, 0x5B3E, 0x5B3F, 0x5DC3, 0x5E70, + 0x5FBF, 0x61FB, 0x6507, 0x6510, 0x650D, 0x6509, 0x650C, 0x650E, 0x6584, 0x65DE, 0x65DD, 0x66DE, 0x6AE7, 0x6AE0, 0x6ACC, 0x6AD1, 0x6AD9, 0x6ACB, 0x6ADF, 0x6ADC, 0x6AD0, 0x6AEB, 0x6ACF, 0x6ACD, 0x6ADE, 0x6B60, 0x6BB0, 0x6C0C, 0x7019, 0x7027, 0x7020, 0x7016, 0x702B, 0x7021, 0x7022, 0x7023, 0x7029, 0x7017, 0x7024, 0x701C, 0x702A, 0x720C, 0x720A, 0x7207, 0x7202, 0x7205, 0x72A5, 0x72A6, 0x72A4, 0x72A3, 0x72A1, 0x74CB, 0x74C5, 0x74B7, 0x74C3, 0x7516, 0x7660, 0x77C9, 0x77CA, 0x77C4, 0x77F1, 0x791D, 0x791B, 0x7921, 0x791C, 0x7917, 0x791E, 0x79B0, 0x7A67, 0x7A68, 0x7C33, 0x7C3C, 0x7C39, 0x7C2C, 0x7C3B, 0x7CEC, 0x7CEA, 0x7E76, 0x7E75, 0x7E78, 0x7E70, 0x7E77, 0x7E6F, 0x7E7A, 0x7E72, 0x7E74, 0x7E68, 0x7F4B, 0x7F4A, 0x7F83, 0x7F86, 0x7FB7, 0x7FFD, 0x7FFE, 0x8078, 0x81D7, 0x81D5, 0x8264, 0x8261, 0x8263, 0x85EB, 0x85F1, 0x85ED, 0x85D9, 0x85E1, 0x85E8, 0x85DA, 0x85D7, 0x85EC, 0x85F2, 0x85F8, 0x85D8, 0x85DF, 0x85E3, 0x85DC, 0x85D1, 0x85F0, 0x85E6, 0x85EF, 0x85DE, 0x85E2, 0x8800, 0x87FA, 0x8803, 0x87F6, 0x87F7, 0x8809, 0x880C, 0x880B, 0x8806, 0x87FC, 0x8808, 0x87FF, 0x880A, 0x8802, 0x8962, 0x895A, 0x895B, 0x8957, 0x8961, 0x895C, 0x8958, 0x895D, 0x8959, 0x8988, 0x89B7, 0x89B6, 0x89F6, 0x8B50, 0x8B48, 0x8B4A, 0x8B40, 0x8B53, 0x8B56, 0x8B54, 0x8B4B, 0x8B55, + 0x8B51, 0x8B42, 0x8B52, 0x8B57, 0x8C43, 0x8C77, 0x8C76, 0x8C9A, 0x8D06, 0x8D07, 0x8D09, 0x8DAC, 0x8DAA, 0x8DAD, 0x8DAB, 0x8E6D, 0x8E78, 0x8E73, 0x8E6A, 0x8E6F, 0x8E7B, 0x8EC2, 0x8F52, 0x8F51, 0x8F4F, 0x8F50, 0x8F53, 0x8FB4, 0x9140, 0x913F, 0x91B0, 0x91AD, 0x93DE, 0x93C7, 0x93CF, 0x93C2, 0x93DA, 0x93D0, 0x93F9, 0x93EC, 0x93CC, 0x93D9, 0x93A9, 0x93E6, 0x93CA, 0x93D4, 0x93EE, 0x93E3, 0x93D5, 0x93C4, 0x93CE, 0x93C0, 0x93D2, 0x93E7, 0x957D, 0x95DA, 0x95DB, 0x96E1, 0x9729, 0x972B, 0x972C, 0x9728, 0x9726, 0x97B3, 0x97B7, 0x97B6, 0x97DD, 0x97DE, 0x97DF, 0x985C, 0x9859, 0x985D, 0x9857, 0x98BF, 0x98BD, 0x98BB, 0x98BE, 0x9948, 0x9947, 0x9943, 0x99A6, 0x99A7, 0x9A1A, 0x9A15, 0x9A25, 0x9A1D, 0x9A24, 0x9A1B, 0x9A22, 0x9A20, 0x9A27, 0x9A23, 0x9A1E, 0x9A1C, 0x9A14, 0x9AC2, 0x9B0B, 0x9B0A, 0x9B0E, 0x9B0C, 0x9B37, 0x9BEA, 0x9BEB, 0x9BE0, 0x9BDE, 0x9BE4, 0x9BE6, 0x9BE2, 0x9BF0, 0x9BD4, 0x9BD7, 0x9BEC, 0x9BDC, 0x9BD9, 0x9BE5, 0x9BD5, 0x9BE1, 0x9BDA, 0x9D77, 0x9D81, 0x9D8A, 0x9D84, 0x9D88, 0x9D71, 0x9D80, 0x9D78, 0x9D86, 0x9D8B, 0x9D8C, 0x9D7D, 0x9D6B, 0x9D74, 0x9D75, 0x9D70, 0x9D69, 0x9D85, 0x9D73, 0x9D7B, 0x9D82, 0x9D6F, 0x9D79, 0x9D7F, 0x9D87, 0x9D68, 0x9E94, 0x9E91, 0x9EC0, 0x9EFC, 0x9F2D, 0x9F40, 0x9F41, 0x9F4D, 0x9F56, 0x9F57, 0x9F58, 0x5337, 0x56B2, + 0x56B5, 0x56B3, 0x58E3, 0x5B45, 0x5DC6, 0x5DC7, 0x5EEE, 0x5EEF, 0x5FC0, 0x5FC1, 0x61F9, 0x6517, 0x6516, 0x6515, 0x6513, 0x65DF, 0x66E8, 0x66E3, 0x66E4, 0x6AF3, 0x6AF0, 0x6AEA, 0x6AE8, 0x6AF9, 0x6AF1, 0x6AEE, 0x6AEF, 0x703C, 0x7035, 0x702F, 0x7037, 0x7034, 0x7031, 0x7042, 0x7038, 0x703F, 0x703A, 0x7039, 0x7040, 0x703B, 0x7033, 0x7041, 0x7213, 0x7214, 0x72A8, 0x737D, 0x737C, 0x74BA, 0x76AB, 0x76AA, 0x76BE, 0x76ED, 0x77CC, 0x77CE, 0x77CF, 0x77CD, 0x77F2, 0x7925, 0x7923, 0x7927, 0x7928, 0x7924, 0x7929, 0x79B2, 0x7A6E, 0x7A6C, 0x7A6D, 0x7AF7, 0x7C49, 0x7C48, 0x7C4A, 0x7C47, 0x7C45, 0x7CEE, 0x7E7B, 0x7E7E, 0x7E81, 0x7E80, 0x7FBA, 0x7FFF, 0x8079, 0x81DB, 0x81D9, 0x820B, 0x8268, 0x8269, 0x8622, 0x85FF, 0x8601, 0x85FE, 0x861B, 0x8600, 0x85F6, 0x8604, 0x8609, 0x8605, 0x860C, 0x85FD, 0x8819, 0x8810, 0x8811, 0x8817, 0x8813, 0x8816, 0x8963, 0x8966, 0x89B9, 0x89F7, 0x8B60, 0x8B6A, 0x8B5D, 0x8B68, 0x8B63, 0x8B65, 0x8B67, 0x8B6D, 0x8DAE, 0x8E86, 0x8E88, 0x8E84, 0x8F59, 0x8F56, 0x8F57, 0x8F55, 0x8F58, 0x8F5A, 0x908D, 0x9143, 0x9141, 0x91B7, 0x91B5, 0x91B2, 0x91B3, 0x940B, 0x9413, 0x93FB, 0x9420, 0x940F, 0x9414, 0x93FE, 0x9415, 0x9410, 0x9428, 0x9419, 0x940D, 0x93F5, 0x9400, 0x93F7, 0x9407, 0x940E, 0x9416, 0x9412, 0x93FA, 0x9409, 0x93F8, 0x940A, 0x93FF, + 0x93FC, 0x940C, 0x93F6, 0x9411, 0x9406, 0x95DE, 0x95E0, 0x95DF, 0x972E, 0x972F, 0x97B9, 0x97BB, 0x97FD, 0x97FE, 0x9860, 0x9862, 0x9863, 0x985F, 0x98C1, 0x98C2, 0x9950, 0x994E, 0x9959, 0x994C, 0x994B, 0x9953, 0x9A32, 0x9A34, 0x9A31, 0x9A2C, 0x9A2A, 0x9A36, 0x9A29, 0x9A2E, 0x9A38, 0x9A2D, 0x9AC7, 0x9ACA, 0x9AC6, 0x9B10, 0x9B12, 0x9B11, 0x9C0B, 0x9C08, 0x9BF7, 0x9C05, 0x9C12, 0x9BF8, 0x9C40, 0x9C07, 0x9C0E, 0x9C06, 0x9C17, 0x9C14, 0x9C09, 0x9D9F, 0x9D99, 0x9DA4, 0x9D9D, 0x9D92, 0x9D98, 0x9D90, 0x9D9B, 0x9DA0, 0x9D94, 0x9D9C, 0x9DAA, 0x9D97, 0x9DA1, 0x9D9A, 0x9DA2, 0x9DA8, 0x9D9E, 0x9DA3, 0x9DBF, 0x9DA9, 0x9D96, 0x9DA6, 0x9DA7, 0x9E99, 0x9E9B, 0x9E9A, 0x9EE5, 0x9EE4, 0x9EE7, 0x9EE6, 0x9F30, 0x9F2E, 0x9F5B, 0x9F60, 0x9F5E, 0x9F5D, 0x9F59, 0x9F91, 0x513A, 0x5139, 0x5298, 0x5297, 0x56C3, 0x56BD, 0x56BE, 0x5B48, 0x5B47, 0x5DCB, 0x5DCF, 0x5EF1, 0x61FD, 0x651B, 0x6B02, 0x6AFC, 0x6B03, 0x6AF8, 0x6B00, 0x7043, 0x7044, 0x704A, 0x7048, 0x7049, 0x7045, 0x7046, 0x721D, 0x721A, 0x7219, 0x737E, 0x7517, 0x766A, 0x77D0, 0x792D, 0x7931, 0x792F, 0x7C54, 0x7C53, 0x7CF2, 0x7E8A, 0x7E87, 0x7E88, 0x7E8B, 0x7E86, 0x7E8D, 0x7F4D, 0x7FBB, 0x8030, 0x81DD, 0x8618, 0x862A, 0x8626, 0x861F, 0x8623, 0x861C, 0x8619, 0x8627, 0x862E, 0x8621, 0x8620, 0x8629, 0x861E, 0x8625, + 0x8829, 0x881D, 0x881B, 0x8820, 0x8824, 0x881C, 0x882B, 0x884A, 0x896D, 0x8969, 0x896E, 0x896B, 0x89FA, 0x8B79, 0x8B78, 0x8B45, 0x8B7A, 0x8B7B, 0x8D10, 0x8D14, 0x8DAF, 0x8E8E, 0x8E8C, 0x8F5E, 0x8F5B, 0x8F5D, 0x9146, 0x9144, 0x9145, 0x91B9, 0x943F, 0x943B, 0x9436, 0x9429, 0x943D, 0x943C, 0x9430, 0x9439, 0x942A, 0x9437, 0x942C, 0x9440, 0x9431, 0x95E5, 0x95E4, 0x95E3, 0x9735, 0x973A, 0x97BF, 0x97E1, 0x9864, 0x98C9, 0x98C6, 0x98C0, 0x9958, 0x9956, 0x9A39, 0x9A3D, 0x9A46, 0x9A44, 0x9A42, 0x9A41, 0x9A3A, 0x9A3F, 0x9ACD, 0x9B15, 0x9B17, 0x9B18, 0x9B16, 0x9B3A, 0x9B52, 0x9C2B, 0x9C1D, 0x9C1C, 0x9C2C, 0x9C23, 0x9C28, 0x9C29, 0x9C24, 0x9C21, 0x9DB7, 0x9DB6, 0x9DBC, 0x9DC1, 0x9DC7, 0x9DCA, 0x9DCF, 0x9DBE, 0x9DC5, 0x9DC3, 0x9DBB, 0x9DB5, 0x9DCE, 0x9DB9, 0x9DBA, 0x9DAC, 0x9DC8, 0x9DB1, 0x9DAD, 0x9DCC, 0x9DB3, 0x9DCD, 0x9DB2, 0x9E7A, 0x9E9C, 0x9EEB, 0x9EEE, 0x9EED, 0x9F1B, 0x9F18, 0x9F1A, 0x9F31, 0x9F4E, 0x9F65, 0x9F64, 0x9F92, 0x4EB9, 0x56C6, 0x56C5, 0x56CB, 0x5971, 0x5B4B, 0x5B4C, 0x5DD5, 0x5DD1, 0x5EF2, 0x6521, 0x6520, 0x6526, 0x6522, 0x6B0B, 0x6B08, 0x6B09, 0x6C0D, 0x7055, 0x7056, 0x7057, 0x7052, 0x721E, 0x721F, 0x72A9, 0x737F, 0x74D8, 0x74D5, 0x74D9, 0x74D7, 0x766D, 0x76AD, 0x7935, 0x79B4, 0x7A70, 0x7A71, 0x7C57, 0x7C5C, 0x7C59, 0x7C5B, 0x7C5A, + 0x7CF4, 0x7CF1, 0x7E91, 0x7F4F, 0x7F87, 0x81DE, 0x826B, 0x8634, 0x8635, 0x8633, 0x862C, 0x8632, 0x8636, 0x882C, 0x8828, 0x8826, 0x882A, 0x8825, 0x8971, 0x89BF, 0x89BE, 0x89FB, 0x8B7E, 0x8B84, 0x8B82, 0x8B86, 0x8B85, 0x8B7F, 0x8D15, 0x8E95, 0x8E94, 0x8E9A, 0x8E92, 0x8E90, 0x8E96, 0x8E97, 0x8F60, 0x8F62, 0x9147, 0x944C, 0x9450, 0x944A, 0x944B, 0x944F, 0x9447, 0x9445, 0x9448, 0x9449, 0x9446, 0x973F, 0x97E3, 0x986A, 0x9869, 0x98CB, 0x9954, 0x995B, 0x9A4E, 0x9A53, 0x9A54, 0x9A4C, 0x9A4F, 0x9A48, 0x9A4A, 0x9A49, 0x9A52, 0x9A50, 0x9AD0, 0x9B19, 0x9B2B, 0x9B3B, 0x9B56, 0x9B55, 0x9C46, 0x9C48, 0x9C3F, 0x9C44, 0x9C39, 0x9C33, 0x9C41, 0x9C3C, 0x9C37, 0x9C34, 0x9C32, 0x9C3D, 0x9C36, 0x9DDB, 0x9DD2, 0x9DDE, 0x9DDA, 0x9DCB, 0x9DD0, 0x9DDC, 0x9DD1, 0x9DDF, 0x9DE9, 0x9DD9, 0x9DD8, 0x9DD6, 0x9DF5, 0x9DD5, 0x9DDD, 0x9EB6, 0x9EF0, 0x9F35, 0x9F33, 0x9F32, 0x9F42, 0x9F6B, 0x9F95, 0x9FA2, 0x513D, 0x5299, 0x58E8, 0x58E7, 0x5972, 0x5B4D, 0x5DD8, 0x882F, 0x5F4F, 0x6201, 0x6203, 0x6204, 0x6529, 0x6525, 0x6596, 0x66EB, 0x6B11, 0x6B12, 0x6B0F, 0x6BCA, 0x705B, 0x705A, 0x7222, 0x7382, 0x7381, 0x7383, 0x7670, 0x77D4, 0x7C67, 0x7C66, 0x7E95, 0x826C, 0x863A, 0x8640, 0x8639, 0x863C, 0x8631, 0x863B, 0x863E, 0x8830, 0x8832, 0x882E, 0x8833, 0x8976, 0x8974, 0x8973, 0x89FE, + 0x8B8C, 0x8B8E, 0x8B8B, 0x8B88, 0x8C45, 0x8D19, 0x8E98, 0x8F64, 0x8F63, 0x91BC, 0x9462, 0x9455, 0x945D, 0x9457, 0x945E, 0x97C4, 0x97C5, 0x9800, 0x9A56, 0x9A59, 0x9B1E, 0x9B1F, 0x9B20, 0x9C52, 0x9C58, 0x9C50, 0x9C4A, 0x9C4D, 0x9C4B, 0x9C55, 0x9C59, 0x9C4C, 0x9C4E, 0x9DFB, 0x9DF7, 0x9DEF, 0x9DE3, 0x9DEB, 0x9DF8, 0x9DE4, 0x9DF6, 0x9DE1, 0x9DEE, 0x9DE6, 0x9DF2, 0x9DF0, 0x9DE2, 0x9DEC, 0x9DF4, 0x9DF3, 0x9DE8, 0x9DED, 0x9EC2, 0x9ED0, 0x9EF2, 0x9EF3, 0x9F06, 0x9F1C, 0x9F38, 0x9F37, 0x9F36, 0x9F43, 0x9F4F, 0x9F71, 0x9F70, 0x9F6E, 0x9F6F, 0x56D3, 0x56CD, 0x5B4E, 0x5C6D, 0x652D, 0x66ED, 0x66EE, 0x6B13, 0x705F, 0x7061, 0x705D, 0x7060, 0x7223, 0x74DB, 0x74E5, 0x77D5, 0x7938, 0x79B7, 0x79B6, 0x7C6A, 0x7E97, 0x7F89, 0x826D, 0x8643, 0x8838, 0x8837, 0x8835, 0x884B, 0x8B94, 0x8B95, 0x8E9E, 0x8E9F, 0x8EA0, 0x8E9D, 0x91BE, 0x91BD, 0x91C2, 0x946B, 0x9468, 0x9469, 0x96E5, 0x9746, 0x9743, 0x9747, 0x97C7, 0x97E5, 0x9A5E, 0x9AD5, 0x9B59, 0x9C63, 0x9C67, 0x9C66, 0x9C62, 0x9C5E, 0x9C60, 0x9E02, 0x9DFE, 0x9E07, 0x9E03, 0x9E06, 0x9E05, 0x9E00, 0x9E01, 0x9E09, 0x9DFF, 0x9DFD, 0x9E04, 0x9EA0, 0x9F1E, 0x9F46, 0x9F74, 0x9F75, 0x9F76, 0x56D4, 0x652E, 0x65B8, 0x6B18, 0x6B19, 0x6B17, 0x6B1A, 0x7062, 0x7226, 0x72AA, 0x77D8, 0x77D9, 0x7939, 0x7C69, 0x7C6B, 0x7CF6, 0x7E9A, + 0x7E98, 0x7E9B, 0x7E99, 0x81E0, 0x81E1, 0x8646, 0x8647, 0x8648, 0x8979, 0x897A, 0x897C, 0x897B, 0x89FF, 0x8B98, 0x8B99, 0x8EA5, 0x8EA4, 0x8EA3, 0x946E, 0x946D, 0x946F, 0x9471, 0x9473, 0x9749, 0x9872, 0x995F, 0x9C68, 0x9C6E, 0x9C6D, 0x9E0B, 0x9E0D, 0x9E10, 0x9E0F, 0x9E12, 0x9E11, 0x9EA1, 0x9EF5, 0x9F09, 0x9F47, 0x9F78, 0x9F7B, 0x9F7A, 0x9F79, 0x571E, 0x7066, 0x7C6F, 0x883C, 0x8DB2, 0x8EA6, 0x91C3, 0x9474, 0x9478, 0x9476, 0x9475, 0x9A60, 0x9C74, 0x9C73, 0x9C71, 0x9C75, 0x9E14, 0x9E13, 0x9EF6, 0x9F0A, 0x9FA4, 0x7068, 0x7065, 0x7CF7, 0x866A, 0x883E, 0x883D, 0x883F, 0x8B9E, 0x8C9C, 0x8EA9, 0x8EC9, 0x974B, 0x9873, 0x9874, 0x98CC, 0x9961, 0x99AB, 0x9A64, 0x9A66, 0x9A67, 0x9B24, 0x9E15, 0x9E17, 0x9F48, 0x6207, 0x6B1E, 0x7227, 0x864C, 0x8EA8, 0x9482, 0x9480, 0x9481, 0x9A69, 0x9A68, 0x9B2E, 0x9E19, 0x7229, 0x864B, 0x8B9F, 0x9483, 0x9C79, 0x9EB7, 0x7675, 0x9A6B, 0x9C7A, 0x9E1D, 0x7069, 0x706A, 0x9EA4, 0x9F7E, 0x9F49, 0x9F98, 0x7881, 0x92B9, 0x88CF, 0x58BB, 0x6052, 0x7CA7, 0x5AFA, 0x2554, 0x2566, 0x2557, 0x2560, 0x256C, 0x2563, 0x255A, 0x2569, 0x255D, 0x2552, 0x2564, 0x2555, 0x255E, 0x256A, 0x2561, 0x2558, 0x2567, 0x255B, 0x2553, 0x2565, 0x2556, 0x255F, 0x256B, 0x2562, 0x2559, 0x2568, 0x255C, 0x2551, 0x2550, 0x256D, 0x256E, 0x2570, 0x256F, 0x2593, +}; + +} diff --git a/core/libs/prx/libSceCesCs/Export.cpp b/core/libs/prx/libSceCesCs/Export.cpp index 50005b98f8..25dcb94514 100644 --- a/core/libs/prx/libSceCesCs/Export.cpp +++ b/core/libs/prx/libSceCesCs/Export.cpp @@ -8,6 +8,8 @@ #include "prx/libc/include/General.hpp" #include "JisX0208.hpp" #include "JisX0212.hpp" +#include "Cp949.hpp" +#include "Cp950.hpp" namespace { @@ -152,7 +154,119 @@ const void* contextProfile(const void* context) { return profile; } +constexpr uint8_t EUC_KR_TAG[8] = {'E', 'U', 'C', 'K', 'R', '9', '4', '9'}; +constexpr uint8_t UHC_TAG[8] = {'U', 'H', 'C', 'C', 'P', '9', '4', '9'}; +constexpr uint8_t BIG5_TAG[8] = {'B', 'I', 'G', '5', 'C', '9', '5', '0'}; +constexpr uint16_t BIG5_LAST_CELL[6] = {0x2550, 0x255E, 0x2561, 0x256A, 0x5341, 0x5345}; + +bool isEucKr(const void* profile) { + return profile != nullptr && std::memcmp(profile, EUC_KR_TAG, sizeof(EUC_KR_TAG)) == 0; +} + +bool isUhc(const void* profile) { + return profile != nullptr && std::memcmp(profile, UHC_TAG, sizeof(UHC_TAG)) == 0; +} + +bool isBig5(const void* profile) { + return profile != nullptr && std::memcmp(profile, BIG5_TAG, sizeof(BIG5_TAG)) == 0; +} + +bool isMbcs(const void* profile) { + return isEucJp(profile) || isSjis(profile) || isSbc(profile) || isEucKr(profile) || isUhc(profile) || isBig5(profile); +} + +int decodeCp949(bool unified, const uint8_t* mbcs, uint32_t mbcsMax, uint32_t& length, uint32_t& code) { + const uint8_t lead = mbcs[0]; + if (lead < 0x80) { + length = 1; + code = lead; + return 0; + } + if (lead < (unified ? 0x81 : 0xA1) || lead == 0xFF) return CES_ERROR_INVALID_ENCODE; + if (mbcsMax < 2) return CES_ERROR_SRC_BUFFER_END; + const uint8_t trail = mbcs[1]; + const bool extended = unified && ((trail >= 0x41 && trail <= 0x5A) || (trail >= 0x61 && trail <= 0x7A) || (trail >= 0x81 && trail <= 0xA0)); + if (!extended && (trail < 0xA1 || trail == 0xFF)) return CES_ERROR_INVALID_ENCODE; + length = 2; + code = Cp949::TO_UCS2[(lead - 0x81) * 190 + (trail - 0x41)]; + return code != 0 ? 0 : CES_ERROR_UNASSIGNED_CODE; +} + +int decodeCp950(const uint8_t* mbcs, uint32_t mbcsMax, uint32_t& length, uint32_t& code) { + const uint8_t lead = mbcs[0]; + if (lead < 0x80) { + length = 1; + code = lead; + return 0; + } + if (lead == 0x80 || lead == 0xFF) return CES_ERROR_INVALID_ENCODE; + if (mbcsMax < 2) return CES_ERROR_SRC_BUFFER_END; + const uint8_t trail = mbcs[1]; + if (trail < 0x40 || (trail > 0x7E && trail < 0xA1) || trail == 0xFF) return CES_ERROR_INVALID_ENCODE; + if (lead < 0xA1 || lead > 0xF9) return CES_ERROR_UNASSIGNED_CODE; + length = 2; + code = Cp950::TO_UCS2[(lead - 0xA1) * 157 + (trail < 0xA1 ? trail - 0x40 : trail - 0xA1 + 63)]; + return code != 0 ? 0 : CES_ERROR_UNASSIGNED_CODE; +} + +struct DbcsCell { + uint16_t code; + uint16_t bytes; +}; + +bool byCode(const DbcsCell& left, const DbcsCell& right) { + return left.code < right.code; +} + +const std::vector& cp949Reverse() { + static const auto reverse = [] { + std::vector entries; + for (uint32_t cell = 0; cell < 126 * 190; ++cell) { + if (Cp949::TO_UCS2[cell] != 0) entries.push_back({Cp949::TO_UCS2[cell], static_cast((0x81 + cell / 190) << 8 | (0x41 + cell % 190))}); + } + std::sort(entries.begin(), entries.end(), byCode); + return entries; + }(); + return reverse; +} + +const std::vector& cp950Reverse() { + static const auto reverse = [] { + std::vector entries; + for (uint32_t cell = 0; cell < 89 * 157; ++cell) { + const uint32_t column = cell % 157; + const uint32_t trail = column < 63 ? 0x40 + column : 0xA1 + column - 63; + if (Cp950::TO_UCS2[cell] != 0) entries.push_back({Cp950::TO_UCS2[cell], static_cast((0xA1 + cell / 157) << 8 | trail)}); + } + std::stable_sort(entries.begin(), entries.end(), byCode); + std::vector unique; + for (const DbcsCell& entry : entries) { + if (unique.empty() || unique.back().code != entry.code) unique.push_back(entry); + else if (std::find(std::begin(BIG5_LAST_CELL), std::end(BIG5_LAST_CELL), entry.code) != std::end(BIG5_LAST_CELL)) unique.back() = entry; + } + return unique; + }(); + return reverse; +} + +int encodeDbcs(const std::vector& reverse, bool euc, uint32_t code, uint8_t* mbcs, uint32_t& length) { + if (code < 0x80) { + length = 1; + mbcs[0] = static_cast(code); + return 0; + } + const auto found = std::lower_bound(reverse.begin(), reverse.end(), code, [](const DbcsCell& entry, uint32_t value) { return entry.code < value; }); + if (found == reverse.end() || found->code != code) return CES_ERROR_UNASSIGNED_CODE; + if (euc && ((found->bytes >> 8) < 0xA1 || (found->bytes & 0xFF) < 0xA1)) return CES_ERROR_UNASSIGNED_CODE; + length = 2; + mbcs[0] = static_cast(found->bytes >> 8); + mbcs[1] = static_cast(found->bytes & 0xFF); + return 0; +} + int decodeMbcs(const void* profile, const uint8_t* mbcs, uint32_t mbcsMax, uint32_t& length, uint32_t& code) { + if (isEucKr(profile) || isUhc(profile)) return decodeCp949(isUhc(profile), mbcs, mbcsMax, length, code); + if (isBig5(profile)) return decodeCp950(mbcs, mbcsMax, length, code); const uint8_t lead = mbcs[0]; if (isSbc(profile)) { length = 1; @@ -262,6 +376,8 @@ const std::vector& jisReverse() { } int encodeMbcs(const void* profile, uint32_t code, uint8_t* mbcs, uint32_t& length) { + if (isEucKr(profile) || isUhc(profile)) return encodeDbcs(cp949Reverse(), isEucKr(profile), code, mbcs, length); + if (isBig5(profile)) return encodeDbcs(cp950Reverse(), false, code, mbcs, length); if (isSbc(profile)) { length = 1; return unicodeToSbc(static_cast(profile), code, mbcs[0]) ? 0 : CES_ERROR_UNASSIGNED_CODE; @@ -336,7 +452,7 @@ template int toMbcs(const void* context, const Source* source, uint32_t sourceMax, uint32_t* sourceLen, uint8_t* mbcs, uint32_t mbcsMax, uint32_t* mbcsLen, Decode decode) { if (!context) return CES_ERROR_INVALID_PARAMETER; const void* profile = contextProfile(context); - if (!isEucJp(profile) && !isSjis(profile) && !isSbc(profile)) return CES_ERROR_INVALID_PARAMETER; + if (!isMbcs(profile)) return CES_ERROR_INVALID_PARAMETER; if (!source) return CES_ERROR_INVALID_SRC_BUFFER; uint32_t read = 0; uint32_t written = 0; @@ -363,7 +479,7 @@ template int fromMbcs(const void* context, const uint8_t* mbcs, uint32_t mbcsMax, uint32_t* mbcsLen, Target* target, uint32_t targetMax, uint32_t* targetLen, Encode encode) { if (!context) return CES_ERROR_INVALID_PARAMETER; const void* profile = contextProfile(context); - if (!isEucJp(profile) && !isSjis(profile) && !isSbc(profile)) return CES_ERROR_INVALID_PARAMETER; + if (!isMbcs(profile)) return CES_ERROR_INVALID_PARAMETER; if (!mbcs) return CES_ERROR_INVALID_SRC_BUFFER; uint32_t read = 0; uint32_t written = 0; @@ -385,6 +501,23 @@ int fromMbcs(const void* context, const uint8_t* mbcs, uint32_t mbcsMax, uint32_ return 0; } +int mbcsToUtf8(bool accepted, const void* profile, const uint8_t* mbcs, uint32_t mbcsMax, uint32_t* mbcsLen, uint8_t* utf8, uint32_t utf8Max, uint32_t* utf8Len) { + if (!accepted) return CES_ERROR_INVALID_PARAMETER; + if (!mbcs) return CES_ERROR_INVALID_SRC_BUFFER; + if (mbcsMax == 0) return CES_ERROR_SRC_BUFFER_END; + if (!utf8) return CES_ERROR_INVALID_DST_BUFFER; + uint32_t consumed = 0; + uint32_t code = 0; + if (const int result = decodeMbcs(profile, mbcs, mbcsMax, consumed, code); result != 0) return result; + uint8_t encoded[3]; + const uint32_t produced = encodeUtf8(code, encoded); + if (utf8Max < produced) return CES_ERROR_DST_BUFFER_END; + std::memcpy(utf8, encoded, produced); + if (mbcsLen) *mbcsLen = consumed; + if (utf8Len) *utf8Len = produced; + return 0; +} + } extern "C" { @@ -419,6 +552,24 @@ void* APS5_VABI sceCesUcsProfileInitEucJpCp51932(void* sheet) { return sheet; } +void* APS5_VABI sceCesUcsProfileInitEucKr(void* sheet) { + if (!sheet) return nullptr; + std::memcpy(sheet, EUC_KR_TAG, sizeof(EUC_KR_TAG)); + return sheet; +} + +void* APS5_VABI sceCesUcsProfileInitUhc(void* sheet) { + if (!sheet) return nullptr; + std::memcpy(sheet, UHC_TAG, sizeof(UHC_TAG)); + return sheet; +} + +void* APS5_VABI sceCesUcsProfileInitBig5Cp950(void* sheet) { + if (!sheet) return nullptr; + std::memcpy(sheet, BIG5_TAG, sizeof(BIG5_TAG)); + return sheet; +} + int APS5_VABI sceCesUtf8StrToMbcsStr(void* context, const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8Len, uint8_t* mbcs, uint32_t mbcsMax, uint32_t* mbcsLen) { if (!mbcs) return CES_ERROR_INVALID_DST_BUFFER; return toMbcs(context, utf8, utf8max, utf8Len, mbcs, mbcsMax, mbcsLen, decodeUtf8); @@ -512,36 +663,15 @@ int APS5_VABI sceCesEucJpToUtf8(const void* profile, const uint8_t* eucBuffer, u } int APS5_VABI sceCesEucKrToUtf8(void* profile, uint8_t* eucBuffer, uint32_t eucMax, uint32_t* eucLength, uint8_t* utf8Buffer, uint32_t utf8Max, uint32_t* utf8Length) { - (void)profile; - (void)eucBuffer; - (void)eucMax; - (void)eucLength; - (void)utf8Buffer; - (void)utf8Max; - (void)utf8Length; - NotImplemented_nid_no_patch(__func__); + return mbcsToUtf8(isEucKr(profile), profile, eucBuffer, eucMax, eucLength, utf8Buffer, utf8Max, utf8Length); } int APS5_VABI sceCesBig5ToUtf8(void* profile, uint8_t* big5Buffer, uint32_t big5Max, uint32_t* big5Length, uint8_t* utf8Buffer, uint32_t utf8Max, uint32_t* utf8Length) { - (void)profile; - (void)big5Buffer; - (void)big5Max; - (void)big5Length; - (void)utf8Buffer; - (void)utf8Max; - (void)utf8Length; - NotImplemented_nid_no_patch(__func__); + return mbcsToUtf8(isBig5(profile), profile, big5Buffer, big5Max, big5Length, utf8Buffer, utf8Max, utf8Length); } int APS5_VABI sceCesUhcToUtf8(void* profile, uint8_t* uhcBuffer, uint32_t uhcMax, uint32_t* uhcLength, uint8_t* utf8Buffer, uint32_t utf8Max, uint32_t* utf8Length) { - (void)profile; - (void)uhcBuffer; - (void)uhcMax; - (void)uhcLength; - (void)utf8Buffer; - (void)utf8Max; - (void)utf8Length; - NotImplemented_nid_no_patch(__func__); + return mbcsToUtf8(isUhc(profile), profile, uhcBuffer, uhcMax, uhcLength, utf8Buffer, utf8Max, utf8Length); } } diff --git a/core/libs/tests/GuestCesDbcs.cpp b/core/libs/tests/GuestCesDbcs.cpp new file mode 100644 index 0000000000..552f5193e9 --- /dev/null +++ b/core/libs/tests/GuestCesDbcs.cpp @@ -0,0 +1,179 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include + +extern "C" { +void* APS5_VABI sceCesUcsProfileInitEucKr(void*); +void* APS5_VABI sceCesUcsProfileInitUhc(void*); +void* APS5_VABI sceCesUcsProfileInitBig5Cp950(void*); +void* APS5_VABI sceCesUcsProfileInitEucJpCp51932(void*); +int APS5_VABI sceCesEucKrToUtf8(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); +int APS5_VABI sceCesUhcToUtf8(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); +int APS5_VABI sceCesBig5ToUtf8(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); +int APS5_VABI sceCesMbcsUcsContextInit(void*, const void*); +int APS5_VABI sceCesUtf8StrToMbcsStr(void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); +int APS5_VABI sceCesMbcsStrToUtf8Str(void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); +int APS5_VABI sceCesUcs2StrToMbcsStr(void*, const std::uint16_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); +int APS5_VABI sceCesMbcsStrToUcs2Str(void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint16_t*, std::uint32_t, std::uint32_t*); +} + +using ToUtf8 = int (APS5_VABI*)(const void*, const std::uint8_t*, std::uint32_t, std::uint32_t*, std::uint8_t*, std::uint32_t, std::uint32_t*); + +constexpr int invalidParameter = static_cast(0x805C0001); +constexpr int invalidSrcBuffer = static_cast(0x805C0010); +constexpr int srcBufferEnd = static_cast(0x805C0011); +constexpr int invalidEncode = static_cast(0x805C0014); +constexpr int unassignedCode = static_cast(0x805C0020); +constexpr int invalidDstBuffer = static_cast(0x805C0030); +constexpr int dstBufferEnd = static_cast(0x805C0031); + +static void Require(bool value) { if (!value) std::abort(); } + +static const std::uint8_t* Bytes(const char* text) { return reinterpret_cast(text); } + +static std::uint32_t Size(const char* text) { return static_cast(std::strlen(text)); } + +static std::uint64_t Fnv(std::uint64_t hash, std::uint8_t byte) { return (hash ^ byte) * 0x100000001B3ull; } + +static void RequireTable(ToUtf8 convert, const void* profile, std::uint64_t expectedHash, std::uint32_t expectedCells) { + std::uint64_t hash = 0xCBF29CE484222325ull; + std::uint32_t cells = 0; + for (std::uint32_t lead = 0; lead < 256; ++lead) { + for (std::uint32_t trail = 0; trail < 256; ++trail) { + const std::uint8_t source[2] = {static_cast(lead), static_cast(trail)}; + std::uint8_t utf8[4] = {}; + std::uint32_t consumed = 0; + std::uint32_t produced = 0; + const int result = convert(profile, source, sizeof(source), &consumed, utf8, sizeof(utf8), &produced); + hash = Fnv(hash, static_cast(result & 0xFF)); + if (result != 0) continue; + hash = Fnv(hash, static_cast(consumed)); + for (std::uint32_t i = 0; i < produced; ++i) hash = Fnv(hash, utf8[i]); + if (consumed == 2) ++cells; + } + } + Require(hash == expectedHash && cells == expectedCells); +} + +static void RequireScalar(ToUtf8 convert, const void* profile, const char* source, const char* expected) { + std::uint8_t utf8[4] = {}; + std::uint32_t consumed = 0; + std::uint32_t produced = 0; + Require(convert(profile, Bytes(source), Size(source), &consumed, utf8, sizeof(utf8), &produced) == 0); + Require(consumed == Size(source) && produced == Size(expected) && std::memcmp(utf8, expected, produced) == 0); +} + +static int Scalar(ToUtf8 convert, const void* profile, const char* source, std::uint32_t sourceMax, std::uint32_t utf8Max) { + std::uint8_t utf8[4] = {0x55, 0x55, 0x55, 0x55}; + std::uint32_t consumed = 77; + std::uint32_t produced = 77; + const int result = convert(profile, Bytes(source), sourceMax, &consumed, utf8, utf8Max, &produced); + if (result != 0) Require(consumed == 77 && produced == 77 && std::memcmp(utf8, "\x55\x55\x55\x55", 4) == 0); + return result; +} + +static void RequireString(void* context, const char* mbcs, const char* utf8) { + std::uint8_t converted[32] = {}; + std::uint32_t read = 0; + std::uint32_t written = 0; + Require(sceCesMbcsStrToUtf8Str(context, Bytes(mbcs), Size(mbcs), &read, converted, sizeof(converted), &written) == 0); + Require(read == Size(mbcs) && written == Size(utf8) && std::memcmp(converted, utf8, written) == 0); + std::uint8_t back[32] = {}; + Require(sceCesUtf8StrToMbcsStr(context, Bytes(utf8), Size(utf8), &read, back, sizeof(back), &written) == 0); + Require(read == Size(utf8) && written == Size(mbcs) && std::memcmp(back, mbcs, written) == 0); +} + +static int Utf8ToMbcs(void* context, const char* utf8) { + std::uint8_t mbcs[8] = {}; + std::uint32_t read = 0; + std::uint32_t written = 0; + return sceCesUtf8StrToMbcsStr(context, Bytes(utf8), Size(utf8), &read, mbcs, sizeof(mbcs), &written); +} + +static std::uint32_t RoundTripMismatches(void* context) { + std::uint32_t mismatches = 0; + for (std::uint32_t lead = 0x81; lead < 0xFF; ++lead) { + for (std::uint32_t trail = 0x40; trail < 0xFF; ++trail) { + const std::uint8_t source[2] = {static_cast(lead), static_cast(trail)}; + std::uint16_t ucs2 = 0; + std::uint32_t read = 0; + std::uint32_t written = 0; + if (sceCesMbcsStrToUcs2Str(context, source, sizeof(source), &read, &ucs2, 1, &written) != 0) continue; + Require(read == 2 && written == 1); + std::uint8_t back[2] = {}; + Require(sceCesUcs2StrToMbcsStr(context, &ucs2, 1, &read, back, sizeof(back), &written) == 0); + Require(read == 1 && written == 2); + if (std::memcmp(source, back, sizeof(back)) != 0) ++mismatches; + } + } + return mismatches; +} + +int main() { + alignas(8) std::uint8_t eucKrSheet[64] = {}; + alignas(8) std::uint8_t uhcSheet[64] = {}; + alignas(8) std::uint8_t big5Sheet[64] = {}; + alignas(8) std::uint8_t eucJpSheet[64] = {}; + Require(sceCesUcsProfileInitEucKr(nullptr) == nullptr); + Require(sceCesUcsProfileInitUhc(nullptr) == nullptr); + Require(sceCesUcsProfileInitBig5Cp950(nullptr) == nullptr); + void* eucKr = sceCesUcsProfileInitEucKr(eucKrSheet); + void* uhc = sceCesUcsProfileInitUhc(uhcSheet); + void* big5 = sceCesUcsProfileInitBig5Cp950(big5Sheet); + void* eucJp = sceCesUcsProfileInitEucJpCp51932(eucJpSheet); + Require(eucKr == eucKrSheet && uhc == uhcSheet && big5 == big5Sheet && eucJp == eucJpSheet); + + RequireTable(sceCesEucKrToUtf8, eucKr, 0x01FF720E36F4AD67ull, 8226); + RequireTable(sceCesUhcToUtf8, uhc, 0x3B861A7E911985ABull, 17048); + RequireTable(sceCesBig5ToUtf8, big5, 0xE4D0D6A8019F35D9ull, 13503); + + RequireScalar(sceCesEucKrToUtf8, eucKr, "\xB0\xA1", "\xEA\xB0\x80"); + RequireScalar(sceCesEucKrToUtf8, eucKr, "\xA2\xE6", "\xE2\x82\xAC"); + RequireScalar(sceCesUhcToUtf8, uhc, "\xB0\xA1", "\xEA\xB0\x80"); + RequireScalar(sceCesUhcToUtf8, uhc, "\x8C\x63", "\xEB\x98\xA0"); + RequireScalar(sceCesBig5ToUtf8, big5, "\xA4\xA4", "\xE4\xB8\xAD"); + RequireScalar(sceCesBig5ToUtf8, big5, "A", "A"); + std::uint8_t zero[4] = {0x55, 0x55, 0x55, 0x55}; + Require(sceCesUhcToUtf8(uhc, Bytes("\0"), 1, nullptr, zero, sizeof(zero), nullptr) == 0 && zero[0] == 0 && zero[1] == 0x55); + + Require(Scalar(sceCesEucKrToUtf8, nullptr, "\xB0\xA1", 2, 4) == invalidParameter); + Require(Scalar(sceCesEucKrToUtf8, uhc, "\xB0\xA1", 2, 4) == invalidParameter); + Require(Scalar(sceCesUhcToUtf8, eucKr, "\xB0\xA1", 2, 4) == invalidParameter); + Require(Scalar(sceCesBig5ToUtf8, eucJp, "\xA4\xA4", 2, 4) == invalidParameter); + Require(Scalar(sceCesEucKrToUtf8, eucKr, nullptr, 2, 4) == invalidSrcBuffer); + Require(Scalar(sceCesEucKrToUtf8, eucKr, "\xB0\xA1", 0, 4) == srcBufferEnd); + Require(Scalar(sceCesUhcToUtf8, uhc, "\x8C\x63", 1, 4) == srcBufferEnd); + Require(Scalar(sceCesBig5ToUtf8, big5, "\xA4\xA4", 1, 4) == srcBufferEnd); + Require(Scalar(sceCesEucKrToUtf8, eucKr, "\xB0\xA1", 2, 2) == dstBufferEnd); + Require(Scalar(sceCesEucKrToUtf8, eucKr, "\x8C\x63", 2, 4) == invalidEncode); + Require(Scalar(sceCesUhcToUtf8, uhc, "\x80\xA1", 2, 4) == invalidEncode); + Require(Scalar(sceCesUhcToUtf8, uhc, "\xB0\x5B", 2, 4) == invalidEncode); + Require(Scalar(sceCesUhcToUtf8, uhc, "\xC9\xA1", 2, 4) == unassignedCode); + Require(Scalar(sceCesUhcToUtf8, uhc, "\xC7\x41", 2, 4) == unassignedCode); + Require(Scalar(sceCesBig5ToUtf8, big5, "\xA4\x80", 2, 4) == invalidEncode); + Require(Scalar(sceCesBig5ToUtf8, big5, "\xFF\xA1", 2, 4) == invalidEncode); + Require(Scalar(sceCesBig5ToUtf8, big5, "\x81\x40", 2, 4) == unassignedCode); + Require(Scalar(sceCesBig5ToUtf8, big5, "\xFA\x40", 2, 4) == unassignedCode); + Require(sceCesEucKrToUtf8(eucKr, Bytes("\xB0\xA1"), 2, nullptr, nullptr, 4, nullptr) == invalidDstBuffer); + + alignas(8) std::uint8_t context[64] = {}; + Require(sceCesMbcsUcsContextInit(context, eucKr) == 0); + RequireString(context, "\xC7\xD1\xB1\xB9\xBE\xEE A", "\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4 A"); + Require(Utf8ToMbcs(context, "\xEB\x98\xA0") == unassignedCode); + Require(RoundTripMismatches(context) == 0); + + Require(sceCesMbcsUcsContextInit(context, uhc) == 0); + RequireString(context, "\x8C\x63\xC7\xD1", "\xEB\x98\xA0\xED\x95\x9C"); + Require(Utf8ToMbcs(context, "\xE4\xB8\xAD") == 0); + Require(Utf8ToMbcs(context, "\xE2\x82\xAC\xEF\xBF\xBF") == unassignedCode); + Require(RoundTripMismatches(context) == 0); + + Require(sceCesMbcsUcsContextInit(context, big5) == 0); + RequireString(context, "\xA4\xA4\xA4\xE5", "\xE4\xB8\xAD\xE6\x96\x87"); + RequireString(context, "\xF9\xF9\xA2\x7E\xA4\x51\xA4\xCA", "\xE2\x95\x90\xE2\x95\xAD\xE5\x8D\x81\xE5\x8D\x85"); + RequireString(context, "\xF9\xE9\xF9\xEB\xF9\xEA\xA2\xA1\xA2\xA3\xA2\xA2", "\xE2\x95\x9E\xE2\x95\xA1\xE2\x95\xAA\xE2\x95\xAE\xE2\x95\xAF\xE2\x95\xB0"); + Require(Utf8ToMbcs(context, "\xEA\xB0\x80") == unassignedCode); + Require(RoundTripMismatches(context) == 10); + return 0; +} diff --git a/docs/dev/debt/libSceCesCs.md b/docs/dev/debt/libSceCesCs.md index df97d9e096..14cef5e67e 100644 --- a/docs/dev/debt/libSceCesCs.md +++ b/docs/dev/debt/libSceCesCs.md @@ -12,3 +12,4 @@ - [sceCesEucJpToUtf8](../../../core/libs/prx/libSceCesCs/Export.cpp) (libSceCesCs) - the scalar conversion's errors and optional length pointers are unverified against the console. Its signature follows the Orbital type database linked above; it uses the existing CP51932 profile and JIS tables, with their mapping and profile-layout assumptions listed above, converts one character (including zero), and writes no terminator. Failure leaves the output and length pointers unchanged - [sceCesRefersUcsProfileCp1250](../../../core/libs/prx/libSceCesCs/Export.cpp) (libSceCesCs) - returns a profile accepted by `sceCesSbcToUtf8`, `sceCesUtf8ToSbc` and the MBCS context conversions, with the Windows-1250 table of Microsoft's [bestfit1250.txt](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit1250.txt), where the unassigned bytes map to their Latin-1 control characters as the Cp1252 and Cp1251 tables do; the console behavior for those bytes and the whole profile is not verified. PPSA34501 fails to start without this export (`qYu6y1E0Qbg`) - [libSceCesCs firmware 3.20 pending exports](../../../core/libs/prx/libSceCesCs/PendingExports.cpp) - 249 symbol names are present in [the PS5 3.20 libSceCesCs export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceCesCs.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [EUC-KR, UHC and Big5 conversions](../../../core/libs/prx/libSceCesCs/Export.cpp) (libSceCesCs) - the tables, errors and Big5 duplicate choices are assumed, not measured. `sceCesUcsProfileInitEucKr`, `sceCesUcsProfileInitUhc` and `sceCesUcsProfileInitBig5Cp950` tag the caller's sheet like the EUC-JP profile. The tables are Microsoft's [CP949.TXT](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT) and [CP950.TXT](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP950.TXT); EUC-KR is CP949 limited to bytes `0xA1`-`0xFE`. Bytes below `0x80` are ASCII; the KS X 1003 single-byte profile (`sceCesEucKrUcsProfileSetSbcs`) is not implemented. The user-defined areas, which Windows maps to the Private Use Area, answer `UNASSIGNED_CODE`. Big5 encoding takes the lower of two cells except for U+2550, U+255E, U+2561, U+256A, U+5341 and U+5345, as Windows code page 950 does. The `*ToUtf8` calls convert one character and leave their outputs unchanged on failure From 54d1a8d6d624e278f6f6927fd08ad2bb5c02241c Mon Sep 17 00:00:00 2001 From: jagrat Singh Date: Tue, 20 Oct 2026 10:18:39 +0530 Subject: [PATCH 127/523] feat(libSceNet): implement sceNetShowRoute6 --- core/libs/prx/libSceNet/Export.cpp | 1 - core/libs/tests/GuestSceNet.cpp | 2 ++ docs/dev/debt/libSceNet.md | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 82fb9cbd4c..d6a9bd0b15 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2086,7 +2086,6 @@ int APS5_VABI sceNetSetDns6Info(void) { } int APS5_VABI sceNetShowRoute6(void) { - NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index b3499c1492..3f00cfe6d4 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -36,6 +36,7 @@ int APS5_VABI sceNetShowNetstatWithMemory(int); int APS5_VABI sceNetShowNetstat(void); int APS5_VABI sceNetShowIfconfig(void); int APS5_VABI sceNetShowRoute(void); +int APS5_VABI sceNetShowRoute6(void); int APS5_VABI sceNetSocket(const char*, int, int, int); int APS5_VABI sceNetBind_nid_postfix(int, const void*, std::uint32_t); int APS5_VABI sceNetListen(int, int); @@ -241,6 +242,7 @@ static void CheckNetstat() { Require(sceNetShowNetstat() == 0); Require(sceNetShowIfconfig() == 0); Require(sceNetShowRoute() == 0); + Require(sceNetShowRoute6() == 0); Require(Failed(sceNetShowNetstatWithMemory(-1), 9)); const int pool = sceNetPoolCreate("netstat", 4096, 0); Require(pool > 0); diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index cdb38de437..fa35cc1780 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -2,7 +2,7 @@ ### Silent stubs -- [sceNetShowIfconfig, sceNetShowNetstat, sceNetShowRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - return success without printing anything: the `()` signatures and the success return follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/net.cpp), which stubs each the same way. PPSA01530 fails to load without the `sceNetShowIfconfig` export (#3177); PPSA11386 imports `sceNetShowNetstat` but does not call it during start-up. What the console prints and where is unverified +- [sceNetShowIfconfig, sceNetShowNetstat, sceNetShowRoute, sceNetShowRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - return success without printing anything: the `()` signatures and the success return follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/net.cpp), which stubs each the same way. PPSA01530 fails to load without the `sceNetShowIfconfig` export (#3177); PPSA11386 imports `sceNetShowNetstat` but does not call it during start-up. What the console prints and where is unverified ### Unknown function info @@ -22,7 +22,6 @@ - [sceNetResolverConnectDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetSockInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetShowRoute6WithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From b9cdbf932d1b88f510f803cc3b295ebce9d7544c Mon Sep 17 00:00:00 2001 From: jagrat Singh Date: Tue, 20 Oct 2026 09:07:43 +0530 Subject: [PATCH 128/523] feat(libSceNpWebApi2): implement sceNpWebApi2AddWebTraceTag Implement sceNpWebApi2AddWebTraceTag with the (int64_t request_id, const char* value) signature from shadPS4. Validates that value is not null, accepts any request id, and acts as a silent stub matching the other offline request functions. --- core/libs/prx/libSceNpWebApi2/Export.cpp | 5 +++-- core/libs/tests/GuestNpWebApi2.cpp | 3 +++ docs/dev/debt/libSceNpWebApi2.md | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpWebApi2/Export.cpp b/core/libs/prx/libSceNpWebApi2/Export.cpp index bb51fabeaa..125c7fb846 100644 --- a/core/libs/prx/libSceNpWebApi2/Export.cpp +++ b/core/libs/prx/libSceNpWebApi2/Export.cpp @@ -43,8 +43,9 @@ int APS5_VABI sceNpWebApi2AddHttpRequestHeader(int64_t request_id, const char* f return 0; } -int APS5_VABI sceNpWebApi2AddWebTraceTag() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceNpWebApi2AddWebTraceTag(int64_t request_id, const char* value) { + (void)request_id; + if (!value) return SCE_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; return 0; } diff --git a/core/libs/tests/GuestNpWebApi2.cpp b/core/libs/tests/GuestNpWebApi2.cpp index fdebb7853e..8ae914ceda 100644 --- a/core/libs/tests/GuestNpWebApi2.cpp +++ b/core/libs/tests/GuestNpWebApi2.cpp @@ -24,6 +24,7 @@ int APS5_VABI sceNpWebApi2PushEventSetHandleTimeout(int, int, std::uint32_t); int APS5_VABI sceNpWebApi2CreateUserContext(int, int); int APS5_VABI sceNpWebApi2CreateRequest(int, const char*, const char*, const char*, const void*, std::int64_t*); int APS5_VABI sceNpWebApi2SetRequestTimeout(std::int64_t, std::uint32_t); +int APS5_VABI sceNpWebApi2AddWebTraceTag(std::int64_t, const char*); int APS5_VABI sceNpWebApi2DeleteRequest(std::int64_t); } @@ -57,6 +58,8 @@ int main() { std::int64_t request = 0; Require(user > 0 && sceNpWebApi2CreateRequest(user, "sessionManager", "/v1/sessions", "GET", nullptr, &request) == 0); Require(sceNpWebApi2SetRequestTimeout(request, 30000000) == 0); + Require(sceNpWebApi2AddWebTraceTag(request, "trace-tag-1") == 0); + Require(sceNpWebApi2AddWebTraceTag(request, nullptr) == static_cast(0x80553402)); Require(sceNpWebApi2DeleteRequest(request) == 0); Require(sceNpWebApi2Terminate(context) == 0); Require(Throws([&] { sceNpWebApi2GetMemoryPoolStats(context, &stats); })); diff --git a/docs/dev/debt/libSceNpWebApi2.md b/docs/dev/debt/libSceNpWebApi2.md index 14eab1d91c..7e455785d7 100644 --- a/docs/dev/debt/libSceNpWebApi2.md +++ b/docs/dev/debt/libSceNpWebApi2.md @@ -2,6 +2,7 @@ ### Silent stubs +- [sceNpWebApi2AddWebTraceTag](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - the `(request id, value)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api2/np_web_api2.cpp). PPSA33016 imports it. It validates that the value pointer is not null (returning `INVALID_ARGUMENT` 0x80553402), accepts any request id, and stores nothing: no request reaches a service, as `sceNpWebApi2SendRequest` fails with `UNAVAILABLE` at once, so trace tags are not transmitted offline - [sceNpWebApi2SetRequestTimeout](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - the `(request id, timeout)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api2/np_web_api2.cpp). Pinball FX (PPSA08755) calls it while its main menu loads. It succeeds for any request id and stores nothing: no request reaches a service, as `sceNpWebApi2SendRequest` fails with `UNAVAILABLE` at once, so no timeout can expire. shadPS4 returns `LIB_CONTEXT_NOT_FOUND`, `USER_CONTEXT_NOT_FOUND` or `REQUEST_NOT_FOUND` for an id it does not know; this library keeps no context or request table (`sceNpWebApi2CreateRequest` hands out counter values, and `sceNpWebApi2AbortRequest`, `sceNpWebApi2AddHttpRequestHeader` and `sceNpWebApi2DeleteRequest` also accept any id), so those errors are not reported until the request functions track their ids together ### Unknown function info From c5e861b868474c4d4a89d578ad095c3a078c2242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Wed, 21 Oct 2026 12:45:44 +0200 Subject: [PATCH 129/523] docs(libSceNpWebApi2): drop title id from AddWebTraceTag debt line Co-Authored-By: Claude --- docs/dev/debt/libSceNpWebApi2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceNpWebApi2.md b/docs/dev/debt/libSceNpWebApi2.md index 7e455785d7..cc65efef68 100644 --- a/docs/dev/debt/libSceNpWebApi2.md +++ b/docs/dev/debt/libSceNpWebApi2.md @@ -2,7 +2,7 @@ ### Silent stubs -- [sceNpWebApi2AddWebTraceTag](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - the `(request id, value)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api2/np_web_api2.cpp). PPSA33016 imports it. It validates that the value pointer is not null (returning `INVALID_ARGUMENT` 0x80553402), accepts any request id, and stores nothing: no request reaches a service, as `sceNpWebApi2SendRequest` fails with `UNAVAILABLE` at once, so trace tags are not transmitted offline +- [sceNpWebApi2AddWebTraceTag](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - the `(request id, value)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api2/np_web_api2.cpp). A title imports it. It validates that the value pointer is not null (returning `INVALID_ARGUMENT` 0x80553402), accepts any request id, and stores nothing: no request reaches a service, as `sceNpWebApi2SendRequest` fails with `UNAVAILABLE` at once, so trace tags are not transmitted offline - [sceNpWebApi2SetRequestTimeout](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - the `(request id, timeout)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api2/np_web_api2.cpp). Pinball FX (PPSA08755) calls it while its main menu loads. It succeeds for any request id and stores nothing: no request reaches a service, as `sceNpWebApi2SendRequest` fails with `UNAVAILABLE` at once, so no timeout can expire. shadPS4 returns `LIB_CONTEXT_NOT_FOUND`, `USER_CONTEXT_NOT_FOUND` or `REQUEST_NOT_FOUND` for an id it does not know; this library keeps no context or request table (`sceNpWebApi2CreateRequest` hands out counter values, and `sceNpWebApi2AbortRequest`, `sceNpWebApi2AddHttpRequestHeader` and `sceNpWebApi2DeleteRequest` also accept any id), so those errors are not reported until the request functions track their ids together ### Unknown function info From 02bf457027ce4fbbdaddc2cbbc0ab8bd92960366 Mon Sep 17 00:00:00 2001 From: hypermist Date: Wed, 21 Oct 2026 23:47:22 +1300 Subject: [PATCH 130/523] test(libc): load libkernel before checking the registered thread forwards On Windows a DLL is only loaded when the executable imports from it. The thread test imported nothing from libkernel, so its load-time registration never ran and _Tss_create threw. Comparing _Thrd_current with scePthreadSelf imports libkernel and checks the forward. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- core/libs/tests/GuestLibcThreads.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/libs/tests/GuestLibcThreads.cpp b/core/libs/tests/GuestLibcThreads.cpp index 102b21ada0..1310f2b947 100644 --- a/core/libs/tests/GuestLibcThreads.cpp +++ b/core/libs/tests/GuestLibcThreads.cpp @@ -54,6 +54,7 @@ int APS5_VABI _Tss_set_nid_postfix(int key, void* value); int APS5_VABI _Thrd_equal_nid_postfix(void* first, void* second); void* APS5_VABI _Thrd_current_nid_postfix(void); int APS5_VABI _Mtx_current_owns_nid_postfix(void** handle); +void* APS5_VABI scePthreadSelf(); } static void Require(bool value) { if (!value) std::abort(); } @@ -319,7 +320,9 @@ static void PadDetach() { RegisterPthreadDetach_nid_no_patch(nullptr); } +// Calling into libkernel also makes Windows load it, which registers the thread forwards. static void KernelKeys() { + Require(_Thrd_current_nid_postfix() == scePthreadSelf()); int key = -1; Require(_Tss_create_nid_postfix(&key, nullptr) == success && key >= 0); int value = 0; From 58c92541543e0983843128541a21c7354fac16e5 Mon Sep 17 00:00:00 2001 From: hypermist Date: Wed, 21 Oct 2026 23:49:07 +1300 Subject: [PATCH 131/523] test(libc): drop the comment from the thread forward check Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK --- core/libs/tests/GuestLibcThreads.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/libs/tests/GuestLibcThreads.cpp b/core/libs/tests/GuestLibcThreads.cpp index 1310f2b947..001558be39 100644 --- a/core/libs/tests/GuestLibcThreads.cpp +++ b/core/libs/tests/GuestLibcThreads.cpp @@ -320,7 +320,6 @@ static void PadDetach() { RegisterPthreadDetach_nid_no_patch(nullptr); } -// Calling into libkernel also makes Windows load it, which registers the thread forwards. static void KernelKeys() { Require(_Thrd_current_nid_postfix() == scePthreadSelf()); int key = -1; From f57ef9986f5e15a9393fbf5d7c7fe4ab746eaf85 Mon Sep 17 00:00:00 2001 From: akkadbakkad89 Date: Wed, 21 Oct 2026 16:30:58 +0530 Subject: [PATCH 132/523] feat(prx): declare exports for 66 inventory-covered system libraries --- core/libs/prx/libSceAc3Enc/Export.cpp | 35 +- core/libs/prx/libSceAudioSystem/Export.cpp | 10 +- .../prx/libSceBackupRestoreUtil/Export.cpp | 80 +- core/libs/prx/libSceComposite/Export.cpp | 390 +++++++- .../prx/libSceCustomMusicAudioOut/Export.cpp | 25 +- .../Export.cpp | 5 +- core/libs/prx/libSceDataTransfer/Export.cpp | 300 +++++- core/libs/prx/libSceDipsw/Export.cpp | 45 +- core/libs/prx/libSceDseehx/Export.cpp | 22 +- core/libs/prx/libSceDtsEnc/Export.cpp | 35 +- core/libs/prx/libSceEditMp4/Export.cpp | 45 +- core/libs/prx/libSceEsvmEngine/Export.cpp | 97 +- core/libs/prx/libSceFios2/Export.cpp | 842 ++++++++++++++++- .../prx/libSceFsInternalForVsh/Export.cpp | 882 +++++++++++++++++- core/libs/prx/libSceGic/Export.cpp | 7 +- core/libs/prx/libSceHmdSetupDialog/Export.cpp | 35 +- core/libs/prx/libSceHubAppUtil/Export.cpp | 17 +- core/libs/prx/libSceIdu/Export.cpp | 42 +- core/libs/prx/libSceLoginMgrServer/Export.cpp | 150 ++- .../libSceMusicFrameworkInterface/Export.cpp | 87 +- core/libs/prx/libSceNpEulaDialog/Export.cpp | 45 +- .../prx/libSceNpFriendListDialog/Export.cpp | 45 +- .../libs/prx/libSceNpProfileDialog/Export.cpp | 40 +- .../Export.cpp | 52 +- core/libs/prx/libSceNpSns/Export.cpp | 105 ++- .../prx/libSceNpSnsYouTubeDialog/Export.cpp | 35 +- core/libs/prx/libSceNpTcs/Export.cpp | 17 +- core/libs/prx/libScePadTracker/Export.cpp | 30 +- .../prx/libScePatchCheckerClient/Export.cpp | 62 +- core/libs/prx/libScePlayReady/Export.cpp | 250 ++++- core/libs/prx/libScePlayReady2/Export.cpp | 80 +- core/libs/prx/libScePlayReady4/Export.cpp | 177 +++- .../prx/libScePs2EmuMenuDialog/Export.cpp | 35 +- core/libs/prx/libScePsmUtil/Export.cpp | 22 +- .../prx/libSceShareFactoryUtil/Export.cpp | 32 +- core/libs/prx/libSceSocialScreen/Export.cpp | 55 +- .../prx/libSceSocialScreenDialog/Export.cpp | 35 +- .../prx/libSceSpSysCallWrapper/Export.cpp | 240 ++++- core/libs/prx/libSceSrcUtl/Export.cpp | 47 +- core/libs/prx/libSceSystemLogger/Export.cpp | 15 +- .../libSceSystemLogger2Delivery/Export.cpp | 22 +- .../Export.cpp | 27 +- core/libs/prx/libSceSystemTts/Export.cpp | 27 +- core/libs/prx/libSceTextToSpeech/Export.cpp | 50 +- core/libs/prx/libSceUpdateService/Export.cpp | 202 +++- .../prx/libSceUrlConfigResolver/Export.cpp | 22 +- .../libs/prx/libSceVdecCore.native/Export.cpp | 20 +- core/libs/prx/libSceVdecSavc/Export.cpp | 20 +- core/libs/prx/libSceVdecSavc2/Export.cpp | 20 +- core/libs/prx/libSceVdecShevc/Export.cpp | 35 +- core/libs/prx/libSceVdecwrap/Export.cpp | 55 +- .../prx/libSceVideoArbitration/Export.cpp | 17 +- .../prx/libSceVideoCoreInterface/Export.cpp | 7 +- .../libSceVideoCoreServerInterface/Export.cpp | 237 ++++- .../libSceVideoDecoderArbitration/Export.cpp | 22 +- .../libSceVideoNativeExtEssential/Export.cpp | 25 +- .../prx/libSceVideoStreamingEngine/Export.cpp | 62 +- core/libs/prx/libSceVisionManager/Export.cpp | 280 +++++- .../libs/prx/libSceVrServiceDialog/Export.cpp | 37 +- core/libs/prx/libmdbg_syscore/Export.cpp | 717 +++++++++++++- docs/dev/debt/libSceAc3Enc.md | 5 + docs/dev/debt/libSceAudioSystem.md | 5 + docs/dev/debt/libSceBackupRestoreUtil.md | 5 + docs/dev/debt/libSceComposite.md | 5 + docs/dev/debt/libSceCompositeExt.md | 5 + docs/dev/debt/libSceCustomMusicAudioOut.md | 5 + .../debt/libSceCustomMusicSysCallWrapper.md | 5 + docs/dev/debt/libSceDataTransfer.md | 5 + docs/dev/debt/libSceDipsw.md | 5 + docs/dev/debt/libSceDseehx.md | 5 + docs/dev/debt/libSceDtsEnc.md | 5 + docs/dev/debt/libSceEditMp4.md | 5 + docs/dev/debt/libSceEsvmEngine.md | 5 + docs/dev/debt/libSceFios2.md | 5 + docs/dev/debt/libSceFsInternalForVsh.md | 5 + docs/dev/debt/libSceGic.md | 5 + docs/dev/debt/libSceHmdSetupDialog.md | 5 + docs/dev/debt/libSceHubAppUtil.md | 5 + docs/dev/debt/libSceIdu.md | 5 + docs/dev/debt/libSceLoginMgrServer.md | 5 + .../dev/debt/libSceMusicFrameworkInterface.md | 5 + docs/dev/debt/libSceNpEulaDialog.md | 5 + docs/dev/debt/libSceNpFriendListDialog.md | 5 + docs/dev/debt/libSceNpProfileDialog.md | 5 + .../libSceNpRemotePlaySessionSignaling.md | 5 + docs/dev/debt/libSceNpSns.md | 8 + docs/dev/debt/libSceNpSnsYouTubeDialog.md | 5 + docs/dev/debt/libSceNpTcs.md | 5 + docs/dev/debt/libScePadTracker.md | 5 + docs/dev/debt/libScePatchCheckerClient.md | 5 + docs/dev/debt/libScePlayReady.md | 5 + docs/dev/debt/libScePlayReady2.md | 8 + docs/dev/debt/libScePlayReady4.md | 8 + docs/dev/debt/libScePs2EmuMenuDialog.md | 5 + docs/dev/debt/libScePsmUtil.md | 5 + docs/dev/debt/libSceShareFactoryUtil.md | 5 + docs/dev/debt/libSceSocialScreen.md | 5 + docs/dev/debt/libSceSocialScreenDialog.md | 5 + docs/dev/debt/libSceSpSysCallWrapper.md | 5 + docs/dev/debt/libSceSrcUtl.md | 5 + docs/dev/debt/libSceSulphaDrv.md | 5 + docs/dev/debt/libSceSystemLogger.md | 5 + docs/dev/debt/libSceSystemLogger2Delivery.md | 5 + .../libSceSystemLogger2NativeQueueClient.md | 5 + docs/dev/debt/libSceSystemTts.md | 5 + docs/dev/debt/libSceTextToSpeech.md | 5 + docs/dev/debt/libSceUpdateService.md | 5 + docs/dev/debt/libSceUrlConfigResolver.md | 1 + docs/dev/debt/libSceVdecCore.native.md | 8 + docs/dev/debt/libSceVdecSavc.md | 5 + docs/dev/debt/libSceVdecSavc2.md | 5 + docs/dev/debt/libSceVdecSavc2.native.md | 5 + docs/dev/debt/libSceVdecShevc.md | 5 + docs/dev/debt/libSceVdecShevc.native.md | 5 + docs/dev/debt/libSceVdecwrap.md | 5 + docs/dev/debt/libSceVideoArbitration.md | 5 + docs/dev/debt/libSceVideoCoreInterface.md | 1 + .../debt/libSceVideoCoreServerInterface.md | 5 + .../dev/debt/libSceVideoDecoderArbitration.md | 5 + .../dev/debt/libSceVideoNativeExtEssential.md | 5 + docs/dev/debt/libSceVideoStreamingEngine.md | 5 + .../debt/libSceVideoStreamingEngine_sys.md | 5 + docs/dev/debt/libSceVisionManager.md | 5 + docs/dev/debt/libSceVrServiceDialog.md | 5 + docs/dev/debt/libmdbg_syscore.md | 5 + 125 files changed, 6716 insertions(+), 87 deletions(-) create mode 100644 docs/dev/debt/libSceAc3Enc.md create mode 100644 docs/dev/debt/libSceAudioSystem.md create mode 100644 docs/dev/debt/libSceBackupRestoreUtil.md create mode 100644 docs/dev/debt/libSceComposite.md create mode 100644 docs/dev/debt/libSceCompositeExt.md create mode 100644 docs/dev/debt/libSceCustomMusicAudioOut.md create mode 100644 docs/dev/debt/libSceCustomMusicSysCallWrapper.md create mode 100644 docs/dev/debt/libSceDataTransfer.md create mode 100644 docs/dev/debt/libSceDipsw.md create mode 100644 docs/dev/debt/libSceDseehx.md create mode 100644 docs/dev/debt/libSceDtsEnc.md create mode 100644 docs/dev/debt/libSceEditMp4.md create mode 100644 docs/dev/debt/libSceEsvmEngine.md create mode 100644 docs/dev/debt/libSceFios2.md create mode 100644 docs/dev/debt/libSceFsInternalForVsh.md create mode 100644 docs/dev/debt/libSceGic.md create mode 100644 docs/dev/debt/libSceHmdSetupDialog.md create mode 100644 docs/dev/debt/libSceHubAppUtil.md create mode 100644 docs/dev/debt/libSceIdu.md create mode 100644 docs/dev/debt/libSceLoginMgrServer.md create mode 100644 docs/dev/debt/libSceMusicFrameworkInterface.md create mode 100644 docs/dev/debt/libSceNpEulaDialog.md create mode 100644 docs/dev/debt/libSceNpFriendListDialog.md create mode 100644 docs/dev/debt/libSceNpProfileDialog.md create mode 100644 docs/dev/debt/libSceNpRemotePlaySessionSignaling.md create mode 100644 docs/dev/debt/libSceNpSns.md create mode 100644 docs/dev/debt/libSceNpSnsYouTubeDialog.md create mode 100644 docs/dev/debt/libSceNpTcs.md create mode 100644 docs/dev/debt/libScePadTracker.md create mode 100644 docs/dev/debt/libScePatchCheckerClient.md create mode 100644 docs/dev/debt/libScePlayReady.md create mode 100644 docs/dev/debt/libScePlayReady2.md create mode 100644 docs/dev/debt/libScePlayReady4.md create mode 100644 docs/dev/debt/libScePs2EmuMenuDialog.md create mode 100644 docs/dev/debt/libScePsmUtil.md create mode 100644 docs/dev/debt/libSceShareFactoryUtil.md create mode 100644 docs/dev/debt/libSceSocialScreen.md create mode 100644 docs/dev/debt/libSceSocialScreenDialog.md create mode 100644 docs/dev/debt/libSceSpSysCallWrapper.md create mode 100644 docs/dev/debt/libSceSrcUtl.md create mode 100644 docs/dev/debt/libSceSulphaDrv.md create mode 100644 docs/dev/debt/libSceSystemLogger.md create mode 100644 docs/dev/debt/libSceSystemLogger2Delivery.md create mode 100644 docs/dev/debt/libSceSystemLogger2NativeQueueClient.md create mode 100644 docs/dev/debt/libSceSystemTts.md create mode 100644 docs/dev/debt/libSceTextToSpeech.md create mode 100644 docs/dev/debt/libSceUpdateService.md create mode 100644 docs/dev/debt/libSceVdecCore.native.md create mode 100644 docs/dev/debt/libSceVdecSavc.md create mode 100644 docs/dev/debt/libSceVdecSavc2.md create mode 100644 docs/dev/debt/libSceVdecSavc2.native.md create mode 100644 docs/dev/debt/libSceVdecShevc.md create mode 100644 docs/dev/debt/libSceVdecShevc.native.md create mode 100644 docs/dev/debt/libSceVdecwrap.md create mode 100644 docs/dev/debt/libSceVideoArbitration.md create mode 100644 docs/dev/debt/libSceVideoCoreServerInterface.md create mode 100644 docs/dev/debt/libSceVideoDecoderArbitration.md create mode 100644 docs/dev/debt/libSceVideoNativeExtEssential.md create mode 100644 docs/dev/debt/libSceVideoStreamingEngine.md create mode 100644 docs/dev/debt/libSceVideoStreamingEngine_sys.md create mode 100644 docs/dev/debt/libSceVisionManager.md create mode 100644 docs/dev/debt/libSceVrServiceDialog.md create mode 100644 docs/dev/debt/libmdbg_syscore.md diff --git a/core/libs/prx/libSceAc3Enc/Export.cpp b/core/libs/prx/libSceAc3Enc/Export.cpp index fb34206c21..4ee193119b 100644 --- a/core/libs/prx/libSceAc3Enc/Export.cpp +++ b/core/libs/prx/libSceAc3Enc/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceAc3EncCreateEncoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAc3EncDeleteEncoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAc3EncEncode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAc3EncEncodeAsync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAc3EncFlush() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAc3EncIsEncoding() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAc3EncWaitEncode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceAudioSystem/Export.cpp b/core/libs/prx/libSceAudioSystem/Export.cpp index fb34206c21..d5c4291d01 100644 --- a/core/libs/prx/libSceAudioSystem/Export.cpp +++ b/core/libs/prx/libSceAudioSystem/Export.cpp @@ -5,6 +5,14 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceAudioSystemSystemResume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceAudioSystemSystemSuspend() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceBackupRestoreUtil/Export.cpp b/core/libs/prx/libSceBackupRestoreUtil/Export.cpp index fb34206c21..77695475b9 100644 --- a/core/libs/prx/libSceBackupRestoreUtil/Export.cpp +++ b/core/libs/prx/libSceBackupRestoreUtil/Export.cpp @@ -5,6 +5,84 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceBackupRestoreUtilBackupData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilCancelBackupData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilCancelGetRequiredSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilCancelGetTitles() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilCancelRestoreData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilDeleteBackupData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilGetBackupDataInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilGetBackupParam() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilGetBackupProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilGetRequiredSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilGetRestoreProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilGetTitles() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilPrepareBackup() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilRestoreData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBackupRestoreUtilTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceComposite/Export.cpp b/core/libs/prx/libSceComposite/Export.cpp index fb34206c21..ebf58fb725 100644 --- a/core/libs/prx/libSceComposite/Export.cpp +++ b/core/libs/prx/libSceComposite/Export.cpp @@ -5,6 +5,394 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceCompositorAllocateIndex() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorCheckCrash() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorCommandGpuPerfBegin() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorCommandGpuPerfEnd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorCreateIndirectRenderTarget() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorDeleteIndirectRenderTarget() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorFlush() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorFlushWithRepeat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetAnotherProcessSystemAddress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetAnotherProcessSystemSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetAnotherProcessVideoAddress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetAnotherProcessVideoSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetCanvasHandle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetClientList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetDmemOffset() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetDummyAddress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetDummySize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetRemainedCommandNum() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetRenderTargetResolution() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetSystemAddress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetSystemSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetVideoAddress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorGetVideoSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorHandleProcessEvents() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorInitWithProcessOrder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorInsertThreadTraceMarker() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorIsDebugCaptureEnabled() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorIsReady4K() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorLockCommandBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorMakeCanvasHandle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorMapAnotherProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorMemoryPoolDecommit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorPrivacyMaskPushArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorQuit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorReleaseCommandBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorReleaseIndex() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorReleaseLocalStall() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorReserveCommandBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorResetZoomCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetAgcCompositeCanvasCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetAgcContextCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetCompositeCanvasCommandInC() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetCursorImageAddress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetDebugPositionCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetEndOfFrameCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetEventCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetFilterType() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetFlipCommand2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetGameSufaceControlCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetGnmContextCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetIndirectCanvasCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetIndirectCompositionCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetIndirectRenderTargetConfigCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetInvisibleCanvasCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetKeepDisplayBufferCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetLocalStallCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetMemoryCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetPatchCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetPostEventCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetRepeatCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetResolutionCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetScalingLayoutInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetSeparateContextCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetSystemConfigWithMask() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetVideoOutMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetVirtualCanvasAgcContextCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetVirtualCanvasCommandFooter() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetVirtualCanvasPatchCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetVrMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSetZoomCommand() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorSystemConfig() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorWaitEndOfRendering() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorWaitEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompositorWaitPostEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompsoitorGetGpuClock() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompsoitorGetProcessRenderingTime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCompsoitorGetRenderingTime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceCustomMusicAudioOut/Export.cpp b/core/libs/prx/libSceCustomMusicAudioOut/Export.cpp index fb34206c21..a383508620 100644 --- a/core/libs/prx/libSceCustomMusicAudioOut/Export.cpp +++ b/core/libs/prx/libSceCustomMusicAudioOut/Export.cpp @@ -5,6 +5,29 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceCustomMusicAudioOutFinalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCustomMusicAudioOutGetAllMusicVolume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCustomMusicAudioOutGetMuted() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCustomMusicAudioOutGetVolume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceCustomMusicAudioOutSetAllMusicVolume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceCustomMusicSysCallWrapper/Export.cpp b/core/libs/prx/libSceCustomMusicSysCallWrapper/Export.cpp index fb34206c21..ee9009496a 100644 --- a/core/libs/prx/libSceCustomMusicSysCallWrapper/Export.cpp +++ b/core/libs/prx/libSceCustomMusicSysCallWrapper/Export.cpp @@ -5,6 +5,9 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceCustomMusicSelect() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceDataTransfer/Export.cpp b/core/libs/prx/libSceDataTransfer/Export.cpp index fb34206c21..43fb2a1574 100644 --- a/core/libs/prx/libSceDataTransfer/Export.cpp +++ b/core/libs/prx/libSceDataTransfer/Export.cpp @@ -5,6 +5,304 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceDataTransferAbortFgTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferAbortRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferAbortSearchPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferCheckBgTransferRunning() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferGetFgTransferProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferGetPrepareFgTransferProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestGetAppInfoPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestGetSavedataInfoPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestGetUsersPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestLaunchPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestPrepareFgTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestRebootAuthPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestSearchPS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestStartFgTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferRequestTransferTimePS4() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortBindSavedata() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortDeactivate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortEth0() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortGetDeviceInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortGetDeviceInfoApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortGetTitles() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortGetUsers() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortLaunch() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortPrepareTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortPwrReq() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortReboot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortSendSsoNew2Old() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortSendSsoOld2New() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetAbortTransferSpeed() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetEventIsAuthReady() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetEventIsIPv6Ready() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetEventIsPwrReqReady() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetGetFailedUsers() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetGetRebootData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetGetTransferProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestAbortSearch() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestActivate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestAuth() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestBindSavedata() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestComplete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestCreateRebootData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestDeactivate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestEndTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestGetDeviceInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestGetDeviceInfoApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestGetTitles() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestGetUsers() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestLaunch() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestPrepareTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestPrepareTransferProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestPwrReq() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestReboot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestSearch() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestSendSsoNew2Old() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestSendSsoOld2New() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestStartTransfer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTargetRequestTransferSpeed() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDataTransferTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceDipsw/Export.cpp b/core/libs/prx/libSceDipsw/Export.cpp index fb34206c21..3cddf792b2 100644 --- a/core/libs/prx/libSceDipsw/Export.cpp +++ b/core/libs/prx/libSceDipsw/Export.cpp @@ -5,6 +5,49 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceKernelCheckDipsw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelInitializeDipsw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelIsAssistMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelIsDevelopmentMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelIsDisableRazor() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelReadDipswData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelSetDipsw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelUnsetDipsw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceKernelWriteDipswData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceDseehx/Export.cpp b/core/libs/prx/libSceDseehx/Export.cpp index d81f4a5e32..43fce06ef6 100644 --- a/core/libs/prx/libSceDseehx/Export.cpp +++ b/core/libs/prx/libSceDseehx/Export.cpp @@ -1,10 +1,28 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceDseehxConvert() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDseehxExtInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDseehxFlush() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDseehxInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceDtsEnc/Export.cpp b/core/libs/prx/libSceDtsEnc/Export.cpp index fb34206c21..06a9bc9f32 100644 --- a/core/libs/prx/libSceDtsEnc/Export.cpp +++ b/core/libs/prx/libSceDtsEnc/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceDtsEncCreateEncoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDtsEncDeleteEncoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDtsEncEncode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDtsEncEncodeAsync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDtsEncFlush() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDtsEncIsEncoding() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDtsEncWaitEncode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceEditMp4/Export.cpp b/core/libs/prx/libSceEditMp4/Export.cpp index fb34206c21..9bafeb8142 100644 --- a/core/libs/prx/libSceEditMp4/Export.cpp +++ b/core/libs/prx/libSceEditMp4/Export.cpp @@ -5,6 +5,49 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceEditMp4Abort() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4Edit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4GetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4Initialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4InitializeParam() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4QueryMemSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4SetFinishCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4Terminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEditMp4Unite() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceEsvmEngine/Export.cpp b/core/libs/prx/libSceEsvmEngine/Export.cpp index d81f4a5e32..6667c6812b 100644 --- a/core/libs/prx/libSceEsvmEngine/Export.cpp +++ b/core/libs/prx/libSceEsvmEngine/Export.cpp @@ -1,10 +1,103 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceEsvmEngineDestroyMediaKeySystemAccess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionGenerateRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionGetExpiration() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionGetKeyStatuses() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionGetSessionId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionLoad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionRemove() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionSetEventHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySessionUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySystemAccessCreateMediaKeys() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySystemAccessDestroyMediaKeys() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySystemAccessGetConfiguration() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeySystemAccessSetCdmStorageConfig() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeysCreateSession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeysDestroySession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeysSetEventHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineMediaKeysSetServerCertificate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceEsvmEngineRequestMediaKeySystemAccess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceFios2/Export.cpp b/core/libs/prx/libSceFios2/Export.cpp index d81f4a5e32..eeedc28190 100644 --- a/core/libs/prx/libSceFios2/Export.cpp +++ b/core/libs/prx/libSceFios2/Export.cpp @@ -1,10 +1,848 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceFiosArchiveGetDecompressorThreadCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveGetMountBufferSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveGetMountBufferSizeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveMount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveMountSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveMountWithOrder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveMountWithOrderSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveSetDecompressorThreadCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveUnmount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosArchiveUnmountSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCacheContainsFileRangeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCacheContainsFileSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCacheFlushFileRangeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCacheFlushFileSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCacheFlushSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFHRange() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFHRangeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFHSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFileRange() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFileRangeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCachePrefetchFileSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCancelAllOps() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosClearTimeStamps() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosCloseAllFiles() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHCloseSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHGetPath() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHOpenSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHRead() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDHReadSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDLLInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDLLTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDateFromComponents() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDateGetCurrent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDateToComponents() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDeallocatePassthruFH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDebugDumpDH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDebugDumpDate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDebugDumpError() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDebugDumpFH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDebugDumpOp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDelete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDeleteSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryCreateSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryCreateWithMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryCreateWithModeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryDelete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryDeleteSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryExists() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosDirectoryExistsSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosExists() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosExistsSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHCloseSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHGetOpenParams() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHGetPath() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHGetSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHOpenSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHOpenWithMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHOpenWithModeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPreadSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPreadv() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPreadvSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPwrite() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPwriteSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPwritev() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHPwritevSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHRead() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHReadSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHReadv() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHReadvSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHSeek() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHStat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHStatSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHSyncSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHTell() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHToFileno() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHTruncate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHTruncateSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHWrite() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHWriteSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHWritev() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFHWritevSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileDelete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileDeleteSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileExists() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileExistsSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileGetSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileGetSizeSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileRead() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileReadSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileTruncate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileTruncateSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileWrite() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFileWriteSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosFilenoToFH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetAllDHs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetAllFHs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetAllOps() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetDefaultOpAttr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetGlobalDefaultOpAttr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetSuspendCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosGetThreadDefaultOpAttr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIOFilterAdd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIOFilterCache() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIOFilterGetInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIOFilterPsarcDearchiver() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIOFilterRemove() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIsIdle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIsInitialized() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIsSuspended() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosIsValidHandle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpCancel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpDelete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetActualCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetAttr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetError() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetOffset() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetPath() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpGetRequestCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpIsCancelled() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpIsDone() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpReschedule() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpRescheduleWithPriority() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpSetBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpSyncWait() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpSyncWaitForIO() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpWait() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOpWaitUntil() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOverlayAdd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOverlayGetInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOverlayGetList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOverlayModify() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOverlayRemove() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosOverlayResolveSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosPathNormalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosPathcmp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosPathncmp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosPrintTimeStamps() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosPrintf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosRename() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosRenameSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosResolve() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosResolveSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosResume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosSaveTimeStamp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosSetGlobalDefaultOpAttr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosSetThreadDefaultOpAttr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosShutdownAndCancelOps() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosStat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosStatSync() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosSuspend() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosTimeGetCurrent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosTimeIntervalFromNanoseconds() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosTimeIntervalToNanoseconds() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosTraceTimestamp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosUpdateParameters() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFiosVprintf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceFsInternalForVsh/Export.cpp b/core/libs/prx/libSceFsInternalForVsh/Export.cpp index d81f4a5e32..4a393cfbb7 100644 --- a/core/libs/prx/libSceFsInternalForVsh/Export.cpp +++ b/core/libs/prx/libSceFsInternalForVsh/Export.cpp @@ -1,10 +1,888 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceBdSchedCancelBackgroundCopyRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedCancelPrioritizedBackgroundCopyRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedConfigure() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedConfigure2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedDeconfigure() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedGetBackgroundCopyRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedGetExtentMap() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedGetPrioritizedBackgroundCopyRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedGetState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedSetBackgroundCopyRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedSetDiscIdentity() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedSetPrioritizedBackgroundCopyRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedStartGame() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedStartRipping() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBdSchedStopScheduling() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsBfsCheckCleanFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsBindApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsBindPackages() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsCheckFormat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsCheckSingleUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsCreateExfatPartition() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsCreatePfsSaveDataImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsCreatePfsTrophyDataImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsCreatePprPfsTrophyDataImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDeviceAlignedPread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDeviceAlignedPwrite() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsA53IocGetBuildMetadataStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsCancelWaitEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsCreateForVSH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsDeleteForVSH() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsDeregisterEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsEventComplete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsEventResponseComplete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsFetchMetaEntry() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsFsck() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsInitFetchMetaEntryCtx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsMountForFSMP() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsRegisterEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDevpfsWaitEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDrfsRegisterSocket() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsDrfsResumeSocket() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsErriCtl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsErriGetState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExfatMkfsExternalHdd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExfatfsMkfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExtUSBSchedDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExtUSBSchedInsert() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExtUSBSchedInsertForHDD() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExternalStorageDeconfigure() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExternalStorageGetRawDevice() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExternalStorageInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsExternalStorageVerifyMetadata() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGetAttribute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGetClusterStatistics() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGetDeviceSectorsize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGetFsMetadata() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGetFsMetadataWithProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGetSmrInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsGschedCtl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedConfigCurrentThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedConfigDescriptor() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedConfigThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedMapStat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedResultToPriority() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedResultToSlot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedStatCurrentThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedStatDescriptor() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedStatThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsISSchedUnmapStat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitCreatePfsSaveDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitCreatePfsTrophyDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountDownloadDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountGamePkgOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountLwfsCompatOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountLwfsOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountPprPkgOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountSaveDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitMountTrophyDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountDownloadDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountGamePkgOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountLwfsCompatOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountLwfsOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountPprPkgOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountSaveDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitUmountTrophyDataOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsInitWorkspaceFileMetadataParam() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdAttach() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdAttach2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdAttachPartiallyCryptedDownloadData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdAttachSingleDefaultImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdAttachSingleImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdDetach() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdDisableLayer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdEnableLayer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdSetDiscIdentity() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsLvdWaitResumed() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMlfsMount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMlfsMountWithFlags() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMlfsUmount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountDownloadData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountGamePkg() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountLwfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountLwfsCompat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountPartiallyCryptedDownloadData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountPprPkg() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountSaveData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsMountTrophyData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsNsfsAddOverlayToNamespace() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsNsfsMountWithFlags() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsNsfsSetGlobalExclude() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsNsfsUmount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsPartitionTrim() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsReserveCluster() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsSetAttribute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsSetClusterCacheSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsSetFileReservedTotal() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsSetSystemReserveSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsSetWorkspaceFileHash() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsStatWorkspace() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsSysStorageGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsTransactionCancel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsTransactionSaveDataInitMountOpt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsTransactionSaveDataMount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsTransactionStart() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsTrophyImageError() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocateAdditionalContents() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocateAligned() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocateDownloadData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocateGameImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocatePatchImage() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocateSaveData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsAllocateTrophyData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsCheckCleanFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsCheckFixedCylinderGroupSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsCollectTelemetry() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsCollectTelemetry2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsDirectWrite() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsEstimateZoneCapacityForUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsExtendFile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsFsck() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsFsckWithMM() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsGetExtentMap() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsGrowLwfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsGrowfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsHideBlocksFromUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsInitalizeSparseFile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsLargewriteFsck() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsLargewriteFsckWithMM() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsMkLwfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsMkfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsMkfsForExternalHDD() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsMkfsForSystem() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsMkfsWithFixedCylinderGroupSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsReallocateSparseFile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsRecryptBlocks() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsRecryptGetState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsRecryptIsUnfinished() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsRecryptSyncMetadata() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsSeekHoleInFile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUfsStopOperation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountDownloadData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountGamePkg() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountLwfs() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountLwfsCompat() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountSaveData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountTrophyData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUmountWorkspace() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUnbindApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsUnbindPackages() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsDelete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsFetchFileMetadata() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsFsck() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsMount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceFsWsUmount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceGic/Export.cpp b/core/libs/prx/libSceGic/Export.cpp index d81f4a5e32..6fff8198ce 100644 --- a/core/libs/prx/libSceGic/Export.cpp +++ b/core/libs/prx/libSceGic/Export.cpp @@ -1,10 +1,13 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceGicGetValue() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceHmdSetupDialog/Export.cpp b/core/libs/prx/libSceHmdSetupDialog/Export.cpp index fb34206c21..6fb905167d 100644 --- a/core/libs/prx/libSceHmdSetupDialog/Export.cpp +++ b/core/libs/prx/libSceHmdSetupDialog/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceHmdSetupDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHmdSetupDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHmdSetupDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHmdSetupDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHmdSetupDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHmdSetupDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHmdSetupDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceHubAppUtil/Export.cpp b/core/libs/prx/libSceHubAppUtil/Export.cpp index d81f4a5e32..b4a5ae963d 100644 --- a/core/libs/prx/libSceHubAppUtil/Export.cpp +++ b/core/libs/prx/libSceHubAppUtil/Export.cpp @@ -1,10 +1,23 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceHubAppUtilDeeplinkToLiveDetailView() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHubAppUtilDeeplinkToPSSubscriptions() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceHubAppUtilGetInstallationStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceIdu/Export.cpp b/core/libs/prx/libSceIdu/Export.cpp index d81f4a5e32..def1b88f9a 100644 --- a/core/libs/prx/libSceIdu/Export.cpp +++ b/core/libs/prx/libSceIdu/Export.cpp @@ -1,10 +1,48 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceIduGetEnv() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduInstallPkg() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduIsAppInstalled() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduIsRifInstalled() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduIsStaffMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduReboot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceIduTerm() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceLoginMgrServer/Export.cpp b/core/libs/prx/libSceLoginMgrServer/Export.cpp index fb34206c21..c0497cf3de 100644 --- a/core/libs/prx/libSceLoginMgrServer/Export.cpp +++ b/core/libs/prx/libSceLoginMgrServer/Export.cpp @@ -5,6 +5,154 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceLoginMgrServerCheckSafetyNoticeShown() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerClearMorpheusRequiredUserId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerConvertToUserId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerCreateUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerDestroyUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerDialogGetAppId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerDialogGetOpenParam() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerDialogNotifyCloseFinished() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerDialogNotifyOpenFinished() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerDialogSetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerGetLogoutInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerGetMorpheusRequiredUserId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerGetSharePlayAllowPadOperation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerInitializeSharePlayAllowPadOperation() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerIsRequestedCdlgClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerLoginServiceGetRequestParam() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerLoginServiceNotifyRequestFinished() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerNotifyEasySignInFinished() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerNotifyEasySignInUserCode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerNotifyHmdConnectInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerNotifySafetyNoticeShown() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerNotifyShellUIState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerSetLoginFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerSetMorpheusAssignEnabledFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerSetMorpheusRequiredUserId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerSetSharePlayMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerSetUserStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLoginMgrServerUpdateUserIdRalatedToPadUniqueId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceMusicFrameworkInterface/Export.cpp b/core/libs/prx/libSceMusicFrameworkInterface/Export.cpp index d81f4a5e32..dc0e9adc97 100644 --- a/core/libs/prx/libSceMusicFrameworkInterface/Export.cpp +++ b/core/libs/prx/libSceMusicFrameworkInterface/Export.cpp @@ -1,10 +1,93 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceMusicFwGetCurrentTime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwGetLoop() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwGetMuted() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwGetNetworkState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwGetVolume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwIsEnded() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwIsPlaying() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwIsStopped() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwRemoveSrc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSendMessageAsynchronously() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSendMessageSynchronously() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSetCurrentTime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSetLoop() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSetShuffle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSetVolume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSkipNext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceMusicFwSkipPrevious() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpEulaDialog/Export.cpp b/core/libs/prx/libSceNpEulaDialog/Export.cpp index fb34206c21..1b666cc199 100644 --- a/core/libs/prx/libSceNpEulaDialog/Export.cpp +++ b/core/libs/prx/libSceNpEulaDialog/Export.cpp @@ -5,6 +5,49 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpEulaDialogCheckVersion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogCheckVersionAbort() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpEulaDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpFriendListDialog/Export.cpp b/core/libs/prx/libSceNpFriendListDialog/Export.cpp index fb34206c21..e744f30761 100644 --- a/core/libs/prx/libSceNpFriendListDialog/Export.cpp +++ b/core/libs/prx/libSceNpFriendListDialog/Export.cpp @@ -5,6 +5,49 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpFriendListDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogGetResultA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogOpenA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpFriendListDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpProfileDialog/Export.cpp b/core/libs/prx/libSceNpProfileDialog/Export.cpp index fb34206c21..b0dd450371 100644 --- a/core/libs/prx/libSceNpProfileDialog/Export.cpp +++ b/core/libs/prx/libSceNpProfileDialog/Export.cpp @@ -5,6 +5,44 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpProfileDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogOpenA() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpProfileDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpRemotePlaySessionSignaling/Export.cpp b/core/libs/prx/libSceNpRemotePlaySessionSignaling/Export.cpp index d81f4a5e32..766da74191 100644 --- a/core/libs/prx/libSceNpRemotePlaySessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpRemotePlaySessionSignaling/Export.cpp @@ -1,10 +1,58 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpRemotePlaySessionSignalingCreateJoinSession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingGetConnectionStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingGetCustomProperty() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingGetLocalNetInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingGetMemoryInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingJoinSession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingLeaveSession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingSetCustomProperty() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpRemotePlaySessionSignalingTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpSns/Export.cpp b/core/libs/prx/libSceNpSns/Export.cpp index fb34206c21..c6647c709c 100644 --- a/core/libs/prx/libSceNpSns/Export.cpp +++ b/core/libs/prx/libSceNpSns/Export.cpp @@ -5,6 +5,109 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpSnsFacebookAbortRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntAbortRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntCreateRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntDeleteRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntFbGetGameAccessToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntFbGetGameAccessTokenAllowed() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntFbGetSystemAccessToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntFbGetTitleInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntLinkedStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntTwGetSystemAccessToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntUnlink() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntYtGetAccessToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsIntYtRefreshMasterToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsTwitchAbortRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsTwitchCreateRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsTwitchDeleteRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsTwitchGetAccessToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeAbortRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeCreateRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDeleteRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeGetAccessToken() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpSnsYouTubeDialog/Export.cpp b/core/libs/prx/libSceNpSnsYouTubeDialog/Export.cpp index fb34206c21..e1b07d4bfc 100644 --- a/core/libs/prx/libSceNpSnsYouTubeDialog/Export.cpp +++ b/core/libs/prx/libSceNpSnsYouTubeDialog/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpSnsYouTubeDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpSnsYouTubeDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceNpTcs/Export.cpp b/core/libs/prx/libSceNpTcs/Export.cpp index d81f4a5e32..483b8fa844 100644 --- a/core/libs/prx/libSceNpTcs/Export.cpp +++ b/core/libs/prx/libSceNpTcs/Export.cpp @@ -1,10 +1,23 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceNpTcsIntDownloadData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpTcsIntUploadDataToNewSlotAbort() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceNpTcsIntUploadDataToNewSlotBegin() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePadTracker/Export.cpp b/core/libs/prx/libScePadTracker/Export.cpp index fb34206c21..e43ebed776 100644 --- a/core/libs/prx/libScePadTracker/Export.cpp +++ b/core/libs/prx/libScePadTracker/Export.cpp @@ -5,6 +5,34 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI scePadTrackerCalibrate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePadTrackerGetWorkingMemorySize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePadTrackerInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePadTrackerReadState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePadTrackerTerm() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePadTrackerUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePatchCheckerClient/Export.cpp b/core/libs/prx/libScePatchCheckerClient/Export.cpp index d81f4a5e32..3cce60d2e7 100644 --- a/core/libs/prx/libScePatchCheckerClient/Export.cpp +++ b/core/libs/prx/libScePatchCheckerClient/Export.cpp @@ -1,10 +1,68 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI scePatchCheckerCancel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerCheckPatch() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerCreateHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerDestroyHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerDisableAutoDownload() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerEnableAutoDownload() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerGetApplicableTick() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerGetPackageInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerRequestCheckPatch() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerRequestCheckPatchByType() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePatchCheckerTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePlayReady/Export.cpp b/core/libs/prx/libScePlayReady/Export.cpp index fb34206c21..59330d9d35 100644 --- a/core/libs/prx/libScePlayReady/Export.cpp +++ b/core/libs/prx/libScePlayReady/Export.cpp @@ -5,6 +5,254 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceBufferAlloc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBufferDup() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBufferFree() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBufferInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceBufferRealloc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyApiInitializeInitParams() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyApiIsStarted() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyApiStart() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyApiStop() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyApiVersion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyApiVersionString() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyBufferFree() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyBufferInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyBufferMalloc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyBufferRealloc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyContentGetProperty() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyContentSetProperty() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainCertEnumFini() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainCertEnumInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainCertEnumNext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainCertFind() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainJoinGenerateChallenge() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainJoinProcessResponse() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainLeaveGenerateChallenge() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDomainLeaveProcessResponse() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyEnvelopeClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyEnvelopeGetSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyEnvelopeInitializeRead() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyEnvelopeOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyEnvelopeRead() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyEnvelopeSeek() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyFinalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyFreeDecryptContext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyInitializeInitParams() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseAcqGenerateAck() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseAcqGenerateChallenge() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseAcqGetAdditionalResponseData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseAcqProcessAckResponse() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseAcqProcessResponse() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyMemAlloc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyMemFree() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyMemGetStats() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyMemRealloc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyNewDecryptContext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderBind() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderCommit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderDecrypt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReset() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyStoreCleanup() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePlayReady2/Export.cpp b/core/libs/prx/libScePlayReady2/Export.cpp index fb34206c21..2b08a30037 100644 --- a/core/libs/prx/libScePlayReady2/Export.cpp +++ b/core/libs/prx/libScePlayReady2/Export.cpp @@ -5,6 +5,84 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI scePlayReadyBufferInitWithData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCloneDecryptContext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDebugPrintf() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyDebugSetLevel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseDelete() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseDeleteInMemory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseGetExpirationTime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseGetProperty() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderDecryptMultiple() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderDecryptMultipleOpaque() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderDecryptOpaque() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderDecrypt_Legacy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderFreeOpaqueContent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReaderRetrieveOpaqueContent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyReinitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePlayReady4/Export.cpp b/core/libs/prx/libScePlayReady4/Export.cpp index d81f4a5e32..0454139571 100644 --- a/core/libs/prx/libScePlayReady4/Export.cpp +++ b/core/libs/prx/libScePlayReady4/Export.cpp @@ -1,10 +1,183 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI scePlayReadyApiPlatform() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiCreateMediaKeySession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiCreateMediaKeySystemAccess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiCreateMediaKeys() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiDecryptMultipleOpaque() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiDecryptOpaque() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiDecryptOpaque2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiDestroyMediaKeySession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiDestroyMediaKeySystemAccess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiDestroyMediaKeys() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiFreeOpaqueDecryptedContent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGenerateRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetConfiguration() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetExpiration() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetKeySystem() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetMediaKeyCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetMediaKeyStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetSessionAppContext() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiGetSessionId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiIsLicenseAckRequired() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiLoad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiRemove() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiRetrieveOpaqueDecryptedContent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiSetServerCertificate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiSetSessionCallbacks() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyCdmiUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyLicenseCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfileClear() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfileEnter() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfileLeave() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfilePause() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfileResume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfileStart() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePlayReadyProfileStop() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePs2EmuMenuDialog/Export.cpp b/core/libs/prx/libScePs2EmuMenuDialog/Export.cpp index fb34206c21..99db1f489a 100644 --- a/core/libs/prx/libScePs2EmuMenuDialog/Export.cpp +++ b/core/libs/prx/libScePs2EmuMenuDialog/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI scePs2EmuMenuDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePs2EmuMenuDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePs2EmuMenuDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePs2EmuMenuDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePs2EmuMenuDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePs2EmuMenuDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePs2EmuMenuDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libScePsmUtil/Export.cpp b/core/libs/prx/libScePsmUtil/Export.cpp index d81f4a5e32..0a7d7939be 100644 --- a/core/libs/prx/libScePsmUtil/Export.cpp +++ b/core/libs/prx/libScePsmUtil/Export.cpp @@ -1,10 +1,28 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI scePsmUtilFinalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePsmUtilGetPsmIniFile() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePsmUtilInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI scePsmUtilRegisterCallbacks() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceShareFactoryUtil/Export.cpp b/core/libs/prx/libSceShareFactoryUtil/Export.cpp index d81f4a5e32..ca9e3680ad 100644 --- a/core/libs/prx/libSceShareFactoryUtil/Export.cpp +++ b/core/libs/prx/libSceShareFactoryUtil/Export.cpp @@ -1,10 +1,38 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceShareFactoryUtilBackToGameFromShareFactory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceShareFactoryUtilGetLaunchStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceShareFactoryUtilGetLaunchStatusOfShareFactory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceShareFactoryUtilInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceShareFactoryUtilLaunchShareFactory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceShareFactoryUtilTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSocialScreen/Export.cpp b/core/libs/prx/libSceSocialScreen/Export.cpp index fb34206c21..ebaf31823a 100644 --- a/core/libs/prx/libSceSocialScreen/Export.cpp +++ b/core/libs/prx/libSceSocialScreen/Export.cpp @@ -5,6 +5,59 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSocialScreenCloseSeparateMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenConfigureSeparateMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenCrashDaemon() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenGetIgnoreSeparateMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenInitializeSeparateModeParameter() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenOpenSeparateMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenReserveHighFrameRateVideoCaptureRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenSetIgnoreSeparateMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenSetMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSocialScreenDialog/Export.cpp b/core/libs/prx/libSceSocialScreenDialog/Export.cpp index fb34206c21..17987cec00 100644 --- a/core/libs/prx/libSceSocialScreenDialog/Export.cpp +++ b/core/libs/prx/libSceSocialScreenDialog/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSocialScreenDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSocialScreenDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSpSysCallWrapper/Export.cpp b/core/libs/prx/libSceSpSysCallWrapper/Export.cpp index fb34206c21..e15c0a9054 100644 --- a/core/libs/prx/libSceSpSysCallWrapper/Export.cpp +++ b/core/libs/prx/libSceSpSysCallWrapper/Export.cpp @@ -5,6 +5,244 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSpGetPid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpKernelGettimeofday() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpKernelNanosleep() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetConnect() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetEpollAbort() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetEpollControl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetEpollCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetEpollDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetEpollWait() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetErrnoLoc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetPoolCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetPoolDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetRecv() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetResolverAbort() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetResolverCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetResolverDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetResolverGetError() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetResolverStartNtoa() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetSend() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetSetsockopt() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetSocket() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpNetSocketClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadAttrDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadAttrInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadAttrSetinheritsched() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadAttrSetschedparam() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadAttrSetstacksize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCondDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCondInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCondSignal() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCondWait() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCondattrDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCondattrInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadCreate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadDetach() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadExit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadJoin() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexLock() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexUnlock() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexattrDestroy() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexattrInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexattrSetprioceiling() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexattrSetprotocol() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadMutexattrSettype() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpPthreadSetprio() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSpSelect() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSrcUtl/Export.cpp b/core/libs/prx/libSceSrcUtl/Export.cpp index d81f4a5e32..fac2eada51 100644 --- a/core/libs/prx/libSceSrcUtl/Export.cpp +++ b/core/libs/prx/libSceSrcUtl/Export.cpp @@ -1,10 +1,53 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSrcUtilityFinalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityFlush() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityGetDelaySamples() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityGetHandleSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityGetPriority() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityInitializeEx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilityResample() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSrcUtilitySetPriority() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSystemLogger/Export.cpp b/core/libs/prx/libSceSystemLogger/Export.cpp index fb34206c21..0b82cdf1e0 100644 --- a/core/libs/prx/libSceSystemLogger/Export.cpp +++ b/core/libs/prx/libSceSystemLogger/Export.cpp @@ -5,6 +5,19 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSystemLoggerInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLoggerSetLogData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLoggerTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSystemLogger2Delivery/Export.cpp b/core/libs/prx/libSceSystemLogger2Delivery/Export.cpp index d81f4a5e32..a071ef1bed 100644 --- a/core/libs/prx/libSceSystemLogger2Delivery/Export.cpp +++ b/core/libs/prx/libSceSystemLogger2Delivery/Export.cpp @@ -1,10 +1,28 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSystemLogger2DeliveryGetBatchResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2DeliveryGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2DeliverySetBatchDeliveryEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2DeliveryTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSystemLogger2NativeQueueClient/Export.cpp b/core/libs/prx/libSceSystemLogger2NativeQueueClient/Export.cpp index d81f4a5e32..1087fa235d 100644 --- a/core/libs/prx/libSceSystemLogger2NativeQueueClient/Export.cpp +++ b/core/libs/prx/libSceSystemLogger2NativeQueueClient/Export.cpp @@ -1,10 +1,33 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSystemLogger2NativeQueueClientCheckAndMaskData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2NativeQueueClientGetEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2NativeQueueClientSetDeliveryEnable() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2NativeQueueClientSetDeliveryResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemLogger2NativeQueueClientSetNativeQueueConfig() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceSystemTts/Export.cpp b/core/libs/prx/libSceSystemTts/Export.cpp index d81f4a5e32..d895b270e8 100644 --- a/core/libs/prx/libSceSystemTts/Export.cpp +++ b/core/libs/prx/libSceSystemTts/Export.cpp @@ -1,10 +1,33 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSystemTtsCancel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemTtsIsAccessibilityAvailable() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemTtsRegisterCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemTtsSpeakRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemTtsUnregisterCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceTextToSpeech/Export.cpp b/core/libs/prx/libSceTextToSpeech/Export.cpp index fb34206c21..05c84e9df9 100644 --- a/core/libs/prx/libSceTextToSpeech/Export.cpp +++ b/core/libs/prx/libSceTextToSpeech/Export.cpp @@ -5,6 +5,54 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceTextToSpeechCancelImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechCloseImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechGetStatusImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechInitImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechOpenImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechPauseImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechResumeImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechSpeakContinuousImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechSpeakImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceTextToSpeechTermImpl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceUpdateService/Export.cpp b/core/libs/prx/libSceUpdateService/Export.cpp index d81f4a5e32..7fc0488786 100644 --- a/core/libs/prx/libSceUpdateService/Export.cpp +++ b/core/libs/prx/libSceUpdateService/Export.cpp @@ -1,10 +1,208 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceUpsrvGetCompletedTaskId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvGetRebootFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvGetSystemExUpdateTaskState() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvPostErrorLog() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvResumePausedTask() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvSetFakeVersion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvStartUpdateSession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvSwitchSystemExBank() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvSwitchSystemExBankWithoutVerify() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCancelBDUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckCancel() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckCreateHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckDestroyHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckDoCheckSystem() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckDoCheckSystemForDevKit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckDoCheckSystemForSettings() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCheckDoCheckSystemWithPupInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateCreateUpdateTask() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateDestroyUpdateTask() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateDoCheckBootUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateDoUpdateSystemEx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateDoUpdateWithEntitlement() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateDoUpdateWithPupInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateDoUpdateWithPupPath2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetBetaAgreementUrl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetEulaDisplay() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetEulaStr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetEulaStrLen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetFeatureListUrl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetFeatureListUrlForSystemExUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetFeatureListUrlForUpdate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetImageWritingProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetProgress() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetUpdateTaskInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateGetUpdateTaskMgrInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateSetEulaAccept() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvUpdateStartUpdateTask() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUpsrvVerifySystemExBank() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceUrlConfigResolver/Export.cpp b/core/libs/prx/libSceUrlConfigResolver/Export.cpp index 9d7da335dc..60d243d931 100644 --- a/core/libs/prx/libSceUrlConfigResolver/Export.cpp +++ b/core/libs/prx/libSceUrlConfigResolver/Export.cpp @@ -1,11 +1,29 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceUrlConfigResolverGetAppConfig() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUrlConfigResolverGetDefaultQueryParameter() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUrlConfigResolverGetDeviceId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceUrlConfigResolverGetJscHeapSizeSoftLimit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} int APS5_VABI sceUrlConfigResolverGetReleaseVersion() { diff --git a/core/libs/prx/libSceVdecCore.native/Export.cpp b/core/libs/prx/libSceVdecCore.native/Export.cpp index fb34206c21..f619cb820b 100644 --- a/core/libs/prx/libSceVdecCore.native/Export.cpp +++ b/core/libs/prx/libSceVdecCore.native/Export.cpp @@ -5,6 +5,24 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVdecCoreCreateDecoderBid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecCoreCreateDecoderSvp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecCoreMapMemory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecCoreSetDecodeInputSvp() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVdecSavc/Export.cpp b/core/libs/prx/libSceVdecSavc/Export.cpp index fb34206c21..c30e4f9c37 100644 --- a/core/libs/prx/libSceVdecSavc/Export.cpp +++ b/core/libs/prx/libSceVdecSavc/Export.cpp @@ -5,6 +5,24 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSdecCreateSw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecDestroySw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecGetVersionSw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecQueryMemorySw() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVdecSavc2/Export.cpp b/core/libs/prx/libSceVdecSavc2/Export.cpp index fb34206c21..6f217fc959 100644 --- a/core/libs/prx/libSceVdecSavc2/Export.cpp +++ b/core/libs/prx/libSceVdecSavc2/Export.cpp @@ -5,6 +5,24 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSdecCreateSw2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecDestroySw2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecGetVersionSw2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecQueryMemorySw2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVdecShevc/Export.cpp b/core/libs/prx/libSceVdecShevc/Export.cpp index fb34206c21..8f63aa61a9 100644 --- a/core/libs/prx/libSceVdecShevc/Export.cpp +++ b/core/libs/prx/libSceVdecShevc/Export.cpp @@ -5,6 +5,39 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceSdecCreateSwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecDestroySwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecExecuteSwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecGetFrameSwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecGetVersionSwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecQueryMemorySwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSdecResetSwHevc() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVdecwrap/Export.cpp b/core/libs/prx/libSceVdecwrap/Export.cpp index fb34206c21..ae14644219 100644 --- a/core/libs/prx/libSceVdecwrap/Export.cpp +++ b/core/libs/prx/libSceVdecwrap/Export.cpp @@ -5,6 +5,59 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVdecwrapCreateDecoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapDeleteDecoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapFlushDecodeOutput() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapGetDecodeOutput() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapMapDirectMemory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapMapMemory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapQueryDecoderMemoryInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapQueryFrameBufferInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapResetDecoder() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapSetDecodeInput() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVdecwrapSyncDecode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVideoArbitration/Export.cpp b/core/libs/prx/libSceVideoArbitration/Export.cpp index d81f4a5e32..2825147db8 100644 --- a/core/libs/prx/libSceVideoArbitration/Export.cpp +++ b/core/libs/prx/libSceVideoArbitration/Export.cpp @@ -1,10 +1,23 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVideoArbitrationAcceptEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoArbitrationEnable() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoArbitrationInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVideoCoreInterface/Export.cpp b/core/libs/prx/libSceVideoCoreInterface/Export.cpp index e4fd254e95..69f5eb3f02 100644 --- a/core/libs/prx/libSceVideoCoreInterface/Export.cpp +++ b/core/libs/prx/libSceVideoCoreInterface/Export.cpp @@ -1,11 +1,14 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVideoCoreIfSetFunctionTableUnlimited() { + NotImplemented_nid_no_patch(__func__); + return 0; +} int APS5_VABI sceVideoCoreIfInitializeInterface() { diff --git a/core/libs/prx/libSceVideoCoreServerInterface/Export.cpp b/core/libs/prx/libSceVideoCoreServerInterface/Export.cpp index d81f4a5e32..37c16803eb 100644 --- a/core/libs/prx/libSceVideoCoreServerInterface/Export.cpp +++ b/core/libs/prx/libSceVideoCoreServerInterface/Export.cpp @@ -1,10 +1,243 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVideoCoreAbortRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreAcquireDecoderResource() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreAcquireDecoderResourceForBeWrapper() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreAddStream() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreAsyncAddStream() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreAsyncSendRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreCancelThumbnail() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreChangeActiveStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreChangeAudioStream() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreChangeDisplaySize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreChangeHdcpSetting() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreChangeSubtitle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreDeleteStream() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGenerateThumbnail() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGenerateThumbnail2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGetCanvasHandle() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGetPlayStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGetPlayStatus2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGetStreamUid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGetThumbnail() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreGetThumbnail2() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreLaunchVideoCore() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceAbortSourceBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceAddSourceBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceAppendData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceEndOfStream() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceGetBufferedTimeRanges() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceGetInputBufferEmptySize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceGetPlaybackQuality() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceGetSourceBufferEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceRemoveSourceBuffer() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceSetDuration() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreMediaSourceSetTimestampOffset() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCorePlayControl() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreRegistEventCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreReleaseDecoderResource() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreReleaseDecoderResourceForBeWrapper() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetAudioMute() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetAudioVolume() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetFullScreen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetInitialInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetInitializeInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetPlaybackRange() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSetThumbnailInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSyncAddStream() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreSyncSendRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoCoreUnregistEventCallback() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVideoDecoderArbitration/Export.cpp b/core/libs/prx/libSceVideoDecoderArbitration/Export.cpp index d81f4a5e32..4e2ab88329 100644 --- a/core/libs/prx/libSceVideoDecoderArbitration/Export.cpp +++ b/core/libs/prx/libSceVideoDecoderArbitration/Export.cpp @@ -1,10 +1,28 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVideoDecoderArbitrationAcceptEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoDecoderArbitrationEnable() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoDecoderArbitrationEnableSuspendMode() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoDecoderArbitrationInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVideoNativeExtEssential/Export.cpp b/core/libs/prx/libSceVideoNativeExtEssential/Export.cpp index fb34206c21..2c3047328f 100644 --- a/core/libs/prx/libSceVideoNativeExtEssential/Export.cpp +++ b/core/libs/prx/libSceVideoNativeExtEssential/Export.cpp @@ -5,6 +5,29 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVideoNativeExtEssential_CreateDecInstance() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoNativeExtEssential_DestroyDecInstance() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoNativeExtEssential_Finalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoNativeExtEssential_Initialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoNativeExtEssential_IsInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVideoStreamingEngine/Export.cpp b/core/libs/prx/libSceVideoStreamingEngine/Export.cpp index d81f4a5e32..1a09e3b9df 100644 --- a/core/libs/prx/libSceVideoStreamingEngine/Export.cpp +++ b/core/libs/prx/libSceVideoStreamingEngine/Export.cpp @@ -1,10 +1,68 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVideoStreamingEngineDestroyMediaKeySystemAccess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySessionGenerateRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySessionGetExpiration() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySessionGetKeyStatuses() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySessionGetSessionId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySessionLoad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySessionSetEventHandler() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySystemAccessCreateMediaKeys() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySystemAccessDestroyMediaKeys() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySystemAccessGetKeySystem() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeySystemAccessSetCdmStorageConfig() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVideoStreamingEngineMediaKeysCreateSession() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVisionManager/Export.cpp b/core/libs/prx/libSceVisionManager/Export.cpp index fb34206c21..91dd5c2463 100644 --- a/core/libs/prx/libSceVisionManager/Export.cpp +++ b/core/libs/prx/libSceVisionManager/Export.cpp @@ -5,6 +5,284 @@ extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVisionManagerApplyCalibrateHmdResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerCancelRequest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerFinalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGenerateRegisterUserData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGenerateRegisterUserDataFinalize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGenerateRegisterUserDataInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetCalibrateTrackingLedInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetCalibrateTrackingLedNecessity() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetHitCountOfRegisterUserData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetLibraryVersion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetNumOfRegisterUserDataSlot() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetRegisterUserDataSize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetRegisterUserDataVersion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfCalibrateHmd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfCalibrateStereoCamera() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfCalibrateTrackingLed() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfFindFace() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfFindPad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfFindPadCorrespondRegion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfFindUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfFindUserInRegion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfFindUserWithoutIdentify() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetResultOfRegisterUserInRegion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetStoreCameraImgYPtr() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerGetWorkingMemorySize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerLoadSettings() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRegistryClear() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestCalibrateHmd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestCalibrateStereoCamera() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestCalibrateTrackingLed() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestFindFace() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestFindPad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestFindPadCorrespondRegion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestFindUser() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestFindUserInRegion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestFindUserWithoutIdentify() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestHeadDetection() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestInitializeRegisterUserData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestPadTracking() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestRecogUtility() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestRegisterUserInRegion() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerRequestStoreCameraImgY() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateCalibrateHmdResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateCalibrateStereoCameraResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateCalibrateTrackingLedResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateCameraFrame() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateCtrlCore() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateRecog() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateRunningStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerSetCallbackForUpdateStoreFrame() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVisionManagerWaitResponse() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libSceVrServiceDialog/Export.cpp b/core/libs/prx/libSceVrServiceDialog/Export.cpp index d81f4a5e32..355ae01310 100644 --- a/core/libs/prx/libSceVrServiceDialog/Export.cpp +++ b/core/libs/prx/libSceVrServiceDialog/Export.cpp @@ -1,10 +1,43 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceVrServiceDialogClose() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVrServiceDialogGetResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVrServiceDialogGetStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVrServiceDialogInitialize() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVrServiceDialogOpen() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVrServiceDialogTerminate() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceVrServiceDialogUpdateStatus() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/core/libs/prx/libmdbg_syscore/Export.cpp b/core/libs/prx/libmdbg_syscore/Export.cpp index d81f4a5e32..195bb3c119 100644 --- a/core/libs/prx/libmdbg_syscore/Export.cpp +++ b/core/libs/prx/libmdbg_syscore/Export.cpp @@ -1,10 +1,723 @@ -#include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" extern "C" { -APS5_DUMMY_FUN +int APS5_VABI sceDebugAcquireAndUpdateDebugRegister() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugAttachProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCancelCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugClearStepThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCreatePerfScratchDataArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCreatePerfScratchExecutableArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCreateScratchDataArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCreateScratchDataAreaForPrx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCreateScratchExecutableArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugCreateScratchExecutableAreaForPrx() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugDestroyPerfScratchDataArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugDestroyPerfScratchExecutableArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugDestroyScratchDataArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugDestroyScratchExecutableArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugDetachProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetApplicationIdByTitleId() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetApplicationInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetApplicationList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetCrashInfoDetailForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetCrashInfoForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetDLLoadFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetDataListOfUltQueue() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetDebugRegisterStatusMap() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetEventList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetEventListForEQueueFd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetEventSubscriptionList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetEventSubscriptionListForEQueueFd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetFiberInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetFileInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetFileInfoForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetFileList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetFileListForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetGpuInfoArea() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetJobManagerInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetJobManagerSequenceInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetJobManagerSequenceList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetModuleInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetModuleList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetModuleMetaData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetMonoVMInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetMonoVMList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessCoredumpHandlerEventBlob() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessCoredumpHandlerEventInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessCoredumpHandlerResult() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessEventCntlFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessPropertyForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessResourceStatCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessResourceStatData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessTime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetProcessTimeCounter() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSyncExclusiveWaiterList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSyncObjectData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSyncObjectList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSyncWaiterList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSyncWaiterListForEQueueFd() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSystemStatusBlob() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetSystemStatusCount() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetThreadInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetThreadInfoByIdent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetThreadList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetThreadListAsIdent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUlObjectList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltCondvarInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltListOfUltRuntime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltMutexInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltQueueDataResourcePoolInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltQueueInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltRuntimeInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltRwlockInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltSemaphoreInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetUltWaitingQueueResourcePoolInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetVirtualMemoryDetailInfoForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetVirtualMemoryInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetVirtualMemoryInfoForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetWaitingListOfUltCondvar() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetWaitingListOfUltMutex() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetWaitingListOfUltQueue() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetWaitingListOfUltRwlock() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetWaitingListOfUltSemaphore() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugGetWorkerThreadListOfUltRuntime() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugInit() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugInitForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugInitForTest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetBlockTimeInfoList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetBlockedIpcInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetChannelInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetChannelKidList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetChannelWaitingThreadList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetClientInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetClientKidList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetClientKidListByDump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetClientKidListByServerKid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetConnectRequestInfoList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetConnectionInfoList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetConnectionWaitingThreadListByClientKid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetConnectionWaitingThreadListBySessionKid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetDump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetEncryptedInfoAllForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetKidInfoListForCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetServerDispatchInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetServerInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetServerKidList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetServerKidListByDump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetServerWaitingThreadList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetSessionInfo() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetSessionKidList() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetSessionKidListByServerKid() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugIpmiGetTidListByDump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugKillApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugKillProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugNoStopChildProcesses() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugNoStopOnDLLoad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugProcessSpawn() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadEvent() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadEventForTest() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadProcessMemory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadProcessMemoryForSDBGP() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadProcessRegister() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadProcessRegisterForSDBGP() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadProcessResourceStatData() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadThreadRegister() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReadThreadRegisterForSDBGP() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugReleaseDebugRegister() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugResumeApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugResumeProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugResumeThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugSetProcessEventCntlFlag() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugSetStepThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugSpawnApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugStopChildProcesses() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugStopOnDLLoad() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugSuspendApplication() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugSuspendProcess() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugSuspendThread() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugTriggerCoredump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugTriggerCoredumpForSystem() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugTriggerVrCaptureDump() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugWriteProcessMemory() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugWriteProcessRegister() { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceDebugWriteThreadRegister() { + NotImplemented_nid_no_patch(__func__); + return 0; +} } diff --git a/docs/dev/debt/libSceAc3Enc.md b/docs/dev/debt/libSceAc3Enc.md new file mode 100644 index 0000000000..0ce6dc4f58 --- /dev/null +++ b/docs/dev/debt/libSceAc3Enc.md @@ -0,0 +1,5 @@ +# libSceAc3Enc technical debt + +### Unknown function info + +- [sceAc3EncCreateEncoder, sceAc3EncDeleteEncoder, sceAc3EncEncode, sceAc3EncEncodeAsync, sceAc3EncFlush, sceAc3EncIsEncoding and sceAc3EncWaitEncode](../../../core/libs/prx/libSceAc3Enc/Export.cpp) (libSceAc3Enc) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAc3Enc.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceAudioSystem.md b/docs/dev/debt/libSceAudioSystem.md new file mode 100644 index 0000000000..0595a501f9 --- /dev/null +++ b/docs/dev/debt/libSceAudioSystem.md @@ -0,0 +1,5 @@ +# libSceAudioSystem technical debt + +### Unknown function info + +- [sceAudioSystemSystemResume and sceAudioSystemSystemSuspend](../../../core/libs/prx/libSceAudioSystem/Export.cpp) (libSceAudioSystem) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAudioSystem.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceBackupRestoreUtil.md b/docs/dev/debt/libSceBackupRestoreUtil.md new file mode 100644 index 0000000000..925f4c078c --- /dev/null +++ b/docs/dev/debt/libSceBackupRestoreUtil.md @@ -0,0 +1,5 @@ +# libSceBackupRestoreUtil technical debt + +### Unknown function info + +- [sceBackupRestoreUtilBackupData, sceBackupRestoreUtilCancelBackupData, sceBackupRestoreUtilCancelGetRequiredSize, sceBackupRestoreUtilCancelGetTitles, sceBackupRestoreUtilCancelRestoreData, sceBackupRestoreUtilDeleteBackupData, sceBackupRestoreUtilGetBackupDataInfo, sceBackupRestoreUtilGetBackupParam, sceBackupRestoreUtilGetBackupProgress, sceBackupRestoreUtilGetRequiredSize, sceBackupRestoreUtilGetRestoreProgress, sceBackupRestoreUtilGetTitles, sceBackupRestoreUtilInitialize, sceBackupRestoreUtilPrepareBackup, sceBackupRestoreUtilRestoreData and sceBackupRestoreUtilTerminate](../../../core/libs/prx/libSceBackupRestoreUtil/Export.cpp) (libSceBackupRestoreUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceBackupRestoreUtil.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceComposite.md b/docs/dev/debt/libSceComposite.md new file mode 100644 index 0000000000..45ba88b721 --- /dev/null +++ b/docs/dev/debt/libSceComposite.md @@ -0,0 +1,5 @@ +# libSceComposite technical debt + +### Unknown function info + +- [sceCompositorAllocateIndex, sceCompositorCheckCrash, sceCompositorCommandGpuPerfBegin, sceCompositorCommandGpuPerfEnd, sceCompositorCreateIndirectRenderTarget, sceCompositorDeleteIndirectRenderTarget, sceCompositorFlush, sceCompositorFlushWithRepeat, sceCompositorGetAnotherProcessSystemAddress, sceCompositorGetAnotherProcessSystemSize, sceCompositorGetAnotherProcessVideoAddress, sceCompositorGetAnotherProcessVideoSize, sceCompositorGetCanvasHandle, sceCompositorGetClientList, sceCompositorGetDmemOffset, sceCompositorGetDummyAddress, sceCompositorGetDummySize, sceCompositorGetRemainedCommandNum, sceCompositorGetRenderTargetResolution, sceCompositorGetSystemAddress, sceCompositorGetSystemSize, sceCompositorGetVideoAddress, sceCompositorGetVideoSize, sceCompositorHandleProcessEvents, sceCompositorInit, sceCompositorInitWithProcessOrder, sceCompositorInsertThreadTraceMarker, sceCompositorIsDebugCaptureEnabled, sceCompositorIsReady4K, sceCompositorLockCommandBuffer, sceCompositorMakeCanvasHandle, sceCompositorMapAnotherProcess, sceCompositorMemoryPoolDecommit, sceCompositorPrivacyMaskPushArea, sceCompositorQuit, sceCompositorReleaseCommandBuffer, sceCompositorReleaseIndex, sceCompositorReleaseLocalStall, sceCompositorReserveCommandBuffer, sceCompositorResetZoomCommand, sceCompositorSetAgcCompositeCanvasCommand, sceCompositorSetAgcContextCommand, sceCompositorSetCompositeCanvasCommandInC, sceCompositorSetCursorImageAddress, sceCompositorSetDebugPositionCommand, sceCompositorSetEndOfFrameCommand, sceCompositorSetEventCommand, sceCompositorSetFilterType, sceCompositorSetFlipCommand2, sceCompositorSetGameSufaceControlCommand, sceCompositorSetGnmContextCommand, sceCompositorSetIndirectCanvasCommand, sceCompositorSetIndirectCompositionCommand, sceCompositorSetIndirectRenderTargetConfigCommand, sceCompositorSetInvisibleCanvasCommand, sceCompositorSetKeepDisplayBufferCommand, sceCompositorSetLocalStallCommand, sceCompositorSetMemoryCommand, sceCompositorSetPatchCommand, sceCompositorSetPostEventCommand, sceCompositorSetRepeatCommand, sceCompositorSetResolutionCommand, sceCompositorSetScalingLayoutInfo, sceCompositorSetSeparateContextCommand, sceCompositorSetSystemConfigWithMask, sceCompositorSetVideoOutMode, sceCompositorSetVirtualCanvasAgcContextCommand, sceCompositorSetVirtualCanvasCommandFooter, sceCompositorSetVirtualCanvasPatchCommand, sceCompositorSetVrMode, sceCompositorSetZoomCommand, sceCompositorSystemConfig, sceCompositorWaitEndOfRendering, sceCompositorWaitEvent, sceCompositorWaitPostEvent, sceCompsoitorGetGpuClock, sceCompsoitorGetProcessRenderingTime and sceCompsoitorGetRenderingTime](../../../core/libs/prx/libSceComposite/Export.cpp) (libSceComposite) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceComposite.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceCompositeExt.md b/docs/dev/debt/libSceCompositeExt.md new file mode 100644 index 0000000000..0f014d4acb --- /dev/null +++ b/docs/dev/debt/libSceCompositeExt.md @@ -0,0 +1,5 @@ +# libSceCompositeExt technical debt + +### Inventory exports declared elsewhere + +The complete PS5 3.20 export table of this module per the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceCompositeExt.c) (78 names) is already declared under the sibling libSceComposite in this tree; the duplicate-export rule keeps each name in a single library, so this module keeps its `APS5_DUMMY_FUN` skeleton. Imports resolve through the sibling declarations. diff --git a/docs/dev/debt/libSceCustomMusicAudioOut.md b/docs/dev/debt/libSceCustomMusicAudioOut.md new file mode 100644 index 0000000000..15ca7e3fa5 --- /dev/null +++ b/docs/dev/debt/libSceCustomMusicAudioOut.md @@ -0,0 +1,5 @@ +# libSceCustomMusicAudioOut technical debt + +### Unknown function info + +- [sceCustomMusicAudioOutFinalize, sceCustomMusicAudioOutGetAllMusicVolume, sceCustomMusicAudioOutGetMuted, sceCustomMusicAudioOutGetVolume and sceCustomMusicAudioOutSetAllMusicVolume](../../../core/libs/prx/libSceCustomMusicAudioOut/Export.cpp) (libSceCustomMusicAudioOut) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceCustomMusicAudioOut.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceCustomMusicSysCallWrapper.md b/docs/dev/debt/libSceCustomMusicSysCallWrapper.md new file mode 100644 index 0000000000..f7379ba3fc --- /dev/null +++ b/docs/dev/debt/libSceCustomMusicSysCallWrapper.md @@ -0,0 +1,5 @@ +# libSceCustomMusicSysCallWrapper technical debt + +### Unknown function info + +- [ and sceCustomMusicSelect](../../../core/libs/prx/libSceCustomMusicSysCallWrapper/Export.cpp) (libSceCustomMusicSysCallWrapper) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceCustomMusicSysCallWrapper.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceDataTransfer.md b/docs/dev/debt/libSceDataTransfer.md new file mode 100644 index 0000000000..34655c5a6c --- /dev/null +++ b/docs/dev/debt/libSceDataTransfer.md @@ -0,0 +1,5 @@ +# libSceDataTransfer technical debt + +### Unknown function info + +- [sceDataTransferAbortFgTransfer, sceDataTransferAbortRequest, sceDataTransferAbortSearchPS4, sceDataTransferCheckBgTransferRunning, sceDataTransferGetFgTransferProgress, sceDataTransferGetPrepareFgTransferProgress, sceDataTransferInitialize, sceDataTransferRequestGetAppInfoPS4, sceDataTransferRequestGetSavedataInfoPS4, sceDataTransferRequestGetUsersPS4, sceDataTransferRequestLaunchPS4, sceDataTransferRequestPrepareFgTransfer, sceDataTransferRequestRebootAuthPS4, sceDataTransferRequestSearchPS4, sceDataTransferRequestStartFgTransfer, sceDataTransferRequestTransferTimePS4, sceDataTransferTargetAbortBindSavedata, sceDataTransferTargetAbortDeactivate, sceDataTransferTargetAbortEth0, sceDataTransferTargetAbortGetDeviceInfo, sceDataTransferTargetAbortGetDeviceInfoApplication, sceDataTransferTargetAbortGetTitles, sceDataTransferTargetAbortGetUsers, sceDataTransferTargetAbortLaunch, sceDataTransferTargetAbortPrepareTransfer, sceDataTransferTargetAbortPwrReq, sceDataTransferTargetAbortReboot, sceDataTransferTargetAbortSendSsoNew2Old, sceDataTransferTargetAbortSendSsoOld2New, sceDataTransferTargetAbortTransfer, sceDataTransferTargetAbortTransferSpeed, sceDataTransferTargetEventIsAuthReady, sceDataTransferTargetEventIsIPv6Ready, sceDataTransferTargetEventIsPwrReqReady, sceDataTransferTargetGetFailedUsers, sceDataTransferTargetGetRebootData, sceDataTransferTargetGetTransferProgress, sceDataTransferTargetRequestAbortSearch, sceDataTransferTargetRequestActivate, sceDataTransferTargetRequestAuth, sceDataTransferTargetRequestBindSavedata, sceDataTransferTargetRequestComplete, sceDataTransferTargetRequestCreateRebootData, sceDataTransferTargetRequestDeactivate, sceDataTransferTargetRequestEndTransfer, sceDataTransferTargetRequestGetDeviceInfo, sceDataTransferTargetRequestGetDeviceInfoApplication, sceDataTransferTargetRequestGetTitles, sceDataTransferTargetRequestGetUsers, sceDataTransferTargetRequestLaunch, sceDataTransferTargetRequestPrepareTransfer, sceDataTransferTargetRequestPrepareTransferProgress, sceDataTransferTargetRequestPwrReq, sceDataTransferTargetRequestReboot, sceDataTransferTargetRequestSearch, sceDataTransferTargetRequestSendSsoNew2Old, sceDataTransferTargetRequestSendSsoOld2New, sceDataTransferTargetRequestStartTransfer, sceDataTransferTargetRequestTransferSpeed and sceDataTransferTerminate](../../../core/libs/prx/libSceDataTransfer/Export.cpp) (libSceDataTransfer) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceDataTransfer.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceDipsw.md b/docs/dev/debt/libSceDipsw.md new file mode 100644 index 0000000000..cebac2c8b7 --- /dev/null +++ b/docs/dev/debt/libSceDipsw.md @@ -0,0 +1,5 @@ +# libSceDipsw technical debt + +### Unknown function info + +- [sceKernelCheckDipsw, sceKernelInitializeDipsw, sceKernelIsAssistMode, sceKernelIsDevelopmentMode, sceKernelIsDisableRazor, sceKernelReadDipswData, sceKernelSetDipsw, sceKernelUnsetDipsw and sceKernelWriteDipswData](../../../core/libs/prx/libSceDipsw/Export.cpp) (libSceDipsw) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceDipsw.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceDseehx.md b/docs/dev/debt/libSceDseehx.md new file mode 100644 index 0000000000..9bde1c867e --- /dev/null +++ b/docs/dev/debt/libSceDseehx.md @@ -0,0 +1,5 @@ +# libSceDseehx technical debt + +### Unknown function info + +- [sceDseehxConvert, sceDseehxExtInit, sceDseehxFlush and sceDseehxInit](../../../core/libs/prx/libSceDseehx/Export.cpp) (libSceDseehx) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceDseehx.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceDtsEnc.md b/docs/dev/debt/libSceDtsEnc.md new file mode 100644 index 0000000000..c5656e8a64 --- /dev/null +++ b/docs/dev/debt/libSceDtsEnc.md @@ -0,0 +1,5 @@ +# libSceDtsEnc technical debt + +### Unknown function info + +- [sceDtsEncCreateEncoder, sceDtsEncDeleteEncoder, sceDtsEncEncode, sceDtsEncEncodeAsync, sceDtsEncFlush, sceDtsEncIsEncoding and sceDtsEncWaitEncode](../../../core/libs/prx/libSceDtsEnc/Export.cpp) (libSceDtsEnc) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceDtsEnc.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceEditMp4.md b/docs/dev/debt/libSceEditMp4.md new file mode 100644 index 0000000000..9619922e39 --- /dev/null +++ b/docs/dev/debt/libSceEditMp4.md @@ -0,0 +1,5 @@ +# libSceEditMp4 technical debt + +### Unknown function info + +- [sceEditMp4Abort, sceEditMp4Edit, sceEditMp4GetStatus, sceEditMp4Initialize, sceEditMp4InitializeParam, sceEditMp4QueryMemSize, sceEditMp4SetFinishCallback, sceEditMp4Terminate and sceEditMp4Unite](../../../core/libs/prx/libSceEditMp4/Export.cpp) (libSceEditMp4) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceEditMp4.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceEsvmEngine.md b/docs/dev/debt/libSceEsvmEngine.md new file mode 100644 index 0000000000..c7fd387983 --- /dev/null +++ b/docs/dev/debt/libSceEsvmEngine.md @@ -0,0 +1,5 @@ +# libSceEsvmEngine technical debt + +### Unknown function info + +- [sceEsvmEngineDestroyMediaKeySystemAccess, sceEsvmEngineMediaKeySessionClose, sceEsvmEngineMediaKeySessionGenerateRequest, sceEsvmEngineMediaKeySessionGetExpiration, sceEsvmEngineMediaKeySessionGetKeyStatuses, sceEsvmEngineMediaKeySessionGetSessionId, sceEsvmEngineMediaKeySessionLoad, sceEsvmEngineMediaKeySessionRemove, sceEsvmEngineMediaKeySessionSetEventHandler, sceEsvmEngineMediaKeySessionUpdate, sceEsvmEngineMediaKeySystemAccessCreateMediaKeys, sceEsvmEngineMediaKeySystemAccessDestroyMediaKeys, sceEsvmEngineMediaKeySystemAccessGetConfiguration, sceEsvmEngineMediaKeySystemAccessSetCdmStorageConfig, sceEsvmEngineMediaKeysCreateSession, sceEsvmEngineMediaKeysDestroySession, sceEsvmEngineMediaKeysSetEventHandler, sceEsvmEngineMediaKeysSetServerCertificate and sceEsvmEngineRequestMediaKeySystemAccess](../../../core/libs/prx/libSceEsvmEngine/Export.cpp) (libSceEsvmEngine) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceEsvmEngine.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceFios2.md b/docs/dev/debt/libSceFios2.md new file mode 100644 index 0000000000..ac6b0499b6 --- /dev/null +++ b/docs/dev/debt/libSceFios2.md @@ -0,0 +1,5 @@ +# libSceFios2 technical debt + +### Unknown function info + +- [sceFiosArchiveGetDecompressorThreadCount, sceFiosArchiveGetMountBufferSize, sceFiosArchiveGetMountBufferSizeSync, sceFiosArchiveMount, sceFiosArchiveMountSync, sceFiosArchiveMountWithOrder, sceFiosArchiveMountWithOrderSync, sceFiosArchiveSetDecompressorThreadCount, sceFiosArchiveUnmount, sceFiosArchiveUnmountSync, sceFiosCacheContainsFileRangeSync, sceFiosCacheContainsFileSync, sceFiosCacheFlushFileRangeSync, sceFiosCacheFlushFileSync, sceFiosCacheFlushSync, sceFiosCachePrefetchFH, sceFiosCachePrefetchFHRange, sceFiosCachePrefetchFHRangeSync, sceFiosCachePrefetchFHSync, sceFiosCachePrefetchFile, sceFiosCachePrefetchFileRange, sceFiosCachePrefetchFileRangeSync, sceFiosCachePrefetchFileSync, sceFiosCancelAllOps, sceFiosClearTimeStamps, sceFiosCloseAllFiles, sceFiosDHClose, sceFiosDHCloseSync, sceFiosDHGetPath, sceFiosDHOpen, sceFiosDHOpenSync, sceFiosDHRead, sceFiosDHReadSync, sceFiosDLLInitialize, sceFiosDLLTerminate, sceFiosDateFromComponents, sceFiosDateGetCurrent, sceFiosDateToComponents, sceFiosDeallocatePassthruFH, sceFiosDebugDumpDH, sceFiosDebugDumpDate, sceFiosDebugDumpError, sceFiosDebugDumpFH, sceFiosDebugDumpOp, sceFiosDelete, sceFiosDeleteSync, sceFiosDirectoryCreate, sceFiosDirectoryCreateSync, sceFiosDirectoryCreateWithMode, sceFiosDirectoryCreateWithModeSync, sceFiosDirectoryDelete, sceFiosDirectoryDeleteSync, sceFiosDirectoryExists, sceFiosDirectoryExistsSync, sceFiosExists, sceFiosExistsSync, sceFiosFHClose, sceFiosFHCloseSync, sceFiosFHGetOpenParams, sceFiosFHGetPath, sceFiosFHGetSize, sceFiosFHOpen, sceFiosFHOpenSync, sceFiosFHOpenWithMode, sceFiosFHOpenWithModeSync, sceFiosFHPread, sceFiosFHPreadSync, sceFiosFHPreadv, sceFiosFHPreadvSync, sceFiosFHPwrite, sceFiosFHPwriteSync, sceFiosFHPwritev, sceFiosFHPwritevSync, sceFiosFHRead, sceFiosFHReadSync, sceFiosFHReadv, sceFiosFHReadvSync, sceFiosFHSeek, sceFiosFHStat, sceFiosFHStatSync, sceFiosFHSync, sceFiosFHSyncSync, sceFiosFHTell, sceFiosFHToFileno, sceFiosFHTruncate, sceFiosFHTruncateSync, sceFiosFHWrite, sceFiosFHWriteSync, sceFiosFHWritev, sceFiosFHWritevSync, sceFiosFileDelete, sceFiosFileDeleteSync, sceFiosFileExists, sceFiosFileExistsSync, sceFiosFileGetSize, sceFiosFileGetSizeSync, sceFiosFileRead, sceFiosFileReadSync, sceFiosFileTruncate, sceFiosFileTruncateSync, sceFiosFileWrite, sceFiosFileWriteSync, sceFiosFilenoToFH, sceFiosGetAllDHs, sceFiosGetAllFHs, sceFiosGetAllOps, sceFiosGetDefaultOpAttr, sceFiosGetGlobalDefaultOpAttr, sceFiosGetSuspendCount, sceFiosGetThreadDefaultOpAttr, sceFiosIOFilterAdd, sceFiosIOFilterCache, sceFiosIOFilterGetInfo, sceFiosIOFilterPsarcDearchiver, sceFiosIOFilterRemove, sceFiosInitialize, sceFiosIsIdle, sceFiosIsInitialized, sceFiosIsSuspended, sceFiosIsValidHandle, sceFiosOpCancel, sceFiosOpDelete, sceFiosOpGetActualCount, sceFiosOpGetAttr, sceFiosOpGetBuffer, sceFiosOpGetError, sceFiosOpGetOffset, sceFiosOpGetPath, sceFiosOpGetRequestCount, sceFiosOpIsCancelled, sceFiosOpIsDone, sceFiosOpReschedule, sceFiosOpRescheduleWithPriority, sceFiosOpSetBuffer, sceFiosOpSyncWait, sceFiosOpSyncWaitForIO, sceFiosOpWait, sceFiosOpWaitUntil, sceFiosOverlayAdd, sceFiosOverlayGetInfo, sceFiosOverlayGetList, sceFiosOverlayModify, sceFiosOverlayRemove, sceFiosOverlayResolveSync, sceFiosPathNormalize, sceFiosPathcmp, sceFiosPathncmp, sceFiosPrintTimeStamps, sceFiosPrintf, sceFiosRename, sceFiosRenameSync, sceFiosResolve, sceFiosResolveSync, sceFiosResume, sceFiosSaveTimeStamp, sceFiosSetGlobalDefaultOpAttr, sceFiosSetThreadDefaultOpAttr, sceFiosShutdownAndCancelOps, sceFiosStat, sceFiosStatSync, sceFiosSuspend, sceFiosTerminate, sceFiosTimeGetCurrent, sceFiosTimeIntervalFromNanoseconds, sceFiosTimeIntervalToNanoseconds, sceFiosTraceTimestamp, sceFiosUpdateParameters and sceFiosVprintf](../../../core/libs/prx/libSceFios2/Export.cpp) (libSceFios2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceFios2.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceFsInternalForVsh.md b/docs/dev/debt/libSceFsInternalForVsh.md new file mode 100644 index 0000000000..92591afa7b --- /dev/null +++ b/docs/dev/debt/libSceFsInternalForVsh.md @@ -0,0 +1,5 @@ +# libSceFsInternalForVsh technical debt + +### Unknown function info + +- [sceBdSchedCancelBackgroundCopyRequest, sceBdSchedCancelPrioritizedBackgroundCopyRequest, sceBdSchedConfigure, sceBdSchedConfigure2, sceBdSchedDeconfigure, sceBdSchedGetBackgroundCopyRequest, sceBdSchedGetExtentMap, sceBdSchedGetPrioritizedBackgroundCopyRequest, sceBdSchedGetState, sceBdSchedSetBackgroundCopyRequest, sceBdSchedSetDiscIdentity, sceBdSchedSetPrioritizedBackgroundCopyRequest, sceBdSchedStartGame, sceBdSchedStartRipping, sceBdSchedStopScheduling, sceFsBfsCheckCleanFlag, sceFsBindApplication, sceFsBindPackages, sceFsCheckFormat, sceFsCheckSingleUser, sceFsCreateExfatPartition, sceFsCreatePfsSaveDataImage, sceFsCreatePfsTrophyDataImage, sceFsCreatePprPfsTrophyDataImage, sceFsDeviceAlignedPread, sceFsDeviceAlignedPwrite, sceFsDevpfsA53IocGetBuildMetadataStatus, sceFsDevpfsCancelWaitEvent, sceFsDevpfsCreateForVSH, sceFsDevpfsDeleteForVSH, sceFsDevpfsDeregisterEvent, sceFsDevpfsEventComplete, sceFsDevpfsEventResponseComplete, sceFsDevpfsFetchMetaEntry, sceFsDevpfsFsck, sceFsDevpfsInitFetchMetaEntryCtx, sceFsDevpfsMountForFSMP, sceFsDevpfsRegisterEvent, sceFsDevpfsWaitEvent, sceFsDrfsRegisterSocket, sceFsDrfsResumeSocket, sceFsErriCtl, sceFsErriGetState, sceFsExfatMkfsExternalHdd, sceFsExfatfsMkfs, sceFsExtUSBSchedDestroy, sceFsExtUSBSchedInsert, sceFsExtUSBSchedInsertForHDD, sceFsExternalStorageDeconfigure, sceFsExternalStorageGetRawDevice, sceFsExternalStorageInitialize, sceFsExternalStorageVerifyMetadata, sceFsGetAttribute, sceFsGetClusterStatistics, sceFsGetDeviceSectorsize, sceFsGetFsMetadata, sceFsGetFsMetadataWithProgress, sceFsGetSmrInfo, sceFsGschedCtl, sceFsISSchedConfigCurrentThread, sceFsISSchedConfigDescriptor, sceFsISSchedConfigThread, sceFsISSchedMapStat, sceFsISSchedResultToPriority, sceFsISSchedResultToSlot, sceFsISSchedStatCurrentThread, sceFsISSchedStatDescriptor, sceFsISSchedStatThread, sceFsISSchedUnmapStat, sceFsInitCreatePfsSaveDataOpt, sceFsInitCreatePfsTrophyDataOpt, sceFsInitMountDownloadDataOpt, sceFsInitMountGamePkgOpt, sceFsInitMountLwfsCompatOpt, sceFsInitMountLwfsOpt, sceFsInitMountPprPkgOpt, sceFsInitMountSaveDataOpt, sceFsInitMountTrophyDataOpt, sceFsInitUmountDownloadDataOpt, sceFsInitUmountGamePkgOpt, sceFsInitUmountLwfsCompatOpt, sceFsInitUmountLwfsOpt, sceFsInitUmountPprPkgOpt, sceFsInitUmountSaveDataOpt, sceFsInitUmountTrophyDataOpt, sceFsInitWorkspaceFileMetadataParam, sceFsLvdAttach, sceFsLvdAttach2, sceFsLvdAttachPartiallyCryptedDownloadData, sceFsLvdAttachSingleDefaultImage, sceFsLvdAttachSingleImage, sceFsLvdDetach, sceFsLvdDisableLayer, sceFsLvdEnableLayer, sceFsLvdSetDiscIdentity, sceFsLvdWaitResumed, sceFsMlfsMount, sceFsMlfsMountWithFlags, sceFsMlfsUmount, sceFsMountDownloadData, sceFsMountGamePkg, sceFsMountLwfs, sceFsMountLwfsCompat, sceFsMountPartiallyCryptedDownloadData, sceFsMountPprPkg, sceFsMountSaveData, sceFsMountTrophyData, sceFsNsfsAddOverlayToNamespace, sceFsNsfsMountWithFlags, sceFsNsfsSetGlobalExclude, sceFsNsfsUmount, sceFsPartitionTrim, sceFsReserveCluster, sceFsSetAttribute, sceFsSetClusterCacheSize, sceFsSetFileReservedTotal, sceFsSetSystemReserveSize, sceFsSetWorkspaceFileHash, sceFsStatWorkspace, sceFsSysStorageGetStatus, sceFsTransactionCancel, sceFsTransactionSaveDataInitMountOpt, sceFsTransactionSaveDataMount, sceFsTransactionStart, sceFsTrophyImageError, sceFsUfsAllocateAdditionalContents, sceFsUfsAllocateAligned, sceFsUfsAllocateDownloadData, sceFsUfsAllocateGameImage, sceFsUfsAllocatePatchImage, sceFsUfsAllocateSaveData, sceFsUfsAllocateTrophyData, sceFsUfsCheckCleanFlag, sceFsUfsCheckFixedCylinderGroupSize, sceFsUfsCollectTelemetry, sceFsUfsCollectTelemetry2, sceFsUfsDirectWrite, sceFsUfsEstimateZoneCapacityForUser, sceFsUfsExtendFile, sceFsUfsFsck, sceFsUfsFsckWithMM, sceFsUfsGetExtentMap, sceFsUfsGrowLwfs, sceFsUfsGrowfs, sceFsUfsHideBlocksFromUser, sceFsUfsInitalizeSparseFile, sceFsUfsLargewriteFsck, sceFsUfsLargewriteFsckWithMM, sceFsUfsMkLwfs, sceFsUfsMkfs, sceFsUfsMkfsForExternalHDD, sceFsUfsMkfsForSystem, sceFsUfsMkfsWithFixedCylinderGroupSize, sceFsUfsReallocateSparseFile, sceFsUfsRecryptBlocks, sceFsUfsRecryptGetState, sceFsUfsRecryptIsUnfinished, sceFsUfsRecryptSyncMetadata, sceFsUfsSeekHoleInFile, sceFsUfsStopOperation, sceFsUmountDownloadData, sceFsUmountGamePkg, sceFsUmountLwfs, sceFsUmountLwfsCompat, sceFsUmountSaveData, sceFsUmountTrophyData, sceFsUmountWorkspace, sceFsUnbindApplication, sceFsUnbindPackages, sceFsWsCreate, sceFsWsDelete, sceFsWsFetchFileMetadata, sceFsWsFsck, sceFsWsGetStatus, sceFsWsMount and sceFsWsUmount](../../../core/libs/prx/libSceFsInternalForVsh/Export.cpp) (libSceFsInternalForVsh) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceFsInternalForVsh.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceGic.md b/docs/dev/debt/libSceGic.md new file mode 100644 index 0000000000..c7b8a71c3e --- /dev/null +++ b/docs/dev/debt/libSceGic.md @@ -0,0 +1,5 @@ +# libSceGic technical debt + +### Unknown function info + +- [ and sceGicGetValue](../../../core/libs/prx/libSceGic/Export.cpp) (libSceGic) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceGic.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceHmdSetupDialog.md b/docs/dev/debt/libSceHmdSetupDialog.md new file mode 100644 index 0000000000..cd9e1a5ae2 --- /dev/null +++ b/docs/dev/debt/libSceHmdSetupDialog.md @@ -0,0 +1,5 @@ +# libSceHmdSetupDialog technical debt + +### Unknown function info + +- [sceHmdSetupDialogClose, sceHmdSetupDialogGetResult, sceHmdSetupDialogGetStatus, sceHmdSetupDialogInitialize, sceHmdSetupDialogOpen, sceHmdSetupDialogTerminate and sceHmdSetupDialogUpdateStatus](../../../core/libs/prx/libSceHmdSetupDialog/Export.cpp) (libSceHmdSetupDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceHmdSetupDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceHubAppUtil.md b/docs/dev/debt/libSceHubAppUtil.md new file mode 100644 index 0000000000..e626d2eb49 --- /dev/null +++ b/docs/dev/debt/libSceHubAppUtil.md @@ -0,0 +1,5 @@ +# libSceHubAppUtil technical debt + +### Unknown function info + +- [sceHubAppUtilDeeplinkToLiveDetailView, sceHubAppUtilDeeplinkToPSSubscriptions and sceHubAppUtilGetInstallationStatus](../../../core/libs/prx/libSceHubAppUtil/Export.cpp) (libSceHubAppUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceHubAppUtil.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceIdu.md b/docs/dev/debt/libSceIdu.md new file mode 100644 index 0000000000..4ce48a2927 --- /dev/null +++ b/docs/dev/debt/libSceIdu.md @@ -0,0 +1,5 @@ +# libSceIdu technical debt + +### Unknown function info + +- [sceIduGetEnv, sceIduInit, sceIduInstallPkg, sceIduIsAppInstalled, sceIduIsRifInstalled, sceIduIsStaffMode, sceIduReboot and sceIduTerm](../../../core/libs/prx/libSceIdu/Export.cpp) (libSceIdu) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceIdu.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceLoginMgrServer.md b/docs/dev/debt/libSceLoginMgrServer.md new file mode 100644 index 0000000000..9c6b1255ce --- /dev/null +++ b/docs/dev/debt/libSceLoginMgrServer.md @@ -0,0 +1,5 @@ +# libSceLoginMgrServer technical debt + +### Unknown function info + +- [sceLoginMgrServerCheckSafetyNoticeShown, sceLoginMgrServerClearMorpheusRequiredUserId, sceLoginMgrServerConvertToUserId, sceLoginMgrServerCreateUser, sceLoginMgrServerDestroyUser, sceLoginMgrServerDialogGetAppId, sceLoginMgrServerDialogGetOpenParam, sceLoginMgrServerDialogNotifyCloseFinished, sceLoginMgrServerDialogNotifyOpenFinished, sceLoginMgrServerDialogSetResult, sceLoginMgrServerGetLogoutInfo, sceLoginMgrServerGetMorpheusRequiredUserId, sceLoginMgrServerGetSharePlayAllowPadOperation, sceLoginMgrServerInitialize, sceLoginMgrServerInitializeSharePlayAllowPadOperation, sceLoginMgrServerIsRequestedCdlgClose, sceLoginMgrServerLoginServiceGetRequestParam, sceLoginMgrServerLoginServiceNotifyRequestFinished, sceLoginMgrServerNotifyEasySignInFinished, sceLoginMgrServerNotifyEasySignInUserCode, sceLoginMgrServerNotifyHmdConnectInfo, sceLoginMgrServerNotifySafetyNoticeShown, sceLoginMgrServerNotifyShellUIState, sceLoginMgrServerSetLoginFlag, sceLoginMgrServerSetMorpheusAssignEnabledFlag, sceLoginMgrServerSetMorpheusRequiredUserId, sceLoginMgrServerSetSharePlayMode, sceLoginMgrServerSetUserStatus, sceLoginMgrServerTerminate and sceLoginMgrServerUpdateUserIdRalatedToPadUniqueId](../../../core/libs/prx/libSceLoginMgrServer/Export.cpp) (libSceLoginMgrServer) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLoginMgrServer.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceMusicFrameworkInterface.md b/docs/dev/debt/libSceMusicFrameworkInterface.md new file mode 100644 index 0000000000..1a47762ae9 --- /dev/null +++ b/docs/dev/debt/libSceMusicFrameworkInterface.md @@ -0,0 +1,5 @@ +# libSceMusicFrameworkInterface technical debt + +### Unknown function info + +- [sceMusicFwGetCurrentTime, sceMusicFwGetLoop, sceMusicFwGetMuted, sceMusicFwGetNetworkState, sceMusicFwGetVolume, sceMusicFwIsEnded, sceMusicFwIsPlaying, sceMusicFwIsStopped, sceMusicFwRemoveSrc, sceMusicFwSendMessageAsynchronously, sceMusicFwSendMessageSynchronously, sceMusicFwSetCurrentTime, sceMusicFwSetLoop, sceMusicFwSetShuffle, sceMusicFwSetVolume, sceMusicFwSkipNext and sceMusicFwSkipPrevious](../../../core/libs/prx/libSceMusicFrameworkInterface/Export.cpp) (libSceMusicFrameworkInterface) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMusicFrameworkInterface.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpEulaDialog.md b/docs/dev/debt/libSceNpEulaDialog.md new file mode 100644 index 0000000000..60dab48126 --- /dev/null +++ b/docs/dev/debt/libSceNpEulaDialog.md @@ -0,0 +1,5 @@ +# libSceNpEulaDialog technical debt + +### Unknown function info + +- [sceNpEulaDialogCheckVersion, sceNpEulaDialogCheckVersionAbort, sceNpEulaDialogClose, sceNpEulaDialogGetResult, sceNpEulaDialogGetStatus, sceNpEulaDialogInitialize, sceNpEulaDialogOpen, sceNpEulaDialogTerminate and sceNpEulaDialogUpdateStatus](../../../core/libs/prx/libSceNpEulaDialog/Export.cpp) (libSceNpEulaDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpEulaDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpFriendListDialog.md b/docs/dev/debt/libSceNpFriendListDialog.md new file mode 100644 index 0000000000..1c3824ba59 --- /dev/null +++ b/docs/dev/debt/libSceNpFriendListDialog.md @@ -0,0 +1,5 @@ +# libSceNpFriendListDialog technical debt + +### Unknown function info + +- [sceNpFriendListDialogClose, sceNpFriendListDialogGetResult, sceNpFriendListDialogGetResultA, sceNpFriendListDialogGetStatus, sceNpFriendListDialogInitialize, sceNpFriendListDialogOpen, sceNpFriendListDialogOpenA, sceNpFriendListDialogTerminate and sceNpFriendListDialogUpdateStatus](../../../core/libs/prx/libSceNpFriendListDialog/Export.cpp) (libSceNpFriendListDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpFriendListDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpProfileDialog.md b/docs/dev/debt/libSceNpProfileDialog.md new file mode 100644 index 0000000000..2755456503 --- /dev/null +++ b/docs/dev/debt/libSceNpProfileDialog.md @@ -0,0 +1,5 @@ +# libSceNpProfileDialog technical debt + +### Unknown function info + +- [sceNpProfileDialogClose, sceNpProfileDialogGetResult, sceNpProfileDialogGetStatus, sceNpProfileDialogInitialize, sceNpProfileDialogOpen, sceNpProfileDialogOpenA, sceNpProfileDialogTerminate and sceNpProfileDialogUpdateStatus](../../../core/libs/prx/libSceNpProfileDialog/Export.cpp) (libSceNpProfileDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpProfileDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpRemotePlaySessionSignaling.md b/docs/dev/debt/libSceNpRemotePlaySessionSignaling.md new file mode 100644 index 0000000000..98a7efb65f --- /dev/null +++ b/docs/dev/debt/libSceNpRemotePlaySessionSignaling.md @@ -0,0 +1,5 @@ +# libSceNpRemotePlaySessionSignaling technical debt + +### Unknown function info + +- [sceNpRemotePlaySessionSignalingCreateJoinSession, sceNpRemotePlaySessionSignalingGetConnectionStatus, sceNpRemotePlaySessionSignalingGetCustomProperty, sceNpRemotePlaySessionSignalingGetLocalNetInfo, sceNpRemotePlaySessionSignalingGetMemoryInfo, sceNpRemotePlaySessionSignalingInitialize, sceNpRemotePlaySessionSignalingJoinSession, sceNpRemotePlaySessionSignalingLeaveSession, sceNpRemotePlaySessionSignalingSetCustomProperty and sceNpRemotePlaySessionSignalingTerminate](../../../core/libs/prx/libSceNpRemotePlaySessionSignaling/Export.cpp) (libSceNpRemotePlaySessionSignaling) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpRemotePlaySessionSignaling.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpSns.md b/docs/dev/debt/libSceNpSns.md new file mode 100644 index 0000000000..9f14bbcc3a --- /dev/null +++ b/docs/dev/debt/libSceNpSns.md @@ -0,0 +1,8 @@ +# libSceNpSns technical debt + +### Unknown function info + +- [sceNpSnsFacebookAbortRequest, sceNpSnsFacebookCreateRequest, sceNpSnsFacebookDeleteRequest, sceNpSnsFacebookGetAccessToken, sceNpSnsIntAbortRequest, sceNpSnsIntCreateRequest, sceNpSnsIntDeleteRequest, sceNpSnsIntFbGetGameAccessToken, sceNpSnsIntFbGetGameAccessTokenAllowed, sceNpSnsIntFbGetSystemAccessToken, sceNpSnsIntFbGetTitleInfo, sceNpSnsIntLinkedStatus, sceNpSnsIntTwGetSystemAccessToken, sceNpSnsIntUnlink, sceNpSnsIntYtGetAccessToken, sceNpSnsIntYtRefreshMasterToken, sceNpSnsTwitchAbortRequest, sceNpSnsTwitchCreateRequest, sceNpSnsTwitchDeleteRequest, sceNpSnsTwitchGetAccessToken, sceNpSnsYouTubeAbortRequest, sceNpSnsYouTubeCreateRequest, sceNpSnsYouTubeDeleteRequest and sceNpSnsYouTubeGetAccessToken](../../../core/libs/prx/libSceNpSns/Export.cpp) (libSceNpSns) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSns.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified + +### Inventory exports declared elsewhere + sceNpSnsFacebookCreateRequest, sceNpSnsFacebookDeleteRequest and sceNpSnsFacebookGetAccessToken are also 3.20 exports of this module but are currently declared by #6977 in libSceNpSnsFacebookDialog, so they are omitted here to respect the duplicate-export rule; re-add if that PR's module attribution is rejected. diff --git a/docs/dev/debt/libSceNpSnsYouTubeDialog.md b/docs/dev/debt/libSceNpSnsYouTubeDialog.md new file mode 100644 index 0000000000..cbd7cf8126 --- /dev/null +++ b/docs/dev/debt/libSceNpSnsYouTubeDialog.md @@ -0,0 +1,5 @@ +# libSceNpSnsYouTubeDialog technical debt + +### Unknown function info + +- [sceNpSnsYouTubeDialogClose, sceNpSnsYouTubeDialogGetResult, sceNpSnsYouTubeDialogGetStatus, sceNpSnsYouTubeDialogInitialize, sceNpSnsYouTubeDialogOpen, sceNpSnsYouTubeDialogTerminate and sceNpSnsYouTubeDialogUpdateStatus](../../../core/libs/prx/libSceNpSnsYouTubeDialog/Export.cpp) (libSceNpSnsYouTubeDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSnsYouTubeDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpTcs.md b/docs/dev/debt/libSceNpTcs.md new file mode 100644 index 0000000000..3f4822e6e5 --- /dev/null +++ b/docs/dev/debt/libSceNpTcs.md @@ -0,0 +1,5 @@ +# libSceNpTcs technical debt + +### Unknown function info + +- [sceNpTcsIntDownloadData, sceNpTcsIntUploadDataToNewSlotAbort and sceNpTcsIntUploadDataToNewSlotBegin](../../../core/libs/prx/libSceNpTcs/Export.cpp) (libSceNpTcs) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpTcs.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libScePadTracker.md b/docs/dev/debt/libScePadTracker.md new file mode 100644 index 0000000000..3111036706 --- /dev/null +++ b/docs/dev/debt/libScePadTracker.md @@ -0,0 +1,5 @@ +# libScePadTracker technical debt + +### Unknown function info + +- [scePadTrackerCalibrate, scePadTrackerGetWorkingMemorySize, scePadTrackerInit, scePadTrackerReadState, scePadTrackerTerm and scePadTrackerUpdate](../../../core/libs/prx/libScePadTracker/Export.cpp) (libScePadTracker) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePadTracker.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libScePatchCheckerClient.md b/docs/dev/debt/libScePatchCheckerClient.md new file mode 100644 index 0000000000..16461c497c --- /dev/null +++ b/docs/dev/debt/libScePatchCheckerClient.md @@ -0,0 +1,5 @@ +# libScePatchCheckerClient technical debt + +### Unknown function info + +- [scePatchCheckerCancel, scePatchCheckerCheckPatch, scePatchCheckerCreateHandler, scePatchCheckerDestroyHandler, scePatchCheckerDisableAutoDownload, scePatchCheckerEnableAutoDownload, scePatchCheckerGetApplicableTick, scePatchCheckerGetPackageInfo, scePatchCheckerInitialize, scePatchCheckerRequestCheckPatch, scePatchCheckerRequestCheckPatchByType and scePatchCheckerTerminate](../../../core/libs/prx/libScePatchCheckerClient/Export.cpp) (libScePatchCheckerClient) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePatchCheckerClient.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libScePlayReady.md b/docs/dev/debt/libScePlayReady.md new file mode 100644 index 0000000000..f3b7a6a60c --- /dev/null +++ b/docs/dev/debt/libScePlayReady.md @@ -0,0 +1,5 @@ +# libScePlayReady technical debt + +### Unknown function info + +- [sceBufferAlloc, sceBufferDup, sceBufferFree, sceBufferInit, sceBufferRealloc, scePlayReadyApiInitializeInitParams, scePlayReadyApiIsStarted, scePlayReadyApiStart, scePlayReadyApiStop, scePlayReadyApiVersion, scePlayReadyApiVersionString, scePlayReadyBufferFree, scePlayReadyBufferInit, scePlayReadyBufferMalloc, scePlayReadyBufferRealloc, scePlayReadyContentGetProperty, scePlayReadyContentSetProperty, scePlayReadyDomainCertEnumFini, scePlayReadyDomainCertEnumInit, scePlayReadyDomainCertEnumNext, scePlayReadyDomainCertFind, scePlayReadyDomainJoinGenerateChallenge, scePlayReadyDomainJoinProcessResponse, scePlayReadyDomainLeaveGenerateChallenge, scePlayReadyDomainLeaveProcessResponse, scePlayReadyEnvelopeClose, scePlayReadyEnvelopeGetSize, scePlayReadyEnvelopeInitializeRead, scePlayReadyEnvelopeOpen, scePlayReadyEnvelopeRead, scePlayReadyEnvelopeSeek, scePlayReadyFinalize, scePlayReadyFreeDecryptContext, scePlayReadyInitialize, scePlayReadyInitializeInitParams, scePlayReadyLicenseAcqGenerateAck, scePlayReadyLicenseAcqGenerateChallenge, scePlayReadyLicenseAcqGetAdditionalResponseData, scePlayReadyLicenseAcqProcessAckResponse, scePlayReadyLicenseAcqProcessResponse, scePlayReadyMemAlloc, scePlayReadyMemFree, scePlayReadyMemGetStats, scePlayReadyMemRealloc, scePlayReadyNewDecryptContext, scePlayReadyReaderBind, scePlayReadyReaderCommit, scePlayReadyReaderDecrypt, scePlayReadyReset and scePlayReadyStoreCleanup](../../../core/libs/prx/libScePlayReady/Export.cpp) (libScePlayReady) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePlayReady.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libScePlayReady2.md b/docs/dev/debt/libScePlayReady2.md new file mode 100644 index 0000000000..e3a51504a9 --- /dev/null +++ b/docs/dev/debt/libScePlayReady2.md @@ -0,0 +1,8 @@ +# libScePlayReady2 technical debt + +### Unknown function info + +- [scePlayReadyBufferInitWithData, scePlayReadyCloneDecryptContext, scePlayReadyDebugPrintf, scePlayReadyDebugSetLevel, scePlayReadyLicenseDelete, scePlayReadyLicenseDeleteInMemory, scePlayReadyLicenseGetExpirationTime, scePlayReadyLicenseGetProperty, scePlayReadyReaderClose, scePlayReadyReaderDecryptMultiple, scePlayReadyReaderDecryptMultipleOpaque, scePlayReadyReaderDecryptOpaque, scePlayReadyReaderDecrypt_Legacy, scePlayReadyReaderFreeOpaqueContent, scePlayReadyReaderRetrieveOpaqueContent and scePlayReadyReinitialize](../../../core/libs/prx/libScePlayReady2/Export.cpp) (libScePlayReady2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePlayReady2.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified + +### Inventory exports declared elsewhere + The PS5 3.20 export table for this module also contains 39 names already declared under libScePlayReady in this tree; the duplicate-export rule keeps each name in a single library, so they are omitted here. They resolve through the sibling declarations. diff --git a/docs/dev/debt/libScePlayReady4.md b/docs/dev/debt/libScePlayReady4.md new file mode 100644 index 0000000000..5ee34a2ccf --- /dev/null +++ b/docs/dev/debt/libScePlayReady4.md @@ -0,0 +1,8 @@ +# libScePlayReady4 technical debt + +### Unknown function info + +- [scePlayReadyApiPlatform, scePlayReadyCdmiClose, scePlayReadyCdmiCreateMediaKeySession, scePlayReadyCdmiCreateMediaKeySystemAccess, scePlayReadyCdmiCreateMediaKeys, scePlayReadyCdmiDecryptMultipleOpaque, scePlayReadyCdmiDecryptOpaque, scePlayReadyCdmiDecryptOpaque2, scePlayReadyCdmiDestroyMediaKeySession, scePlayReadyCdmiDestroyMediaKeySystemAccess, scePlayReadyCdmiDestroyMediaKeys, scePlayReadyCdmiFreeOpaqueDecryptedContent, scePlayReadyCdmiGenerateRequest, scePlayReadyCdmiGetConfiguration, scePlayReadyCdmiGetExpiration, scePlayReadyCdmiGetKeySystem, scePlayReadyCdmiGetMediaKeyCount, scePlayReadyCdmiGetMediaKeyStatus, scePlayReadyCdmiGetSessionAppContext, scePlayReadyCdmiGetSessionId, scePlayReadyCdmiIsLicenseAckRequired, scePlayReadyCdmiLoad, scePlayReadyCdmiRemove, scePlayReadyCdmiRetrieveOpaqueDecryptedContent, scePlayReadyCdmiSetServerCertificate, scePlayReadyCdmiSetSessionCallbacks, scePlayReadyCdmiUpdate, scePlayReadyLicenseCount, scePlayReadyProfileClear, scePlayReadyProfileEnter, scePlayReadyProfileLeave, scePlayReadyProfilePause, scePlayReadyProfileResume, scePlayReadyProfileStart and scePlayReadyProfileStop](../../../core/libs/prx/libScePlayReady4/Export.cpp) (libScePlayReady4) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePlayReady4.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified + +### Inventory exports declared elsewhere + The PS5 3.20 export table for this module also contains 49 names already declared under libScePlayReady, libScePlayReady2 in this tree; the duplicate-export rule keeps each name in a single library, so they are omitted here. They resolve through the sibling declarations. diff --git a/docs/dev/debt/libScePs2EmuMenuDialog.md b/docs/dev/debt/libScePs2EmuMenuDialog.md new file mode 100644 index 0000000000..abb7485619 --- /dev/null +++ b/docs/dev/debt/libScePs2EmuMenuDialog.md @@ -0,0 +1,5 @@ +# libScePs2EmuMenuDialog technical debt + +### Unknown function info + +- [scePs2EmuMenuDialogClose, scePs2EmuMenuDialogGetResult, scePs2EmuMenuDialogGetStatus, scePs2EmuMenuDialogInitialize, scePs2EmuMenuDialogOpen, scePs2EmuMenuDialogTerminate and scePs2EmuMenuDialogUpdateStatus](../../../core/libs/prx/libScePs2EmuMenuDialog/Export.cpp) (libScePs2EmuMenuDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePs2EmuMenuDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libScePsmUtil.md b/docs/dev/debt/libScePsmUtil.md new file mode 100644 index 0000000000..569dad80bc --- /dev/null +++ b/docs/dev/debt/libScePsmUtil.md @@ -0,0 +1,5 @@ +# libScePsmUtil technical debt + +### Unknown function info + +- [scePsmUtilFinalize, scePsmUtilGetPsmIniFile, scePsmUtilInitialize and scePsmUtilRegisterCallbacks](../../../core/libs/prx/libScePsmUtil/Export.cpp) (libScePsmUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePsmUtil.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceShareFactoryUtil.md b/docs/dev/debt/libSceShareFactoryUtil.md new file mode 100644 index 0000000000..3bb30765d2 --- /dev/null +++ b/docs/dev/debt/libSceShareFactoryUtil.md @@ -0,0 +1,5 @@ +# libSceShareFactoryUtil technical debt + +### Unknown function info + +- [sceShareFactoryUtilBackToGameFromShareFactory, sceShareFactoryUtilGetLaunchStatus, sceShareFactoryUtilGetLaunchStatusOfShareFactory, sceShareFactoryUtilInitialize, sceShareFactoryUtilLaunchShareFactory and sceShareFactoryUtilTerminate](../../../core/libs/prx/libSceShareFactoryUtil/Export.cpp) (libSceShareFactoryUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceShareFactoryUtil.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSocialScreen.md b/docs/dev/debt/libSceSocialScreen.md new file mode 100644 index 0000000000..1978669c08 --- /dev/null +++ b/docs/dev/debt/libSceSocialScreen.md @@ -0,0 +1,5 @@ +# libSceSocialScreen technical debt + +### Unknown function info + +- [sceSocialScreenCloseSeparateMode, sceSocialScreenConfigureSeparateMode, sceSocialScreenCrashDaemon, sceSocialScreenGetIgnoreSeparateMode, sceSocialScreenInitialize, sceSocialScreenInitializeSeparateModeParameter, sceSocialScreenOpenSeparateMode, sceSocialScreenReserveHighFrameRateVideoCaptureRequest, sceSocialScreenSetIgnoreSeparateMode, sceSocialScreenSetMode and sceSocialScreenTerminate](../../../core/libs/prx/libSceSocialScreen/Export.cpp) (libSceSocialScreen) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSocialScreen.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSocialScreenDialog.md b/docs/dev/debt/libSceSocialScreenDialog.md new file mode 100644 index 0000000000..a1b39f0af9 --- /dev/null +++ b/docs/dev/debt/libSceSocialScreenDialog.md @@ -0,0 +1,5 @@ +# libSceSocialScreenDialog technical debt + +### Unknown function info + +- [sceSocialScreenDialogClose, sceSocialScreenDialogGetResult, sceSocialScreenDialogGetStatus, sceSocialScreenDialogInitialize, sceSocialScreenDialogOpen, sceSocialScreenDialogTerminate and sceSocialScreenDialogUpdateStatus](../../../core/libs/prx/libSceSocialScreenDialog/Export.cpp) (libSceSocialScreenDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSocialScreenDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSpSysCallWrapper.md b/docs/dev/debt/libSceSpSysCallWrapper.md new file mode 100644 index 0000000000..ea6d5fbf4e --- /dev/null +++ b/docs/dev/debt/libSceSpSysCallWrapper.md @@ -0,0 +1,5 @@ +# libSceSpSysCallWrapper technical debt + +### Unknown function info + +- [sceSpGetPid, sceSpKernelGettimeofday, sceSpKernelNanosleep, sceSpNetConnect, sceSpNetEpollAbort, sceSpNetEpollControl, sceSpNetEpollCreate, sceSpNetEpollDestroy, sceSpNetEpollWait, sceSpNetErrnoLoc, sceSpNetPoolCreate, sceSpNetPoolDestroy, sceSpNetRecv, sceSpNetResolverAbort, sceSpNetResolverCreate, sceSpNetResolverDestroy, sceSpNetResolverGetError, sceSpNetResolverStartNtoa, sceSpNetSend, sceSpNetSetsockopt, sceSpNetSocket, sceSpNetSocketClose, sceSpPthreadAttrDestroy, sceSpPthreadAttrInit, sceSpPthreadAttrSetinheritsched, sceSpPthreadAttrSetschedparam, sceSpPthreadAttrSetstacksize, sceSpPthreadCondDestroy, sceSpPthreadCondInit, sceSpPthreadCondSignal, sceSpPthreadCondWait, sceSpPthreadCondattrDestroy, sceSpPthreadCondattrInit, sceSpPthreadCreate, sceSpPthreadDetach, sceSpPthreadExit, sceSpPthreadJoin, sceSpPthreadMutexDestroy, sceSpPthreadMutexInit, sceSpPthreadMutexLock, sceSpPthreadMutexUnlock, sceSpPthreadMutexattrDestroy, sceSpPthreadMutexattrInit, sceSpPthreadMutexattrSetprioceiling, sceSpPthreadMutexattrSetprotocol, sceSpPthreadMutexattrSettype, sceSpPthreadSetprio and sceSpSelect](../../../core/libs/prx/libSceSpSysCallWrapper/Export.cpp) (libSceSpSysCallWrapper) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSpSysCallWrapper.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSrcUtl.md b/docs/dev/debt/libSceSrcUtl.md new file mode 100644 index 0000000000..bb8018ab3a --- /dev/null +++ b/docs/dev/debt/libSceSrcUtl.md @@ -0,0 +1,5 @@ +# libSceSrcUtl technical debt + +### Unknown function info + +- [sceSrcUtilityFinalize, sceSrcUtilityFlush, sceSrcUtilityGetDelaySamples, sceSrcUtilityGetHandleSize, sceSrcUtilityGetPriority, sceSrcUtilityInitialize, sceSrcUtilityInitializeEx, sceSrcUtilityResample and sceSrcUtilitySetPriority](../../../core/libs/prx/libSceSrcUtl/Export.cpp) (libSceSrcUtl) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSrcUtl.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSulphaDrv.md b/docs/dev/debt/libSceSulphaDrv.md new file mode 100644 index 0000000000..88b6f9091f --- /dev/null +++ b/docs/dev/debt/libSceSulphaDrv.md @@ -0,0 +1,5 @@ +# libSceSulphaDrv technical debt + +### Inventory exports declared elsewhere + +sceSulphaGetClientInfo, sceSulphaGetConnectedClientCount, sceSulphaGetDefaultConfig, sceSulphaGetNeededMemory, sceSulphaInit, sceSulphaMessage, sceSulphaSetBookmark and sceSulphaShutdown are the complete PS5 3.20 export table of this module per the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSulphaDrv.c), but all eight are currently declared by #7416 in libSceSulpha, so this module keeps its `APS5_DUMMY_FUN` skeleton to respect the duplicate-export rule. Declare them here (with no parameters, matching the `1b5004d77` unknown-prototype shape) if that PR's module attribution is rejected. diff --git a/docs/dev/debt/libSceSystemLogger.md b/docs/dev/debt/libSceSystemLogger.md new file mode 100644 index 0000000000..f4f9de0077 --- /dev/null +++ b/docs/dev/debt/libSceSystemLogger.md @@ -0,0 +1,5 @@ +# libSceSystemLogger technical debt + +### Unknown function info + +- [sceSystemLoggerInitialize, sceSystemLoggerSetLogData and sceSystemLoggerTerminate](../../../core/libs/prx/libSceSystemLogger/Export.cpp) (libSceSystemLogger) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemLogger.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSystemLogger2Delivery.md b/docs/dev/debt/libSceSystemLogger2Delivery.md new file mode 100644 index 0000000000..c97c25f582 --- /dev/null +++ b/docs/dev/debt/libSceSystemLogger2Delivery.md @@ -0,0 +1,5 @@ +# libSceSystemLogger2Delivery technical debt + +### Unknown function info + +- [sceSystemLogger2DeliveryGetBatchResult, sceSystemLogger2DeliveryGetResult, sceSystemLogger2DeliverySetBatchDeliveryEvent and sceSystemLogger2DeliveryTerminate](../../../core/libs/prx/libSceSystemLogger2Delivery/Export.cpp) (libSceSystemLogger2Delivery) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemLogger2Delivery.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSystemLogger2NativeQueueClient.md b/docs/dev/debt/libSceSystemLogger2NativeQueueClient.md new file mode 100644 index 0000000000..378c259200 --- /dev/null +++ b/docs/dev/debt/libSceSystemLogger2NativeQueueClient.md @@ -0,0 +1,5 @@ +# libSceSystemLogger2NativeQueueClient technical debt + +### Unknown function info + +- [sceSystemLogger2NativeQueueClientCheckAndMaskData, sceSystemLogger2NativeQueueClientGetEvent, sceSystemLogger2NativeQueueClientSetDeliveryEnable, sceSystemLogger2NativeQueueClientSetDeliveryResult and sceSystemLogger2NativeQueueClientSetNativeQueueConfig](../../../core/libs/prx/libSceSystemLogger2NativeQueueClient/Export.cpp) (libSceSystemLogger2NativeQueueClient) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemLogger2NativeQueueClient.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceSystemTts.md b/docs/dev/debt/libSceSystemTts.md new file mode 100644 index 0000000000..593c01117d --- /dev/null +++ b/docs/dev/debt/libSceSystemTts.md @@ -0,0 +1,5 @@ +# libSceSystemTts technical debt + +### Unknown function info + +- [sceSystemTtsCancel, sceSystemTtsIsAccessibilityAvailable, sceSystemTtsRegisterCallback, sceSystemTtsSpeakRequest and sceSystemTtsUnregisterCallback](../../../core/libs/prx/libSceSystemTts/Export.cpp) (libSceSystemTts) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemTts.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceTextToSpeech.md b/docs/dev/debt/libSceTextToSpeech.md new file mode 100644 index 0000000000..4098de045d --- /dev/null +++ b/docs/dev/debt/libSceTextToSpeech.md @@ -0,0 +1,5 @@ +# libSceTextToSpeech technical debt + +### Unknown function info + +- [sceTextToSpeechCancelImpl, sceTextToSpeechCloseImpl, sceTextToSpeechGetStatusImpl, sceTextToSpeechInitImpl, sceTextToSpeechOpenImpl, sceTextToSpeechPauseImpl, sceTextToSpeechResumeImpl, sceTextToSpeechSpeakContinuousImpl, sceTextToSpeechSpeakImpl and sceTextToSpeechTermImpl](../../../core/libs/prx/libSceTextToSpeech/Export.cpp) (libSceTextToSpeech) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceTextToSpeech.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceUpdateService.md b/docs/dev/debt/libSceUpdateService.md new file mode 100644 index 0000000000..73c61c3d32 --- /dev/null +++ b/docs/dev/debt/libSceUpdateService.md @@ -0,0 +1,5 @@ +# libSceUpdateService technical debt + +### Unknown function info + +- [sceUpsrvGetCompletedTaskId, sceUpsrvGetRebootFlag, sceUpsrvGetSystemExUpdateTaskState, sceUpsrvInitialize, sceUpsrvPostErrorLog, sceUpsrvResumePausedTask, sceUpsrvSetFakeVersion, sceUpsrvStartUpdateSession, sceUpsrvSwitchSystemExBank, sceUpsrvSwitchSystemExBankWithoutVerify, sceUpsrvTerminate, sceUpsrvUpdateCancelBDUpdate, sceUpsrvUpdateCheckCancel, sceUpsrvUpdateCheckCreateHandler, sceUpsrvUpdateCheckDestroyHandler, sceUpsrvUpdateCheckDoCheckSystem, sceUpsrvUpdateCheckDoCheckSystemForDevKit, sceUpsrvUpdateCheckDoCheckSystemForSettings, sceUpsrvUpdateCheckDoCheckSystemWithPupInfo, sceUpsrvUpdateCreateUpdateTask, sceUpsrvUpdateDestroyUpdateTask, sceUpsrvUpdateDoCheckBootUpdate, sceUpsrvUpdateDoUpdateSystemEx, sceUpsrvUpdateDoUpdateWithEntitlement, sceUpsrvUpdateDoUpdateWithPupInfo, sceUpsrvUpdateDoUpdateWithPupPath2, sceUpsrvUpdateGetBetaAgreementUrl, sceUpsrvUpdateGetEulaDisplay, sceUpsrvUpdateGetEulaStr, sceUpsrvUpdateGetEulaStrLen, sceUpsrvUpdateGetFeatureListUrl, sceUpsrvUpdateGetFeatureListUrlForSystemExUpdate, sceUpsrvUpdateGetFeatureListUrlForUpdate, sceUpsrvUpdateGetImageWritingProgress, sceUpsrvUpdateGetProgress, sceUpsrvUpdateGetUpdateTaskInfo, sceUpsrvUpdateGetUpdateTaskMgrInfo, sceUpsrvUpdateSetEulaAccept, sceUpsrvUpdateStartUpdateTask and sceUpsrvVerifySystemExBank](../../../core/libs/prx/libSceUpdateService/Export.cpp) (libSceUpdateService) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceUpdateService.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceUrlConfigResolver.md b/docs/dev/debt/libSceUrlConfigResolver.md index 035bdc8e88..a770778d1b 100644 --- a/docs/dev/debt/libSceUrlConfigResolver.md +++ b/docs/dev/debt/libSceUrlConfigResolver.md @@ -3,3 +3,4 @@ ### Unknown function info - The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`sceUrlConfigResolverGetReleaseVersion`](../../../core/libs/prx/libSceUrlConfigResolver/Export.cpp), [`sceUrlConfigResolverGetSysVer`](../../../core/libs/prx/libSceUrlConfigResolver/Export.cpp), [`sceUrlConfigResolverIsUpdated`](../../../core/libs/prx/libSceUrlConfigResolver/Export.cpp), [`sceUrlConfigResolverUpdate`](../../../core/libs/prx/libSceUrlConfigResolver/Export.cpp). +- [sceUrlConfigResolverGetAppConfig, sceUrlConfigResolverGetDefaultQueryParameter, sceUrlConfigResolverGetDeviceId and sceUrlConfigResolverGetJscHeapSizeSoftLimit](../../../core/libs/prx/libSceUrlConfigResolver/Export.cpp) (libSceUrlConfigResolver) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceUrlConfigResolver.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVdecCore.native.md b/docs/dev/debt/libSceVdecCore.native.md new file mode 100644 index 0000000000..fd07ad5f05 --- /dev/null +++ b/docs/dev/debt/libSceVdecCore.native.md @@ -0,0 +1,8 @@ +# libSceVdecCore.native technical debt + +### Unknown function info + +- [sceVdecCoreCreateDecoderBid, sceVdecCoreCreateDecoderSvp, sceVdecCoreMapMemory and sceVdecCoreSetDecodeInputSvp](../../../core/libs/prx/libSceVdecCore.native/Export.cpp) (libSceVdecCore.native) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecCore.native.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified + +### Inventory exports declared elsewhere + The PS5 3.20 export table for this module also contains 20 names already declared under libSceVdecCore in this tree; the duplicate-export rule keeps each name in a single library, so they are omitted here. They resolve through the sibling declarations. diff --git a/docs/dev/debt/libSceVdecSavc.md b/docs/dev/debt/libSceVdecSavc.md new file mode 100644 index 0000000000..be07a4d7b7 --- /dev/null +++ b/docs/dev/debt/libSceVdecSavc.md @@ -0,0 +1,5 @@ +# libSceVdecSavc technical debt + +### Unknown function info + +- [sceSdecCreateSw, sceSdecDestroySw, sceSdecGetVersionSw and sceSdecQueryMemorySw](../../../core/libs/prx/libSceVdecSavc/Export.cpp) (libSceVdecSavc) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecSavc.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVdecSavc2.md b/docs/dev/debt/libSceVdecSavc2.md new file mode 100644 index 0000000000..d72e048aac --- /dev/null +++ b/docs/dev/debt/libSceVdecSavc2.md @@ -0,0 +1,5 @@ +# libSceVdecSavc2 technical debt + +### Unknown function info + +- [sceSdecCreateSw2, sceSdecDestroySw2, sceSdecGetVersionSw2 and sceSdecQueryMemorySw2](../../../core/libs/prx/libSceVdecSavc2/Export.cpp) (libSceVdecSavc2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecSavc2.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVdecSavc2.native.md b/docs/dev/debt/libSceVdecSavc2.native.md new file mode 100644 index 0000000000..ac5be7701f --- /dev/null +++ b/docs/dev/debt/libSceVdecSavc2.native.md @@ -0,0 +1,5 @@ +# libSceVdecSavc2.native technical debt + +### Inventory exports declared elsewhere + +The complete PS5 3.20 export table of this module per the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecSavc2.native.c) (4 names) is already declared under the sibling libSceVdecSavc2 in this tree; the duplicate-export rule keeps each name in a single library, so this module keeps its `APS5_DUMMY_FUN` skeleton. Imports resolve through the sibling declarations. diff --git a/docs/dev/debt/libSceVdecShevc.md b/docs/dev/debt/libSceVdecShevc.md new file mode 100644 index 0000000000..0413883bb5 --- /dev/null +++ b/docs/dev/debt/libSceVdecShevc.md @@ -0,0 +1,5 @@ +# libSceVdecShevc technical debt + +### Unknown function info + +- [sceSdecCreateSwHevc, sceSdecDestroySwHevc, sceSdecExecuteSwHevc, sceSdecGetFrameSwHevc, sceSdecGetVersionSwHevc, sceSdecQueryMemorySwHevc and sceSdecResetSwHevc](../../../core/libs/prx/libSceVdecShevc/Export.cpp) (libSceVdecShevc) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecShevc.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVdecShevc.native.md b/docs/dev/debt/libSceVdecShevc.native.md new file mode 100644 index 0000000000..1187969c07 --- /dev/null +++ b/docs/dev/debt/libSceVdecShevc.native.md @@ -0,0 +1,5 @@ +# libSceVdecShevc.native technical debt + +### Inventory exports declared elsewhere + +The complete PS5 3.20 export table of this module per the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecShevc.native.c) (7 names) is already declared under the sibling libSceVdecShevc in this tree; the duplicate-export rule keeps each name in a single library, so this module keeps its `APS5_DUMMY_FUN` skeleton. Imports resolve through the sibling declarations. diff --git a/docs/dev/debt/libSceVdecwrap.md b/docs/dev/debt/libSceVdecwrap.md new file mode 100644 index 0000000000..d76e301e8e --- /dev/null +++ b/docs/dev/debt/libSceVdecwrap.md @@ -0,0 +1,5 @@ +# libSceVdecwrap technical debt + +### Unknown function info + +- [sceVdecwrapCreateDecoder, sceVdecwrapDeleteDecoder, sceVdecwrapFlushDecodeOutput, sceVdecwrapGetDecodeOutput, sceVdecwrapMapDirectMemory, sceVdecwrapMapMemory, sceVdecwrapQueryDecoderMemoryInfo, sceVdecwrapQueryFrameBufferInfo, sceVdecwrapResetDecoder, sceVdecwrapSetDecodeInput and sceVdecwrapSyncDecode](../../../core/libs/prx/libSceVdecwrap/Export.cpp) (libSceVdecwrap) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVdecwrap.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoArbitration.md b/docs/dev/debt/libSceVideoArbitration.md new file mode 100644 index 0000000000..d683418191 --- /dev/null +++ b/docs/dev/debt/libSceVideoArbitration.md @@ -0,0 +1,5 @@ +# libSceVideoArbitration technical debt + +### Unknown function info + +- [sceVideoArbitrationAcceptEvent, sceVideoArbitrationEnable and sceVideoArbitrationInitialize](../../../core/libs/prx/libSceVideoArbitration/Export.cpp) (libSceVideoArbitration) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoArbitration.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoCoreInterface.md b/docs/dev/debt/libSceVideoCoreInterface.md index f5053cfc00..347236cfd8 100644 --- a/docs/dev/debt/libSceVideoCoreInterface.md +++ b/docs/dev/debt/libSceVideoCoreInterface.md @@ -3,3 +3,4 @@ ### Unknown function info - The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`sceVideoCoreIfInitializeInterface`](../../../core/libs/prx/libSceVideoCoreInterface/Export.cpp), [`sceVideoCoreIfMainLoop`](../../../core/libs/prx/libSceVideoCoreInterface/Export.cpp), [`sceVideoCoreIfSetFunctionTable`](../../../core/libs/prx/libSceVideoCoreInterface/Export.cpp). +- [sceVideoCoreIfSetFunctionTableUnlimited](../../../core/libs/prx/libSceVideoCoreInterface/Export.cpp) (libSceVideoCoreInterface) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoCoreInterface.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoCoreServerInterface.md b/docs/dev/debt/libSceVideoCoreServerInterface.md new file mode 100644 index 0000000000..d5d88b3c3d --- /dev/null +++ b/docs/dev/debt/libSceVideoCoreServerInterface.md @@ -0,0 +1,5 @@ +# libSceVideoCoreServerInterface technical debt + +### Unknown function info + +- [sceVideoCoreAbortRequest, sceVideoCoreAcquireDecoderResource, sceVideoCoreAcquireDecoderResourceForBeWrapper, sceVideoCoreAddStream, sceVideoCoreAsyncAddStream, sceVideoCoreAsyncSendRequest, sceVideoCoreCancelThumbnail, sceVideoCoreChangeActiveStatus, sceVideoCoreChangeAudioStream, sceVideoCoreChangeDisplaySize, sceVideoCoreChangeHdcpSetting, sceVideoCoreChangeSubtitle, sceVideoCoreDeleteStream, sceVideoCoreGenerateThumbnail, sceVideoCoreGenerateThumbnail2, sceVideoCoreGetCanvasHandle, sceVideoCoreGetPlayStatus, sceVideoCoreGetPlayStatus2, sceVideoCoreGetStreamUid, sceVideoCoreGetThumbnail, sceVideoCoreGetThumbnail2, sceVideoCoreLaunchVideoCore, sceVideoCoreMediaSourceAbortSourceBuffer, sceVideoCoreMediaSourceAddSourceBuffer, sceVideoCoreMediaSourceAppendData, sceVideoCoreMediaSourceEndOfStream, sceVideoCoreMediaSourceGetBufferedTimeRanges, sceVideoCoreMediaSourceGetInputBufferEmptySize, sceVideoCoreMediaSourceGetPlaybackQuality, sceVideoCoreMediaSourceGetSourceBufferEvent, sceVideoCoreMediaSourceRemoveSourceBuffer, sceVideoCoreMediaSourceSetDuration, sceVideoCoreMediaSourceSetTimestampOffset, sceVideoCorePlayControl, sceVideoCoreRegistEventCallback, sceVideoCoreReleaseDecoderResource, sceVideoCoreReleaseDecoderResourceForBeWrapper, sceVideoCoreSetAudioMute, sceVideoCoreSetAudioVolume, sceVideoCoreSetFullScreen, sceVideoCoreSetInitialInfo, sceVideoCoreSetInitializeInfo, sceVideoCoreSetPlaybackRange, sceVideoCoreSetThumbnailInfo, sceVideoCoreSyncAddStream, sceVideoCoreSyncSendRequest and sceVideoCoreUnregistEventCallback](../../../core/libs/prx/libSceVideoCoreServerInterface/Export.cpp) (libSceVideoCoreServerInterface) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoCoreServerInterface.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoDecoderArbitration.md b/docs/dev/debt/libSceVideoDecoderArbitration.md new file mode 100644 index 0000000000..219e005a17 --- /dev/null +++ b/docs/dev/debt/libSceVideoDecoderArbitration.md @@ -0,0 +1,5 @@ +# libSceVideoDecoderArbitration technical debt + +### Unknown function info + +- [sceVideoDecoderArbitrationAcceptEvent, sceVideoDecoderArbitrationEnable, sceVideoDecoderArbitrationEnableSuspendMode and sceVideoDecoderArbitrationInitialize](../../../core/libs/prx/libSceVideoDecoderArbitration/Export.cpp) (libSceVideoDecoderArbitration) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoDecoderArbitration.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoNativeExtEssential.md b/docs/dev/debt/libSceVideoNativeExtEssential.md new file mode 100644 index 0000000000..f332e1ccbd --- /dev/null +++ b/docs/dev/debt/libSceVideoNativeExtEssential.md @@ -0,0 +1,5 @@ +# libSceVideoNativeExtEssential technical debt + +### Unknown function info + +- [sceVideoNativeExtEssential_CreateDecInstance, sceVideoNativeExtEssential_DestroyDecInstance, sceVideoNativeExtEssential_Finalize, sceVideoNativeExtEssential_Initialize and sceVideoNativeExtEssential_IsInit](../../../core/libs/prx/libSceVideoNativeExtEssential/Export.cpp) (libSceVideoNativeExtEssential) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoNativeExtEssential.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoStreamingEngine.md b/docs/dev/debt/libSceVideoStreamingEngine.md new file mode 100644 index 0000000000..216d7f8beb --- /dev/null +++ b/docs/dev/debt/libSceVideoStreamingEngine.md @@ -0,0 +1,5 @@ +# libSceVideoStreamingEngine technical debt + +### Unknown function info + +- [sceVideoStreamingEngineDestroyMediaKeySystemAccess, sceVideoStreamingEngineMediaKeySessionGenerateRequest, sceVideoStreamingEngineMediaKeySessionGetExpiration, sceVideoStreamingEngineMediaKeySessionGetKeyStatuses, sceVideoStreamingEngineMediaKeySessionGetSessionId, sceVideoStreamingEngineMediaKeySessionLoad, sceVideoStreamingEngineMediaKeySessionSetEventHandler, sceVideoStreamingEngineMediaKeySystemAccessCreateMediaKeys, sceVideoStreamingEngineMediaKeySystemAccessDestroyMediaKeys, sceVideoStreamingEngineMediaKeySystemAccessGetKeySystem, sceVideoStreamingEngineMediaKeySystemAccessSetCdmStorageConfig and sceVideoStreamingEngineMediaKeysCreateSession](../../../core/libs/prx/libSceVideoStreamingEngine/Export.cpp) (libSceVideoStreamingEngine) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoStreamingEngine.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVideoStreamingEngine_sys.md b/docs/dev/debt/libSceVideoStreamingEngine_sys.md new file mode 100644 index 0000000000..d3211d8a90 --- /dev/null +++ b/docs/dev/debt/libSceVideoStreamingEngine_sys.md @@ -0,0 +1,5 @@ +# libSceVideoStreamingEngine_sys technical debt + +### Inventory exports declared elsewhere + +The complete PS5 3.20 export table of this module per the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVideoStreamingEngine_sys.c) (12 names) is already declared under the sibling libSceVideoStreamingEngine in this tree; the duplicate-export rule keeps each name in a single library, so this module keeps its `APS5_DUMMY_FUN` skeleton. Imports resolve through the sibling declarations. diff --git a/docs/dev/debt/libSceVisionManager.md b/docs/dev/debt/libSceVisionManager.md new file mode 100644 index 0000000000..6e8c187895 --- /dev/null +++ b/docs/dev/debt/libSceVisionManager.md @@ -0,0 +1,5 @@ +# libSceVisionManager technical debt + +### Unknown function info + +- [sceVisionManagerApplyCalibrateHmdResult, sceVisionManagerCancelRequest, sceVisionManagerClose, sceVisionManagerFinalize, sceVisionManagerGenerateRegisterUserData, sceVisionManagerGenerateRegisterUserDataFinalize, sceVisionManagerGenerateRegisterUserDataInitialize, sceVisionManagerGetCalibrateTrackingLedInfo, sceVisionManagerGetCalibrateTrackingLedNecessity, sceVisionManagerGetHitCountOfRegisterUserData, sceVisionManagerGetLibraryVersion, sceVisionManagerGetNumOfRegisterUserDataSlot, sceVisionManagerGetRegisterUserDataSize, sceVisionManagerGetRegisterUserDataVersion, sceVisionManagerGetResultOfCalibrateHmd, sceVisionManagerGetResultOfCalibrateStereoCamera, sceVisionManagerGetResultOfCalibrateTrackingLed, sceVisionManagerGetResultOfFindFace, sceVisionManagerGetResultOfFindPad, sceVisionManagerGetResultOfFindPadCorrespondRegion, sceVisionManagerGetResultOfFindUser, sceVisionManagerGetResultOfFindUserInRegion, sceVisionManagerGetResultOfFindUserWithoutIdentify, sceVisionManagerGetResultOfRegisterUserInRegion, sceVisionManagerGetStoreCameraImgYPtr, sceVisionManagerGetWorkingMemorySize, sceVisionManagerInitialize, sceVisionManagerLoadSettings, sceVisionManagerOpen, sceVisionManagerRegistryClear, sceVisionManagerRequestCalibrateHmd, sceVisionManagerRequestCalibrateStereoCamera, sceVisionManagerRequestCalibrateTrackingLed, sceVisionManagerRequestClose, sceVisionManagerRequestFindFace, sceVisionManagerRequestFindPad, sceVisionManagerRequestFindPadCorrespondRegion, sceVisionManagerRequestFindUser, sceVisionManagerRequestFindUserInRegion, sceVisionManagerRequestFindUserWithoutIdentify, sceVisionManagerRequestHeadDetection, sceVisionManagerRequestInitializeRegisterUserData, sceVisionManagerRequestOpen, sceVisionManagerRequestPadTracking, sceVisionManagerRequestRecogUtility, sceVisionManagerRequestRegisterUserInRegion, sceVisionManagerRequestStoreCameraImgY, sceVisionManagerSetCallbackForUpdateCalibrateHmdResult, sceVisionManagerSetCallbackForUpdateCalibrateStereoCameraResult, sceVisionManagerSetCallbackForUpdateCalibrateTrackingLedResult, sceVisionManagerSetCallbackForUpdateCameraFrame, sceVisionManagerSetCallbackForUpdateCtrlCore, sceVisionManagerSetCallbackForUpdateRecog, sceVisionManagerSetCallbackForUpdateRunningStatus, sceVisionManagerSetCallbackForUpdateStoreFrame and sceVisionManagerWaitResponse](../../../core/libs/prx/libSceVisionManager/Export.cpp) (libSceVisionManager) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVisionManager.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceVrServiceDialog.md b/docs/dev/debt/libSceVrServiceDialog.md new file mode 100644 index 0000000000..f6be543e6d --- /dev/null +++ b/docs/dev/debt/libSceVrServiceDialog.md @@ -0,0 +1,5 @@ +# libSceVrServiceDialog technical debt + +### Unknown function info + +- [sceVrServiceDialogClose, sceVrServiceDialogGetResult, sceVrServiceDialogGetStatus, sceVrServiceDialogInitialize, sceVrServiceDialogOpen, sceVrServiceDialogTerminate and sceVrServiceDialogUpdateStatus](../../../core/libs/prx/libSceVrServiceDialog/Export.cpp) (libSceVrServiceDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrServiceDialog.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libmdbg_syscore.md b/docs/dev/debt/libmdbg_syscore.md new file mode 100644 index 0000000000..cb843c4f05 --- /dev/null +++ b/docs/dev/debt/libmdbg_syscore.md @@ -0,0 +1,5 @@ +# libmdbg_syscore technical debt + +### Unknown function info + +- [sceDebugAcquireAndUpdateDebugRegister, sceDebugAttachProcess, sceDebugCancelCoredump, sceDebugClearStepThread, sceDebugCreatePerfScratchDataArea, sceDebugCreatePerfScratchExecutableArea, sceDebugCreateScratchDataArea, sceDebugCreateScratchDataAreaForPrx, sceDebugCreateScratchExecutableArea, sceDebugCreateScratchExecutableAreaForPrx, sceDebugDestroyPerfScratchDataArea, sceDebugDestroyPerfScratchExecutableArea, sceDebugDestroyScratchDataArea, sceDebugDestroyScratchExecutableArea, sceDebugDetachProcess, sceDebugGetApplicationIdByTitleId, sceDebugGetApplicationInfo, sceDebugGetApplicationList, sceDebugGetCrashInfoDetailForCoredump, sceDebugGetCrashInfoForCoredump, sceDebugGetDLLoadFlag, sceDebugGetDataListOfUltQueue, sceDebugGetDebugRegisterStatusMap, sceDebugGetEventList, sceDebugGetEventListForEQueueFd, sceDebugGetEventSubscriptionList, sceDebugGetEventSubscriptionListForEQueueFd, sceDebugGetFiberInfo, sceDebugGetFileInfo, sceDebugGetFileInfoForCoredump, sceDebugGetFileList, sceDebugGetFileListForCoredump, sceDebugGetGpuInfoArea, sceDebugGetJobManagerInfo, sceDebugGetJobManagerSequenceInfo, sceDebugGetJobManagerSequenceList, sceDebugGetModuleInfo, sceDebugGetModuleList, sceDebugGetModuleMetaData, sceDebugGetMonoVMInfo, sceDebugGetMonoVMList, sceDebugGetProcessCoredumpHandlerEventBlob, sceDebugGetProcessCoredumpHandlerEventInfo, sceDebugGetProcessCoredumpHandlerResult, sceDebugGetProcessEventCntlFlag, sceDebugGetProcessInfo, sceDebugGetProcessList, sceDebugGetProcessPropertyForCoredump, sceDebugGetProcessResourceStatCount, sceDebugGetProcessResourceStatData, sceDebugGetProcessTime, sceDebugGetProcessTimeCounter, sceDebugGetSyncExclusiveWaiterList, sceDebugGetSyncObjectData, sceDebugGetSyncObjectList, sceDebugGetSyncWaiterList, sceDebugGetSyncWaiterListForEQueueFd, sceDebugGetSystemStatusBlob, sceDebugGetSystemStatusCount, sceDebugGetThreadInfo, sceDebugGetThreadInfoByIdent, sceDebugGetThreadList, sceDebugGetThreadListAsIdent, sceDebugGetUlObjectList, sceDebugGetUltCondvarInfo, sceDebugGetUltInfo, sceDebugGetUltListOfUltRuntime, sceDebugGetUltMutexInfo, sceDebugGetUltQueueDataResourcePoolInfo, sceDebugGetUltQueueInfo, sceDebugGetUltRuntimeInfo, sceDebugGetUltRwlockInfo, sceDebugGetUltSemaphoreInfo, sceDebugGetUltWaitingQueueResourcePoolInfo, sceDebugGetVirtualMemoryDetailInfoForCoredump, sceDebugGetVirtualMemoryInfo, sceDebugGetVirtualMemoryInfoForCoredump, sceDebugGetWaitingListOfUltCondvar, sceDebugGetWaitingListOfUltMutex, sceDebugGetWaitingListOfUltQueue, sceDebugGetWaitingListOfUltRwlock, sceDebugGetWaitingListOfUltSemaphore, sceDebugGetWorkerThreadListOfUltRuntime, sceDebugInit, sceDebugInitForCoredump, sceDebugInitForTest, sceDebugIpmiGetBlockTimeInfoList, sceDebugIpmiGetBlockedIpcInfo, sceDebugIpmiGetChannelInfo, sceDebugIpmiGetChannelKidList, sceDebugIpmiGetChannelWaitingThreadList, sceDebugIpmiGetClientInfo, sceDebugIpmiGetClientKidList, sceDebugIpmiGetClientKidListByDump, sceDebugIpmiGetClientKidListByServerKid, sceDebugIpmiGetConnectRequestInfoList, sceDebugIpmiGetConnectionInfoList, sceDebugIpmiGetConnectionWaitingThreadListByClientKid, sceDebugIpmiGetConnectionWaitingThreadListBySessionKid, sceDebugIpmiGetDump, sceDebugIpmiGetEncryptedInfoAllForCoredump, sceDebugIpmiGetKidInfoListForCoredump, sceDebugIpmiGetServerDispatchInfo, sceDebugIpmiGetServerInfo, sceDebugIpmiGetServerKidList, sceDebugIpmiGetServerKidListByDump, sceDebugIpmiGetServerWaitingThreadList, sceDebugIpmiGetSessionInfo, sceDebugIpmiGetSessionKidList, sceDebugIpmiGetSessionKidListByServerKid, sceDebugIpmiGetTidListByDump, sceDebugKillApplication, sceDebugKillProcess, sceDebugNoStopChildProcesses, sceDebugNoStopOnDLLoad, sceDebugProcessSpawn, sceDebugReadEvent, sceDebugReadEventForTest, sceDebugReadProcessMemory, sceDebugReadProcessMemoryForSDBGP, sceDebugReadProcessRegister, sceDebugReadProcessRegisterForSDBGP, sceDebugReadProcessResourceStatData, sceDebugReadThreadRegister, sceDebugReadThreadRegisterForSDBGP, sceDebugReleaseDebugRegister, sceDebugResumeApplication, sceDebugResumeProcess, sceDebugResumeThread, sceDebugSetProcessEventCntlFlag, sceDebugSetStepThread, sceDebugSpawnApplication, sceDebugStopChildProcesses, sceDebugStopOnDLLoad, sceDebugSuspendApplication, sceDebugSuspendProcess, sceDebugSuspendThread, sceDebugTriggerCoredump, sceDebugTriggerCoredumpForSystem, sceDebugTriggerVrCaptureDump, sceDebugWriteProcessMemory, sceDebugWriteProcessRegister and sceDebugWriteThreadRegister](../../../core/libs/prx/libmdbg_syscore/Export.cpp) (libmdbg_syscore) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libmdbg_syscore.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified From 1d0451f659167d990e871f4a5f1b6a4645d53f2b Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Wed, 21 Oct 2026 14:12:12 +0300 Subject: [PATCH 133/523] docs(relinker): note that a deferred module the title never loads gets no module_start Co-Authored-By: Claude Opus 5.5 --- docs/dev/debt/relinker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/relinker.md b/docs/dev/debt/relinker.md index 8719280930..9f1e9fae13 100644 --- a/docs/dev/debt/relinker.md +++ b/docs/dev/debt/relinker.md @@ -6,7 +6,7 @@ - Windows [input and output collision checks](../../../core/relinker/main.cpp) use temporary hard links when MinGW reports different permissions for two multiply linked files, as with `.elf` and `.exe` names. The comparison fails if either file is read-only or either parent directory refuses the temporary link, even when the files are distinct. - [Runtime-loaded modules](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) that another module needs are initialized at startup with no start arguments, before the module that needs them is loaded; the console is assumed to start them when that module is loaded. -- [Runtime-loaded modules the executable needs](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) are mapped at startup on Windows and started when the title loads them with `sceKernelLoadStartModule`, with their start arguments, as a Unity player loads `mono-ps4.sprx`. A title that calls such a module before loading it finds its initializers not run; whether the console starts it earlier is not measured. Linux and macOS still start it at startup. +- [Runtime-loaded modules the executable needs](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) are mapped at startup on Windows and started when the title loads them with `sceKernelLoadStartModule`, with their start arguments, as a Unity player loads `mono-ps4.sprx`. A title that calls such a module before loading it finds its initializers not run; whether the console starts it earlier is not measured. A module the executable only imports from and the title never loads itself gets no `module_start` and no initializers at all under this deferral, and nothing reports it. Linux and macOS still start it at startup. - The executable file that [relinker](../../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) generates opens a console when launched, unless it is relinked with `--windows-gui`, which selects the GUI subsystem. A GUI build started from Explorer has no standard output or error handle; the [entry stub](../../../core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp) gives each missing one `anyps5.log` beside the executable, replacing the previous run's file (#3282). Errors about the executable path or about waiting for the previous LoadExec process are written before the log is opened and are lost when the standard error handle is missing. If the log cannot be created, output to the missing handles is lost. - [Guest import registries](../../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) - declared host imports of bundled modules are recorded with their module name and relocation target, even when another guest module exports the same stripped NID. No-op relocations are omitted. Registries describe import bindings, not implementation correctness or runtime reachability. - Converted Windows main executables [omit `DYNAMIC_BASE`](../../../core/relinker/elfpatcher/src/windows/WindowsPeWriter.cpp) by default, not only with `--windows-fixed-base`: on the console the executable lies inside the 40-bit window that the title's own `sceLibcMspaceCreate` checks (#3043). Relocations are kept, so if `0x140000000` is taken Windows relocates the image, possibly above that window. Verified only on Windows 10. From 79a5d6b65aac2ff522ff46d720374396dcc6ec48 Mon Sep 17 00:00:00 2001 From: Savvanis Spyros Date: Wed, 21 Oct 2026 14:13:04 +0300 Subject: [PATCH 134/523] fix(libSceNpManager): refuse an unknown request in sceNpSetGamePresenceOnline After the argument checks, shadPS4 looks the request up and returns REQUEST_NOT_FOUND for one that was never created or was deleted. The library already tracks live requests for sceNpSetTimeout. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceNpManager/Export.cpp | 4 ++++ core/libs/tests/GuestNpManager.cpp | 9 +++++++-- docs/dev/debt/libSceNpManager.md | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNpManager/Export.cpp b/core/libs/prx/libSceNpManager/Export.cpp index f0fe106801..f20b3beeb3 100644 --- a/core/libs/prx/libSceNpManager/Export.cpp +++ b/core/libs/prx/libSceNpManager/Export.cpp @@ -169,6 +169,10 @@ int APS5_VABI sceNpGetAccountId(const NpOnlineId* online_id, uint64_t* account_i int APS5_VABI sceNpSetGamePresenceOnline(int req_id, const NpOnlineId* online_id, uint32_t presence) { (void)presence; if (req_id == 0 || !online_id) return SCE_NP_ERROR_INVALID_ARGUMENT; + { + std::lock_guard lock(requestMutex); + if (!liveRequests.count(req_id)) return SCE_NP_ERROR_REQUEST_NOT_FOUND; + } return SCE_NP_ERROR_SIGNED_OUT; } diff --git a/core/libs/tests/GuestNpManager.cpp b/core/libs/tests/GuestNpManager.cpp index 96b146845e..3e74958669 100644 --- a/core/libs/tests/GuestNpManager.cpp +++ b/core/libs/tests/GuestNpManager.cpp @@ -245,9 +245,14 @@ void CheckOnlineIdQueries() { Require(sceNpGetAccountId(&onlineId, &accountId) == SignedOut, "sceNpGetAccountId must report NP as signed out"); Require(sceNpGetAccountId(nullptr, &accountId) == InvalidArgument, "sceNpGetAccountId must reject a null online id"); Require(sceNpGetAccountId(&onlineId, nullptr) == InvalidArgument, "sceNpGetAccountId must reject a null output"); - Require(sceNpSetGamePresenceOnline(1, &onlineId, 0) == SignedOut, "sceNpSetGamePresenceOnline must report NP as signed out"); + const int presenceRequest = sceNpCreateRequest(); + Require(presenceRequest > 0, "sceNpCreateRequest must return a positive request id"); + Require(sceNpSetGamePresenceOnline(presenceRequest, &onlineId, 0) == SignedOut, "sceNpSetGamePresenceOnline must report NP as signed out"); Require(sceNpSetGamePresenceOnline(0, &onlineId, 0) == InvalidArgument, "sceNpSetGamePresenceOnline must reject request id 0"); - Require(sceNpSetGamePresenceOnline(1, nullptr, 0) == InvalidArgument, "sceNpSetGamePresenceOnline must reject a null online id"); + Require(sceNpSetGamePresenceOnline(presenceRequest, nullptr, 0) == InvalidArgument, "sceNpSetGamePresenceOnline must reject a null online id"); + Require(sceNpSetGamePresenceOnline(0x7fffffff, &onlineId, 0) == RequestNotFound, "sceNpSetGamePresenceOnline must reject a request that was never created"); + Require(sceNpDeleteRequest(presenceRequest) == 0, "sceNpDeleteRequest failed"); + Require(sceNpSetGamePresenceOnline(presenceRequest, &onlineId, 0) == RequestNotFound, "sceNpSetGamePresenceOnline must reject a deleted request"); Require(sceNpRegisterPlusEventCallback(output, nullptr) == 0 && sceNpUnregisterPlusEventCallback() == 0, "the plus event callback must register and unregister"); Require(sceNpUnregisterPlusEventCallback() == 0, "unregistering the plus event callback again must succeed like the premium one"); Require(std::memcmp(output, untouched, sizeof(output)) == 0 && age == 0x5a && userId == 0x5a5a && accountId == 0x5a5a, "a failed query wrote its output"); diff --git a/docs/dev/debt/libSceNpManager.md b/docs/dev/debt/libSceNpManager.md index 41f897369e..594a656555 100644 --- a/docs/dev/debt/libSceNpManager.md +++ b/docs/dev/debt/libSceNpManager.md @@ -18,7 +18,7 @@ - [sceNpGetAccountCountry, sceNpGetAccountDateOfBirth, sceNpGetAccountLanguage, sceNpGetParentalControlInfo](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms taking an online id, not measured on a console. Signatures and checks follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp): a null pointer is `INVALID_ARGUMENT`; no user is signed in, so no online id matches one and the rest return `USER_NOT_FOUND` without writing, like `sceNpCheckNpAvailability`. Request ids are not checked - [sceNpGetUserIdByOnlineId](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(online id, user id output)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L703): a null online id or output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output, as `sceNpGetUserIdByAccountId` does - [sceNpCheckPlus](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(request id, parameter, result)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L246): request id 0 or a null parameter or result returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and every other call returns `SCE_NP_ERROR_SIGNED_OUT` without writing the result, as `sceNpCheckPremium` does. The parameter's size and feature fields, which shadPS4 checks, are not read: the errors the console returns for them are not known -- [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(request id, online id, presence)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L1092-L1096). A null online id or output, or request id 0 for `sceNpSetGamePresenceOnline`, returns `SCE_NP_ERROR_INVALID_ARGUMENT`; otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured +- [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(request id, online id, presence)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L1092-L1096). A null online id or output, or request id 0 for `sceNpSetGamePresenceOnline`, returns `SCE_NP_ERROR_INVALID_ARGUMENT`. `sceNpSetGamePresenceOnline` then returns `SCE_NP_ERROR_REQUEST_NOT_FOUND` for a request that was never created or was deleted, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/c1b26558352b863f80142256e4df38585aa7d727/src/core/libraries/np/np_manager.cpp#L1098-L1104) does. Otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured - [NP reachability callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable. - [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests - [NP state callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the registration rules are [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L857-L901)'s: `sceNpRegisterStateCallbackA` keeps up to 8 callbacks and returns the slot plus 1, a null callback returns `INVALID_ARGUMENT`, a callback already registered `CALLBACK_ALREADY_REGISTERED` and a ninth one `CALLBACK_MAX` (0x8055001D, [np_error.h](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_error.h#L38)); `sceNpUnregisterStateCallbackA` returns `INVALID_ARGUMENT` for an id outside 1..8 and `CALLBACK_NOT_REGISTERED` for an empty slot; the legacy [sceNpRegisterStateCallback](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L981-L1005) keeps one callback with the same errors. Each registration gets `SIGNED_OUT` for the initial user (0x10000000) once, from the next `sceNpCheckCallback` on the calling thread. For the A form this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3959-L3974), whose [sceNpRegisterStateCallbackA](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4021-L4034) marks the registration pending. KytyPS5 never calls the legacy callback ([sceNpRegisterStateCallback](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4009-L4019) stores nothing); it is called as `(user, SIGNED_OUT, nullptr, userdata)`, as in shadPS4, which passes an NpId only for `SIGNED_IN` ([L823](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L823), [L938-L940](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L938-L940)). shadPS4 instead queues `SIGNED_OUT` from the user service login event and delivers it from `sceNpCheckCallback` ([DispatchPendingNpStateCallbacks](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L903-L962)); the reachability callback is not given an initial `UNAVAILABLE`, unlike that function ([L922-L961](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L922-L961)). A callback being delivered when another thread unregisters it is still called once. When and how often the console calls the state callbacks, and the errors, are not measured From 982db9121707655593ca2397bdb97522e0ca04f0 Mon Sep 17 00:00:00 2001 From: NoOverflow Date: Tue, 20 Oct 2026 15:08:23 +0200 Subject: [PATCH 135/523] feat(libSceNpAuth): implement sceNpAuthGetAuthorizationCodeA and sceNpAuthGetIdTokenA Both forward to their V3 counterparts, which share one implementation in shadPS4, and complete libSceNpAuth (14/14). --- core/libs/prx/libSceNpAuth/Export.cpp | 6 ++--- core/libs/tests/GuestNpAuth.cpp | 34 +++++++++++++++++++++++++++ docs/dev/debt/libSceNpAuth.md | 4 +--- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceNpAuth/Export.cpp b/core/libs/prx/libSceNpAuth/Export.cpp index 2e9ece6558..bd2d4b0e95 100644 --- a/core/libs/prx/libSceNpAuth/Export.cpp +++ b/core/libs/prx/libSceNpAuth/Export.cpp @@ -145,13 +145,11 @@ int APS5_VABI sceNpAuthWaitAsync(int req_id, int* result) { } int APS5_VABI sceNpAuthGetAuthorizationCodeA(int req_id, const void* param, void* auth_code, int* issuer_id) { - NotImplemented_nid_no_patch(__func__); - return 0; + return sceNpAuthGetAuthorizationCodeV3(req_id, param, auth_code, issuer_id); } int APS5_VABI sceNpAuthGetIdTokenA(int req_id, const void* param, void* token) { - NotImplemented_nid_no_patch(__func__); - return 0; + return sceNpAuthGetIdTokenV3(req_id, param, token); } } diff --git a/core/libs/tests/GuestNpAuth.cpp b/core/libs/tests/GuestNpAuth.cpp index e9352d3f8c..2ab80ba71c 100644 --- a/core/libs/tests/GuestNpAuth.cpp +++ b/core/libs/tests/GuestNpAuth.cpp @@ -9,9 +9,11 @@ int APS5_VABI sceNpAuthCreateAsyncRequest(const void* param); int APS5_VABI sceNpAuthCreateRequest(void); int APS5_VABI sceNpAuthDeleteRequest(int req_id); int APS5_VABI sceNpAuthGetAuthorizationCode(int req_id, const void* param, void* auth_code); +int APS5_VABI sceNpAuthGetAuthorizationCodeA(int req_id, const void* param, void* auth_code, int* issuer_id); int APS5_VABI sceNpAuthGetAuthorizationCodeV3(int req_id, const void* param, void* auth_code, int* issuer_id); int APS5_VABI sceNpAuthGetAuthorizedAppCode(); int APS5_VABI sceNpAuthGetIdToken(int req_id, const void* param, void* id_token); +int APS5_VABI sceNpAuthGetIdTokenA(int req_id, const void* param, void* id_token); int APS5_VABI sceNpAuthGetIdTokenV3(int req_id, const void* param, void* id_token); int APS5_VABI sceNpAuthPollAsync(int req_id, int* result); int APS5_VABI sceNpAuthSetTimeout(int req_id, int resolve_retry, uint32_t resolve_timeout, uint32_t connect_timeout, uint32_t send_timeout, uint32_t receive_timeout); @@ -121,6 +123,38 @@ int main() { Require(sceNpAuthWaitAsync(abortedReq, &abortedWait) == 0); Require(abortedWait == aborted); + const int syncReqA = sceNpAuthCreateRequest(); + Require(sceNpAuthGetIdTokenA(syncReqA, nullptr, token) == invalidArgument); + Require(sceNpAuthGetIdTokenA(syncReqA, param, nullptr) == invalidArgument); + Require(sceNpAuthGetIdTokenA(syncReqA, param, token) == signedOut); + Require(sceNpAuthGetIdTokenA(syncReqA, param, token) == invalidArgument); + Require(sceNpAuthGetIdTokenA(unknownReq, param, token) == requestNotFound); + + const int syncReqAuthA = sceNpAuthCreateRequest(); + Require(sceNpAuthGetAuthorizationCodeA(syncReqAuthA, nullptr, authCode, &issuerId) == invalidArgument); + Require(sceNpAuthGetAuthorizationCodeA(syncReqAuthA, param, nullptr, &issuerId) == invalidArgument); + Require(sceNpAuthGetAuthorizationCodeA(syncReqAuthA, param, authCode, nullptr) == signedOut); + Require(sceNpAuthGetAuthorizationCodeA(syncReqAuthA, param, authCode, &issuerId) == invalidArgument); + Require(sceNpAuthGetAuthorizationCodeA(unknownReq, param, authCode, &issuerId) == requestNotFound); + + const int asyncReqA = sceNpAuthCreateAsyncRequest(nullptr); + Require(sceNpAuthGetIdTokenA(asyncReqA, param, token) == 0); + int asyncPollA = 0; + Require(sceNpAuthPollAsync(asyncReqA, &asyncPollA) == pollRunning); + Require(sceNpAuthPollAsync(asyncReqA, &asyncPollA) == 0); + Require(asyncPollA == signedOut); + + const int asyncReqAuthA = sceNpAuthCreateAsyncRequest(nullptr); + Require(sceNpAuthGetAuthorizationCodeA(asyncReqAuthA, param, authCode, &issuerId) == 0); + int asyncWaitA = 0; + Require(sceNpAuthWaitAsync(asyncReqAuthA, &asyncWaitA) == 0); + Require(asyncWaitA == signedOut); + + const int abortedReqA = sceNpAuthCreateAsyncRequest(nullptr); + Require(sceNpAuthAbortRequest(abortedReqA) == 0); + Require(sceNpAuthGetIdTokenA(abortedReqA, param, token) == aborted); + Require(sceNpAuthGetAuthorizationCodeA(abortedReqA, param, authCode, &issuerId) == aborted); + Require(sceNpAuthDeleteRequest(syncReq) == 0); Require(sceNpAuthGetIdTokenV3(syncReq, param, token) == requestNotFound); Require(sceNpAuthWaitAsync(syncReq, &waitResult) == requestNotFound); diff --git a/docs/dev/debt/libSceNpAuth.md b/docs/dev/debt/libSceNpAuth.md index bb10e4b7d8..cf49ab0a2e 100644 --- a/docs/dev/debt/libSceNpAuth.md +++ b/docs/dev/debt/libSceNpAuth.md @@ -2,8 +2,6 @@ ### Unknown function info -- [sceNpAuthGetAuthorizationCodeA](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpAuth.c); its signature follows [shadPS4 `np_auth.cpp`](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L155-L190), is not measured on a console, and the throwing declaration only resolves the import -- [sceNpAuthGetIdTokenA](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpAuth.c); its prototype follows [shadPS4 `np_auth.cpp`](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L340-L375), is not measured on a console, and the throwing declaration only resolves the import - [sceNpAuthGetAuthorizedAppCode](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - declared without parameters and returns `SCE_NP_ERROR_SIGNED_OUT` without reading them, as there is no PSN sign-in; the request parameter and code output are not known. Minecraft (PPSA17221) imports it, fails to load without it, and after its sign-in dialog treats the signed-out code as offline play. -- [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCode, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdToken, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4); async requests defer `SCE_NP_ERROR_SIGNED_OUT` to Poll/Wait while sync requests fail immediately; PSN is not contacted. Parameter size validation and PS5 behavior are unverified +- [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCode, sceNpAuthGetAuthorizationCodeA, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdToken, sceNpAuthGetIdTokenA, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4); the A calls forward to their V3 counterparts, which share one implementation in shadPS4; async requests defer `SCE_NP_ERROR_SIGNED_OUT` to Poll/Wait while sync requests fail immediately; PSN is not contacted. Parameter size validation and PS5 behavior are unverified - [sceNpAuthSetTimeout](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - imported by PPSA01317, which fails to load without it. The signature `(reqId, resolveRetry, resolveTimeout, connTimeout, sendTimeout, recvTimeout)` follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4), which only stubs it. An unknown request ID returns `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` like the other request calls; otherwise it returns OK and keeps nothing, as no request reaches the network the timeouts would bound. Argument checks of the console are unverified From 488da1774da6517c1c63ca2572b0bdf019c885ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Wed, 21 Oct 2026 13:28:34 +0200 Subject: [PATCH 136/523] fix(libSceNpAuth): indent the A forwards and pin the shadPS4 links in the debt entry Co-Authored-By: Claude --- core/libs/prx/libSceNpAuth/Export.cpp | 4 ++-- docs/dev/debt/libSceNpAuth.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNpAuth/Export.cpp b/core/libs/prx/libSceNpAuth/Export.cpp index bd2d4b0e95..7ce7edb5ee 100644 --- a/core/libs/prx/libSceNpAuth/Export.cpp +++ b/core/libs/prx/libSceNpAuth/Export.cpp @@ -145,11 +145,11 @@ int APS5_VABI sceNpAuthWaitAsync(int req_id, int* result) { } int APS5_VABI sceNpAuthGetAuthorizationCodeA(int req_id, const void* param, void* auth_code, int* issuer_id) { - return sceNpAuthGetAuthorizationCodeV3(req_id, param, auth_code, issuer_id); + return sceNpAuthGetAuthorizationCodeV3(req_id, param, auth_code, issuer_id); } int APS5_VABI sceNpAuthGetIdTokenA(int req_id, const void* param, void* token) { - return sceNpAuthGetIdTokenV3(req_id, param, token); + return sceNpAuthGetIdTokenV3(req_id, param, token); } } diff --git a/docs/dev/debt/libSceNpAuth.md b/docs/dev/debt/libSceNpAuth.md index cf49ab0a2e..2ad98e9bdd 100644 --- a/docs/dev/debt/libSceNpAuth.md +++ b/docs/dev/debt/libSceNpAuth.md @@ -3,5 +3,5 @@ ### Unknown function info - [sceNpAuthGetAuthorizedAppCode](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - declared without parameters and returns `SCE_NP_ERROR_SIGNED_OUT` without reading them, as there is no PSN sign-in; the request parameter and code output are not known. Minecraft (PPSA17221) imports it, fails to load without it, and after its sign-in dialog treats the signed-out code as offline play. -- [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCode, sceNpAuthGetAuthorizationCodeA, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdToken, sceNpAuthGetIdTokenA, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4); the A calls forward to their V3 counterparts, which share one implementation in shadPS4; async requests defer `SCE_NP_ERROR_SIGNED_OUT` to Poll/Wait while sync requests fail immediately; PSN is not contacted. Parameter size validation and PS5 behavior are unverified +- [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCode, sceNpAuthGetAuthorizationCodeA, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdToken, sceNpAuthGetIdTokenA, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp) (PS4); the A calls forward to their V3 counterparts, which in shadPS4 ([AuthorizationCode A](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L189-L193), [V3](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L195-L199); [IdToken A](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L375-L378), [V3](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L380-L383)) call the same helper and differ only in a version flag; async requests defer `SCE_NP_ERROR_SIGNED_OUT` to Poll/Wait while sync requests fail immediately; PSN is not contacted. Parameter size validation and PS5 behavior are unverified - [sceNpAuthSetTimeout](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - imported by PPSA01317, which fails to load without it. The signature `(reqId, resolveRetry, resolveTimeout, connTimeout, sendTimeout, recvTimeout)` follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4), which only stubs it. An unknown request ID returns `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` like the other request calls; otherwise it returns OK and keeps nothing, as no request reaches the network the timeouts would bound. Argument checks of the console are unverified From 2050c88eed668af6caff60746aa9963f461c385f Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Thu, 22 Oct 2026 00:35:08 +1300 Subject: [PATCH 137/523] test(agc): unmap only the no-window test's own tail The guest block's tail trim unmapped begin + bytes + UnitBytes - aligned bytes from the end of the block, which runs past the mapping it made. Trim begin + UnitBytes - aligned, as the sibling tests do. --- .../tests/execution/StorageWriteBackNoWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/StorageWriteBackNoWindow.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/StorageWriteBackNoWindow.cpp index acde6de596..de8bb3a7d9 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/StorageWriteBackNoWindow.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/StorageWriteBackNoWindow.cpp @@ -70,7 +70,7 @@ public: const auto begin = reinterpret_cast(mapped); const auto aligned = (begin + UnitBytes - 1) & ~(static_cast(UnitBytes) - 1); if (aligned != begin) munmap(mapped, aligned - begin); - if (aligned + bytes != begin + bytes + UnitBytes) munmap(reinterpret_cast(aligned + bytes), begin + bytes + UnitBytes - aligned); + if (aligned + bytes != begin + bytes + UnitBytes) munmap(reinterpret_cast(aligned + bytes), begin + UnitBytes - aligned); block = reinterpret_cast(aligned); if (AgcDriver::GuestMemory::WriteWatched()) GuestWriteWatch::GuestWriteWatchRegister_nid_postfix(block, bytes); } From a936eadd6a053f6ebec8de1377b899aaab9d15b3 Mon Sep 17 00:00:00 2001 From: thuanquang Date: Wed, 21 Oct 2026 19:08:42 +0700 Subject: [PATCH 138/523] test(libc): drop the wcsncat_s overlap case that pins a false positive The console rejects a copy only when the written range intersects the read range; this call writes indices 3-4 and reads 1-2, so the console copies and returns 0 (oneandonlydean, reading libc.prx). Main returns 22, and the row would have pinned that. Co-Authored-By: Claude --- core/libs/tests/GuestWideStrings.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/libs/tests/GuestWideStrings.cpp b/core/libs/tests/GuestWideStrings.cpp index 711cc31a6d..7294832158 100644 --- a/core/libs/tests/GuestWideStrings.cpp +++ b/core/libs/tests/GuestWideStrings.cpp @@ -224,8 +224,6 @@ int main() { wmemset_nid_postfix(strBuf, u'x', 4); require(wcsncat_s_nid_postfix(strBuf, 4, u"a", 1) == 34 && strBuf[0] == 0); wcsncpy_s_nid_postfix(strBuf, 16, u"abc", 3); - require(wcsncat_s_nid_postfix(strBuf, 16, strBuf + 1, 2) == 22 && strBuf[0] == 0); - wcsncpy_s_nid_postfix(strBuf, 16, u"abc", 3); require(wcscat_s_nid_postfix(strBuf, 16, u"def") == 0 && same(strBuf, u"abcdef", 7)); wcsncpy_s_nid_postfix(strBuf, 8, u"12345", 5); require(wcscat_s_nid_postfix(strBuf, 8, u"6789") == 34 && strBuf[0] == 0); From b8297adab94813820d459687ba609495eb6780ab Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Fri, 16 Oct 2026 00:15:13 +0300 Subject: [PATCH 139/523] feat(agc): add fragment-preserving R_X and Z_X layouts --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 23 +++ .../include/MultisampleSwizzleEquations.hpp | 53 +++++ .../include/MultisampleTextureLayout.hpp | 54 ++++++ .../Graphics/src/MultisampleTextureLayout.cpp | 120 ++++++++++++ .../tests/MultisampleTextureLayout.cpp | 182 ++++++++++++++++++ docs/dev/debt/libSceAgcDriver.md | 1 + 6 files changed, 433 insertions(+) create mode 100644 core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleSwizzleEquations.hpp create mode 100644 core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp create mode 100644 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTextureLayout.cpp create mode 100644 core/libs/prx/libSceAgcDriver/tests/MultisampleTextureLayout.cpp diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 177d7b1d5c..7efd108e1d 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -334,6 +334,7 @@ set(agcDriverSources ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/TextureFormat.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/DccMetadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/TextureTiling.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/MultisampleTextureLayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/TextureDetiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/MultisampleReadback.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Texture.cpp @@ -1846,6 +1847,28 @@ endif() set_target_properties(agc_driver_graphics_tests PROPERTIES CXX_EXTENSIONS OFF) configure_windows_unwind(agc_driver_graphics_tests) +add_test_executable(agc_driver_msaa_layout_tests EXCLUDE_FROM_ALL + tests/MultisampleTextureLayout.cpp + Graphics/src/MultisampleTextureLayout.cpp +) +target_include_directories(agc_driver_msaa_layout_tests PRIVATE + ${LIBS_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/core/shader/recompiler + ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR} +) +target_link_libraries(agc_driver_msaa_layout_tests PRIVATE libc) +set_target_properties(agc_driver_msaa_layout_tests PROPERTIES CXX_EXTENSIONS OFF) +configure_windows_unwind(agc_driver_msaa_layout_tests) +if(BUILD_TESTING) + set_target_properties(agc_driver_msaa_layout_tests PROPERTIES EXCLUDE_FROM_ALL OFF) + add_test(NAME agc_driver_msaa_layout COMMAND agc_driver_msaa_layout_tests) + set_tests_properties(agc_driver_msaa_layout PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(agc_driver_msaa_layout PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() + foreach(agcVertexRangeShader IN ITEMS VertexRange.vert VertexRangeProducer.frag) string(REPLACE "." "_" agcVertexRangeName "${agcVertexRangeShader}") string(TOUPPER "${agcVertexRangeName}_SPV" agcVertexRangeSymbol) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleSwizzleEquations.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleSwizzleEquations.hpp new file mode 100644 index 0000000000..358d893eb8 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleSwizzleEquations.hpp @@ -0,0 +1,53 @@ +#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_MULTISAMPLESWIZZLEEQUATIONS_HPP +#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_MULTISAMPLESWIZZLEEQUATIONS_HPP + +#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp" +#include +#include + +namespace AgcDriver::Graphics { + +struct MultisampleSwizzleEquation { + TextureTileMode tileMode; + std::uint32_t elementBytes; + std::uint32_t fragments; + std::array bits; +}; + +inline constexpr MultisampleSwizzleEquation kMultisampleSwizzleEquations[] = { + {TextureTileMode::kZ64KBX, 1u, 2u, {0x0001000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000000000004ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000100000ull, 0x0000000000400000ull, 0x0000000000000040ull, 0x0000000000800000ull}}, + {TextureTileMode::kZ64KBX, 2u, 2u, {0x0000000000000000ull, 0x0001000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000000000004ull, 0x0000000000040000ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000400000ull, 0x0000000000000040ull}}, + {TextureTileMode::kZ64KBX, 4u, 2u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0001000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000000000004ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000400000ull, 0x0000000000840000ull}}, + {TextureTileMode::kZ64KBX, 1u, 4u, {0x0001000000000000ull, 0x0002000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000000000004ull, 0x0000000000040000ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000400000ull, 0x0000000000000040ull}}, + {TextureTileMode::kZ64KBX, 2u, 4u, {0x0000000000000000ull, 0x0001000000000000ull, 0x0002000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000000000004ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0000000000000040ull, 0x0000000000440000ull}}, + {TextureTileMode::kZ64KBX, 4u, 4u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0001000000000000ull, 0x0002000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000440000ull, 0x0000000000800004ull}}, + {TextureTileMode::kZ64KBX, 1u, 8u, {0x0001000000000000ull, 0x0002000000000000ull, 0x0004000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000000000004ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0000000800400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000400000ull, 0x0000000000840000ull}}, + {TextureTileMode::kZ64KBX, 2u, 8u, {0x0000000000000000ull, 0x0001000000000000ull, 0x0002000000000000ull, 0x0004000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0000000800400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000440000ull, 0x0000000000800004ull}}, + {TextureTileMode::kZ64KBX, 4u, 8u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0001000000000000ull, 0x0002000000000000ull, 0x0004000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0000000000440020ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0000000000400004ull, 0x0000000000820000ull}}, + {TextureTileMode::kR64KBX, 1u, 2u, {0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000020000ull, 0x0000000000010000ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000400000ull, 0x0000000000000040ull, 0x0000000000800000ull, 0x0001000001000000ull}}, + {TextureTileMode::kR64KBX, 2u, 2u, {0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000010000ull, 0x0000000000020000ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000100000ull, 0x0000000000000040ull, 0x0000000000400000ull, 0x0001000000800000ull}}, + {TextureTileMode::kR64KBX, 4u, 2u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000010000ull, 0x0000000000020000ull, 0x0000000000040000ull, 0x0000000000000004ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0000000000400000ull, 0x0001000000800000ull}}, + {TextureTileMode::kR64KBX, 8u, 2u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000020000ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0001000000400000ull}}, + {TextureTileMode::kR64KBX, 16u, 2u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0001000000400020ull, 0x0000000000040000ull, 0x0000000000000004ull, 0x0000000000080000ull, 0x0000000000000010ull}}, + {TextureTileMode::kR64KBX, 1u, 4u, {0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000020000ull, 0x0000000000010000ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000400000ull, 0x0000000000000040ull, 0x0001000000800000ull, 0x0002000001000000ull}}, + {TextureTileMode::kR64KBX, 2u, 4u, {0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000010000ull, 0x0000000000020000ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000100000ull, 0x0000000000000040ull, 0x0001000000400000ull, 0x0002000000800000ull}}, + {TextureTileMode::kR64KBX, 4u, 4u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000010000ull, 0x0000000000020000ull, 0x0000000000040000ull, 0x0000000000000004ull, 0x0000000800080008ull, 0x0000000400100010ull, 0x0000000200200040ull, 0x0000000100400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0001000000400000ull, 0x0002000000800000ull}}, + {TextureTileMode::kR64KBX, 8u, 4u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000020000ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0002000000200040ull, 0x0000000100400020ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0001000000400000ull}}, + {TextureTileMode::kR64KBX, 16u, 4u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000200080008ull, 0x0000000100100010ull, 0x0002000000200040ull, 0x0001000000400020ull, 0x0000000000040000ull, 0x0000000000000004ull, 0x0000000000080000ull, 0x0000000000000010ull}}, + {TextureTileMode::kR64KBX, 1u, 8u, {0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000020000ull, 0x0000000000010000ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0000000800400020ull, 0x0000000000400000ull, 0x0001000000800000ull, 0x0002000001000000ull, 0x0004000002000000ull}}, + {TextureTileMode::kR64KBX, 2u, 8u, {0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000010000ull, 0x0000000000020000ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0000000800400020ull, 0x0000000000100000ull, 0x0001000000400000ull, 0x0002000000800000ull, 0x0004000001000000ull}}, + {TextureTileMode::kR64KBX, 4u, 8u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull, 0x0000000000010000ull, 0x0000000000020000ull, 0x0000000000040000ull, 0x0000000000000004ull, 0x0000000400080008ull, 0x0000000200100010ull, 0x0000000100200040ull, 0x0004000000400020ull, 0x0000000000080000ull, 0x0000000000000010ull, 0x0001000000400000ull, 0x0002000000800000ull}}, + {TextureTileMode::kR64KBX, 8u, 8u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000000004ull, 0x0000000000020000ull, 0x0000000200080008ull, 0x0000000100100010ull, 0x0004000000200040ull, 0x0002000000400020ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0000000000100000ull, 0x0001000000400000ull}}, + {TextureTileMode::kR64KBX, 16u, 8u, {0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000000ull, 0x0000000000000001ull, 0x0000000000010000ull, 0x0000000000000002ull, 0x0000000000020000ull, 0x0000000200080008ull, 0x0000000100100010ull, 0x0004000000200040ull, 0x0002000000400020ull, 0x0000000000000004ull, 0x0000000000040000ull, 0x0000000000000008ull, 0x0001000000400000ull}}, +}; + +constexpr const MultisampleSwizzleEquation* FindMultisampleSwizzleEquation(TextureTileMode tileMode, std::uint32_t elementBytes, std::uint32_t fragments) { + for (const auto& equation : kMultisampleSwizzleEquations) { + if (equation.tileMode == tileMode && equation.elementBytes == elementBytes && equation.fragments == fragments) return &equation; + } + return nullptr; +} + +} + +#endif diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp new file mode 100644 index 0000000000..a7960c43dc --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp @@ -0,0 +1,54 @@ +#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_MULTISAMPLETEXTURELAYOUT_HPP +#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_MULTISAMPLETEXTURELAYOUT_HPP + +#include "prx/libSceAgcDriver/Graphics/include/GuestTextureResource.hpp" +#include +#include +#include +#include +#include + +namespace AgcDriver::Graphics { + +class MultisampleTextureLayout { +public: + static constexpr std::uint32_t Alignment = 65536u; + + MultisampleTextureLayout(TextureTileMode tileMode, std::uint32_t elementBytes, std::uint32_t fragments, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t pipeBankXor = 0); + std::uint64_t Bytes() const { return sliceBytes * layers; } + std::uint64_t SliceBytes() const { return sliceBytes; } + std::uint64_t LinearBytes() const { return static_cast(width) * height * layers * fragments * elementBytes; } + std::uint32_t Pitch() const { return pitch; } + std::uint32_t PaddedHeight() const { return paddedHeight; } + std::uint32_t BlockWidth() const { return blockWidth; } + std::uint32_t BlockHeight() const { return blockHeight; } + const std::array& InverseBlockEquation() const { return inverseBlockEquation; } + std::uint64_t Offset(std::uint32_t x, std::uint32_t y, std::uint32_t layer, std::uint32_t fragment) const; + void Detile(std::span source, std::span destination) const; + void Tile(std::span source, std::span destination) const; + +private: + std::uint64_t offset(std::uint32_t x, std::uint32_t y, std::uint32_t layer, std::uint32_t fragment) const; + void transfer(std::span source, std::span destination, bool retile) const; + + std::uint32_t elementBytes; + std::uint32_t fragments; + std::uint32_t width; + std::uint32_t height; + std::uint32_t layers; + std::uint32_t pipeBankXor; + std::uint32_t blockWidth; + std::uint32_t blockHeight; + std::uint32_t pitch; + std::uint32_t paddedHeight; + std::uint64_t sliceBytes; + std::array inverseBlockEquation; + std::vector xOffsets; + std::vector yOffsets; + std::vector layerOffsets; + std::vector fragmentOffsets; +}; + +} + +#endif diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTextureLayout.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTextureLayout.cpp new file mode 100644 index 0000000000..96a5cbc02a --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTextureLayout.cpp @@ -0,0 +1,120 @@ +#include "prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp" +#include "prx/libSceAgcDriver/Graphics/include/MultisampleSwizzleEquations.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Context.hpp" +#include +#include +#include +#include + +namespace AgcDriver::Graphics { +namespace { + +std::array inverseBlock(const MultisampleSwizzleEquation& equation, std::uint32_t byteBits, std::uint32_t fragmentBits, std::uint32_t widthBits) { + std::array rows{}, inverse{}; + for (std::uint32_t input = 0; input < 16u; ++input) { + std::uint32_t address = 0u; + if (input < byteBits) address = 1u << input; + else { + const auto coordinateBit = input < byteBits + fragmentBits ? 48u + input - byteBits : input < byteBits + fragmentBits + widthBits ? input - byteBits - fragmentBits : 16u + input - byteBits - fragmentBits - widthBits; + const auto coordinate = std::uint64_t{1} << coordinateBit; + for (std::uint32_t output = 0; output < 16u; ++output) address |= static_cast(std::popcount(coordinate & equation.bits[output]) & 1u) << output; + } + for (std::uint32_t output = 0; output < 16u; ++output) rows[output] |= ((address >> output) & 1u) << input; + inverse[input] = 1u << input; + } + for (std::uint32_t input = 0; input < 16u; ++input) { + auto pivot = input; + while (pivot < 16u && (rows[pivot] & (1u << input)) == 0u) ++pivot; + Require(pivot < 16u, "multisample block address equation is not invertible"); + std::swap(rows[input], rows[pivot]); + std::swap(inverse[input], inverse[pivot]); + for (std::uint32_t row = 0; row < 16u; ++row) { + if (row == input || (rows[row] & (1u << input)) == 0u) continue; + rows[row] ^= rows[input]; + inverse[row] ^= inverse[input]; + } + } + return inverse; +} + +std::vector axisOffsets(const MultisampleSwizzleEquation& equation, std::uint32_t count, std::uint32_t shift) { + std::vector offsets(count); + for (std::uint32_t value = 0; value < count; ++value) { + const auto coordinate = static_cast(value) << shift; + for (std::uint32_t bit = 0; bit < equation.bits.size(); ++bit) { + offsets[value] |= static_cast(std::popcount(coordinate & equation.bits[bit]) & 1u) << bit; + } + } + return offsets; +} + +} + +MultisampleTextureLayout::MultisampleTextureLayout(TextureTileMode tileMode, std::uint32_t elementBytes, std::uint32_t fragments, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t pipeBankXor) : elementBytes(elementBytes), fragments(fragments), width(width), height(height), layers(layers), pipeBankXor(pipeBankXor) { + Require(width != 0 && height != 0 && width <= 16384u && height <= 16384u && layers != 0 && layers <= 8192u, "invalid multisampled surface extent or layer count"); + Require(std::has_single_bit(elementBytes) && elementBytes <= 16u, "unsupported multisampled surface element size"); + Require(fragments == 2u || fragments == 4u || fragments == 8u, "unsupported multisampled surface fragment count"); + Require((pipeBankXor & ~0xcf00u) == 0, "multisampled surface pipe/bank XOR uses bits outside its 16-pipe swizzle"); + Require(tileMode != TextureTileMode::kZ64KBX || elementBytes <= 4u, "multisampled Z surface element size exceeds 32 bits"); + const auto* equation = FindMultisampleSwizzleEquation(tileMode, elementBytes, fragments); + Require(equation != nullptr, "unsupported multisampled surface tile mode"); + + const auto fragmentBits = static_cast(std::countr_zero(fragments)); + const auto elementBits = 16u - static_cast(std::countr_zero(elementBytes)) - fragmentBits; + const auto widthBits = (elementBits + ((fragmentBits & 1u) == 0 ? 1u : 0u)) / 2u; + blockWidth = 1u << widthBits; + blockHeight = 1u << (elementBits - widthBits); + inverseBlockEquation = inverseBlock(*equation, static_cast(std::countr_zero(elementBytes)), fragmentBits, widthBits); + pitch = (width + blockWidth - 1u) / blockWidth * blockWidth; + paddedHeight = (height + blockHeight - 1u) / blockHeight * blockHeight; + sliceBytes = static_cast(pitch) * paddedHeight * fragments * elementBytes; + Require(sliceBytes <= std::numeric_limits::max() / layers, "multisampled surface size overflows"); + xOffsets = axisOffsets(*equation, width, 0u); + yOffsets = axisOffsets(*equation, height, 16u); + layerOffsets = axisOffsets(*equation, layers, 32u); + fragmentOffsets = axisOffsets(*equation, fragments, 48u); +} + +std::uint64_t MultisampleTextureLayout::offset(std::uint32_t x, std::uint32_t y, std::uint32_t layer, std::uint32_t fragment) const { + const auto block = static_cast(y / blockHeight) * (pitch / blockWidth) + x / blockWidth; + const auto withinBlock = xOffsets[x] ^ yOffsets[y] ^ layerOffsets[layer] ^ fragmentOffsets[fragment] ^ pipeBankXor; + return static_cast(layer) * sliceBytes + block * Alignment + withinBlock; +} + +std::uint64_t MultisampleTextureLayout::Offset(std::uint32_t x, std::uint32_t y, std::uint32_t layer, std::uint32_t fragment) const { + Require(x < width && y < height && layer < layers && fragment < fragments, "multisampled surface coordinate is outside its extent"); + return offset(x, y, layer, fragment); +} + +void MultisampleTextureLayout::transfer(std::span source, std::span destination, bool retile) const { + const auto sourceBytes = retile ? LinearBytes() : Bytes(); + const auto destinationBytes = retile ? Bytes() : LinearBytes(); + Require(source.size() >= sourceBytes && destination.size() >= destinationBytes, "multisampled surface transfer buffer is too small"); + const auto sourceAddress = reinterpret_cast(source.data()); + const auto destinationAddress = reinterpret_cast(destination.data()); + Require(sourceAddress <= std::numeric_limits::max() - sourceBytes && destinationAddress <= std::numeric_limits::max() - destinationBytes, "multisampled surface transfer range overflows"); + Require(sourceAddress + sourceBytes <= destinationAddress || destinationAddress + destinationBytes <= sourceAddress, "multisampled surface transfer buffers overlap"); + + std::uint64_t linear = 0; + for (std::uint32_t layer = 0; layer < layers; ++layer) { + for (std::uint32_t y = 0; y < height; ++y) { + for (std::uint32_t x = 0; x < width; ++x) { + for (std::uint32_t fragment = 0; fragment < fragments; ++fragment) { + const auto tiled = offset(x, y, layer, fragment); + std::memcpy(destination.data() + (retile ? tiled : linear), source.data() + (retile ? linear : tiled), elementBytes); + linear += elementBytes; + } + } + } + } +} + +void MultisampleTextureLayout::Detile(std::span source, std::span destination) const { + transfer(source, destination, false); +} + +void MultisampleTextureLayout::Tile(std::span source, std::span destination) const { + transfer(source, destination, true); +} + +} diff --git a/core/libs/prx/libSceAgcDriver/tests/MultisampleTextureLayout.cpp b/core/libs/prx/libSceAgcDriver/tests/MultisampleTextureLayout.cpp new file mode 100644 index 0000000000..2986c79da0 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/MultisampleTextureLayout.cpp @@ -0,0 +1,182 @@ +#include "prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Context.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace AgcDriver::Graphics; + +namespace { + +struct ReferenceCase { + TextureTileMode tileMode; + std::uint32_t elementBytes; + std::uint32_t fragments; + std::uint32_t blockWidth; + std::uint32_t blockHeight; + std::uint32_t pitch; + std::uint32_t paddedHeight; + std::uint64_t sliceBytes; + std::array addresses; +}; + +constexpr std::array, 7> kCoordinates{{ + {0u, 0u, 0u}, {128u, 66u, 16u}, {64u, 32u, 1u}, {32u, 64u, 2u}, + {63u, 31u, 7u}, {127u, 65u, 15u}, {1u, 1u, 3u} +}}; + +constexpr ReferenceCase kReferences[] = { + {TextureTileMode::kR64KBX, 1u, 2u, 128u, 256u, 256u, 256u, 131072ull, {{0ull, 2201608ull, 141312ull, 300032ull, 919295ull, 2013271ull, 396305ull}}}, + {TextureTileMode::kR64KBX, 1u, 4u, 128u, 128u, 256u, 128u, 131072ull, {{0ull, 2185224ull, 174080ull, 316416ull, 919295ull, 1996887ull, 429073ull}}}, + {TextureTileMode::kR64KBX, 1u, 8u, 64u, 128u, 192u, 128u, 196608ull, {{0ull, 3291144ull, 279552ull, 422400ull, 1413119ull, 3061847ull, 640529ull}}}, + {TextureTileMode::kR64KBX, 2u, 2u, 128u, 128u, 256u, 128u, 131072ull, {{0ull, 2213920ull, 141312ull, 312320ull, 923390ull, 2025630ull, 396306ull}}}, + {TextureTileMode::kR64KBX, 2u, 4u, 128u, 64u, 256u, 128u, 262144ull, {{0ull, 4392992ull, 305152ull, 689152ull, 1840894ull, 4073630ull, 822290ull}}}, + {TextureTileMode::kR64KBX, 2u, 8u, 64u, 64u, 192u, 128u, 393216ull, {{0ull, 6621216ull, 476160ull, 999936ull, 2793470ull, 6195358ull, 1230354ull}}}, + {TextureTileMode::kR64KBX, 4u, 2u, 64u, 128u, 192u, 128u, 196608ull, {{0ull, 3328032ull, 264192ull, 443392ull, 1390332ull, 3074204ull, 592916ull}}}, + {TextureTileMode::kR64KBX, 4u, 4u, 64u, 64u, 192u, 128u, 393216ull, {{0ull, 6621216ull, 493568ull, 1016832ull, 2766588ull, 6170780ull, 1215508ull}}}, + {TextureTileMode::kR64KBX, 4u, 8u, 32u, 64u, 160u, 128u, 655360ull, {{0ull, 11077664ull, 820224ull, 1737216ull, 4667388ull, 10365084ull, 2002452ull}}}, + {TextureTileMode::kR64KBX, 8u, 2u, 64u, 64u, 192u, 128u, 393216ull, {{0ull, 6621312ull, 460800ull, 984064ull, 2782968ull, 6170744ull, 1182744ull}}}, + {TextureTileMode::kR64KBX, 8u, 4u, 64u, 32u, 192u, 96u, 589824ull, {{0ull, 9963648ull, 855040ull, 1574400ull, 4158456ull, 9317496ull, 1773080ull}}}, + {TextureTileMode::kR64KBX, 8u, 8u, 32u, 32u, 160u, 96u, 983040ull, {{0ull, 16648320ull, 1444352ull, 2689280ull, 6979576ull, 15605880ull, 2952984ull}}}, + {TextureTileMode::kR64KBX, 16u, 2u, 32u, 64u, 160u, 128u, 655360ull, {{0ull, 11075712ull, 787456ull, 1706496ull, 4718576ull, 10397808ull, 1967664ull}}}, + {TextureTileMode::kR64KBX, 16u, 4u, 32u, 32u, 160u, 96u, 983040ull, {{0ull, 16646272ull, 1443328ull, 2690304ull, 7011312ull, 15641712ull, 2950960ull}}}, + {TextureTileMode::kR64KBX, 16u, 8u, 16u, 32u, 144u, 96u, 1769472ull, {{0ull, 30017664ull, 2624000ull, 4851968ull, 12615664ull, 28201072ull, 5312304ull}}}, + {TextureTileMode::kZ64KBX, 1u, 2u, 128u, 256u, 256u, 256u, 131072ull, {{0ull, 2172945ull, 149504ull, 271361ull, 923390ull, 1992879ull, 396294ull}}}, + {TextureTileMode::kZ64KBX, 1u, 4u, 128u, 128u, 256u, 128u, 131072ull, {{0ull, 2181153ull, 165890ull, 279555ull, 931580ull, 2025565ull, 396302ull}}}, + {TextureTileMode::kZ64KBX, 1u, 8u, 64u, 128u, 192u, 128u, 196608ull, {{0ull, 3295297ull, 263170ull, 410115ull, 1425404ull, 3041469ull, 591390ull}}}, + {TextureTileMode::kZ64KBX, 2u, 2u, 128u, 128u, 256u, 128u, 131072ull, {{0ull, 2181154ull, 165888ull, 279554ull, 931580ull, 2025566ull, 396300ull}}}, + {TextureTileMode::kZ64KBX, 2u, 4u, 128u, 64u, 256u, 128u, 262144ull, {{0ull, 4425794ull, 280580ull, 689158ull, 1881848ull, 4118714ull, 789532ull}}}, + {TextureTileMode::kZ64KBX, 2u, 8u, 64u, 64u, 192u, 128u, 393216ull, {{0ull, 6637698ull, 459780ull, 999942ull, 2818040ull, 6219898ull, 1181244ull}}}, + {TextureTileMode::kZ64KBX, 4u, 2u, 64u, 128u, 192u, 128u, 196608ull, {{0ull, 3295300ull, 264192ull, 410628ull, 1423096ull, 3041468ull, 592920ull}}}, + {TextureTileMode::kZ64KBX, 4u, 4u, 64u, 64u, 192u, 128u, 393216ull, {{0ull, 6637700ull, 460808ull, 1000460ull, 2815728ull, 6219892ull, 1182776ull}}}, + {TextureTileMode::kZ64KBX, 4u, 8u, 32u, 64u, 160u, 128u, 655360ull, {{0ull, 11126788ull, 787464ull, 1720844ull, 4716528ull, 10359028ull, 1967736ull}}}, +}; + +template +void requireRefusal(TFunction action) { + bool refused = false; + try { action(); } catch (const std::exception&) { refused = true; } + Require(refused, "multisampled layout accepted an unsupported operation"); +} + +void checkReference(const ReferenceCase& reference) { + MultisampleTextureLayout layout(reference.tileMode, reference.elementBytes, reference.fragments, 129u, 67u, 17u); + Require(layout.BlockWidth() == reference.blockWidth && layout.BlockHeight() == reference.blockHeight, "multisampled block extent differs from AMD reference"); + Require(layout.Pitch() == reference.pitch && layout.PaddedHeight() == reference.paddedHeight, "multisampled padded extent differs from AMD reference"); + Require(layout.SliceBytes() == reference.sliceBytes && layout.Bytes() == reference.sliceBytes * 17u, "multisampled allocation differs from AMD reference"); + Require(layout.LinearBytes() == 129ull * 67u * 17u * reference.fragments * reference.elementBytes, "multisampled linear region omits fragments or layers"); + for (std::uint32_t n = 0; n < kCoordinates.size(); ++n) { + const auto& coordinate = kCoordinates[n]; + const auto address = layout.Offset(coordinate[0], coordinate[1], coordinate[2], n % reference.fragments); + Require(address == reference.addresses[n], "multisampled fragment address differs from AMD reference"); + for (auto xorBytes : {0x100u, 0x800u, 0x4000u, 0x8000u, 0xcf00u}) { + MultisampleTextureLayout swizzled(reference.tileMode, reference.elementBytes, reference.fragments, 129u, 67u, 17u, xorBytes); + const auto expected = (address & ~65535ull) | ((address & 65535ull) ^ xorBytes); + Require(swizzled.Offset(coordinate[0], coordinate[1], coordinate[2], n % reference.fragments) == expected, "multisampled pipe/bank XOR moves a fragment outside its tile"); + } + } +} + +void checkTransfer(const ReferenceCase& reference) { + MultisampleTextureLayout layout(reference.tileMode, reference.elementBytes, reference.fragments, 129u, 67u, 3u, 0x8100u); + std::vector source(layout.LinearBytes() + 32u, std::byte{0xa5}); + auto linear = std::span(source).subspan(16u, layout.LinearBytes()); + for (std::size_t n = 0; n < linear.size(); ++n) linear[n] = static_cast((n * 13u + n / 251u) & 255u); + const auto savedSource = source; + std::vector tiled(layout.Bytes() + 32u, std::byte{0xa5}); + auto guest = std::span(tiled).subspan(16u, layout.Bytes()); + layout.Tile(linear, guest); + Require(source == savedSource, "multisampled retile changed its source"); + std::vector covered(guest.size()); + std::size_t position = 0; + for (std::uint32_t layer = 0; layer < 3u; ++layer) { + for (std::uint32_t y = 0; y < 67u; ++y) { + for (std::uint32_t x = 0; x < 129u; ++x) { + for (std::uint32_t fragment = 0; fragment < reference.fragments; ++fragment) { + const auto address = layout.Offset(x, y, layer, fragment); + for (std::uint32_t channel = 0; channel < reference.elementBytes; ++channel) { + Require(address + channel < guest.size() && !covered[address + channel], "multisampled element addresses overlap or exceed the allocation"); + covered[address + channel] = true; + Require(guest[address + channel] == linear[position++], "multisampled retile changed element bytes"); + } + } + } + } + } + for (std::size_t n = 0; n < guest.size(); ++n) Require(covered[n] || guest[n] == std::byte{0xa5}, "multisampled retile changed tile padding"); + Require(std::all_of(tiled.begin(), tiled.begin() + 16u, [](auto value) { return value == std::byte{0xa5}; }) && std::all_of(tiled.end() - 16u, tiled.end(), [](auto value) { return value == std::byte{0xa5}; }), "multisampled retile changed guard bytes"); + const auto savedTiled = tiled; + std::vector restored(source.size(), std::byte{0xa5}); + layout.Detile(guest, std::span(restored).subspan(16u, layout.LinearBytes())); + Require(restored == source && tiled == savedTiled, "multisampled detile changed element, source or guard bytes"); +} + +void checkInverse(const ReferenceCase& reference) { + const auto byteBits = static_cast(std::countr_zero(reference.elementBytes)); + const auto fragmentBits = static_cast(std::countr_zero(reference.fragments)); + const auto widthBits = static_cast(std::countr_zero(reference.blockWidth)); + for (const auto xorBytes : {0u, 0x4000u, 0x8000u, 0xcf00u}) { + const MultisampleTextureLayout layout(reference.tileMode, reference.elementBytes, reference.fragments, reference.blockWidth * 4u, reference.blockHeight * 4u, 8u, xorBytes); + for (const auto origin : {std::array{0u, 0u, 0u}, std::array{reference.blockWidth * 2u, reference.blockHeight * 3u, 7u}}) { + const auto base = static_cast(layout.Offset(origin[0], origin[1], origin[2], 0u) & 65535u); + const auto step = xorBytes == 0u && origin[2] == 0u ? 1u : 257u; + for (std::uint32_t packed = 0u; packed < 65536u; packed += step) { + const auto byte = packed & (reference.elementBytes - 1u); + const auto fragment = (packed >> byteBits) & (reference.fragments - 1u); + const auto x = (packed >> (byteBits + fragmentBits)) & (reference.blockWidth - 1u); + const auto y = packed >> (byteBits + fragmentBits + widthBits); + const auto physical = static_cast((layout.Offset(origin[0] + x, origin[1] + y, origin[2], fragment) + byte) & 65535u) ^ base; + std::uint32_t restored = 0u; + for (std::uint32_t bit = 0; bit < 16u; ++bit) restored |= static_cast(std::popcount(physical & layout.InverseBlockEquation()[bit]) & 1u) << bit; + Require(restored == packed, "multisampled inverse equation changed a byte, fragment or pixel coordinate"); + } + } + } +} + +void checkRefusals() { + for (const auto& extent : {std::array{0u, 3u, 1u}, std::array{5u, 0u, 1u}, std::array{16385u, 3u, 1u}, std::array{5u, 16385u, 1u}, std::array{5u, 3u, 0u}, std::array{5u, 3u, 8193u}}) { + requireRefusal([&] { MultisampleTextureLayout invalid(TextureTileMode::kR64KBX, 8u, 8u, extent[0], extent[1], extent[2]); }); + } + for (auto elementBytes : {0u, 3u, 32u}) requireRefusal([&] { MultisampleTextureLayout invalid(TextureTileMode::kR64KBX, elementBytes, 8u, 5u, 3u, 1u); }); + for (auto fragments : {0u, 1u, 3u, 16u}) requireRefusal([&] { MultisampleTextureLayout invalid(TextureTileMode::kR64KBX, 8u, fragments, 5u, 3u, 1u); }); + for (auto tileMode : {TextureTileMode::kLinear, TextureTileMode::kStandard4KB, TextureTileMode::kStandard64KB, TextureTileMode::kS64KBX, TextureTileMode::kD64KBX}) requireRefusal([&] { MultisampleTextureLayout invalid(tileMode, 8u, 8u, 5u, 3u, 1u); }); + for (auto elementBytes : {8u, 16u}) requireRefusal([&] { MultisampleTextureLayout invalid(TextureTileMode::kZ64KBX, elementBytes, 8u, 5u, 3u, 1u); }); + for (auto xorBytes : {1u, 255u, 0x1000u, 0x2000u, 0x10000u}) requireRefusal([&] { MultisampleTextureLayout invalid(TextureTileMode::kR64KBX, 8u, 8u, 5u, 3u, 1u, xorBytes); }); + + MultisampleTextureLayout layout(TextureTileMode::kR64KBX, 8u, 8u, 5u, 3u, 2u); + for (const auto& coordinate : {std::array{5u, 0u, 0u, 0u}, std::array{0u, 3u, 0u, 0u}, std::array{0u, 0u, 2u, 0u}, std::array{0u, 0u, 0u, 8u}}) requireRefusal([&] { layout.Offset(coordinate[0], coordinate[1], coordinate[2], coordinate[3]); }); + std::vector source(layout.Bytes(), std::byte{0xa5}), destination(layout.Bytes(), std::byte{0x5a}); + const auto savedSource = source, savedDestination = destination; + requireRefusal([&] { layout.Detile(std::span(source).first(layout.Bytes() - 1u), destination); }); + requireRefusal([&] { layout.Detile(source, std::span(destination).first(layout.LinearBytes() - 1u)); }); + requireRefusal([&] { layout.Tile(std::span(source).first(layout.LinearBytes() - 1u), destination); }); + requireRefusal([&] { layout.Tile(source, std::span(destination).first(layout.Bytes() - 1u)); }); + requireRefusal([&] { layout.Detile(source, source); }); + requireRefusal([&] { layout.Tile(source, source); }); + requireRefusal([&] { layout.Detile(source, std::span(source).subspan(8u)); }); + Require(source == savedSource && destination == savedDestination, "refused multisampled transfer changed a buffer"); +} + +} + +int main() { + try { + for (const auto& reference : kReferences) { + checkReference(reference); + checkTransfer(reference); + checkInverse(reference); + } + checkRefusals(); + std::cout << "Multisampled texture layout reference, fragment, layer, padding and refusal tests passed\n"; + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index 52e16ee07a..23232ad317 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -14,6 +14,7 @@ - [Initial point context](../../../core/libs/prx/libSceAgcDriver/Execution/include/QueueState.hpp) - new queues and CLEAR_STATE seed PA_SU_POINT_SIZE 0x00080008 and PA_SU_POINT_MINMAX 0xffff0000, matching every public context table in libSceAgc/Command/include/RegisterDefaultsData.hpp. Cocoon (PPSA08766) reaches geometry point output without writing these registers in the observed startup. Applying the SDK context defaults to initial/CLEAR_STATE driver context is inferred; GPU power-on reset state is not measured and is not the source of these values. Explicit writes and pushed/restored contexts retain their own values. +- [Multisampled R_X and Z_X layouts](../../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTextureLayout.cpp) implement single-mip 2D arrays with 2, 4 or 8 equal samples and fragments, using the non-RB+ 16-pipe GFX10 equations from [AMD Addrlib](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10SwizzlePattern.h). R_X accepts 1/2/4/8/16-byte elements and Z_X accepts 1/2/4-byte elements. Tile and detile preserve element bytes and leave padding untouched; they do not synchronize resident images or publish compression metadata. Other modes, sample counts, element sizes and pipe/bank XOR bits outside the supported configuration throw. PS5 address-layout equivalence has not been measured. - [Optional BMP frame dumps](../../../core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp) use checked host stdio. An open, header, pixel or close failure prints one diagnostic and disables subsequent BMP dumps for the process; queued frames drain without more file I/O. Pixel validation and allocation failures retain the existing fatal path. The CPU fixture checks identical BMP bytes and real pipe failures. VideoOut flips on an RTX 3060 continue after a first dump that cannot be opened; header, pixel and close failures during presentation are unmeasured. - [Inactive vertex point size](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) - `PA_CL_VS_OUT_CNTL.USE_VTX_POINT_SIZE` is accepted for lines, triangles and rectangles with `GS_EN` and `HS_EN` clear, when polygon mode is disabled or both faces are filled. Points, patches and absent routing or rasterization registers still throw. Bit 16 selects the misc export's X component as PointSize ([AMD CIK register reference](https://www.amd.com/content/dam/amd/en/documents/radeon-tech-docs/programmer-references/CIK_3D_registers_v2.pdf), PA_CL_VS_OUT_CNTL); ignoring it for non-point rasterization follows [Vulkan PointSize](https://docs.vulkan.org/refpages/latest/refpages/source/PointSize.html). This is inferred, not measured on console hardware. XashPS5 (PPSA19111 v1.3) sets `0x01210000` on a filled triangle draw. From b4fbf24d1e811e0518ccd9359d2c7e1109d340f7 Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Sat, 17 Oct 2026 01:18:45 +0300 Subject: [PATCH 140/523] feat(agc): upload individual multisample image samples --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 16 ++- .../Execution/src/VulkanDevice.cpp | 3 + .../Graphics/include/Context.hpp | 1 + .../Graphics/include/TextureDetiler.hpp | 3 + .../Graphics/shaders/MultisampleUpload.comp | 32 +++++ .../Graphics/src/MultisampleUpload.cpp | 68 ++++++++++ .../Graphics/src/TextureDetiler.cpp | 1 + .../tests/execution/MultisampleReadback.cpp | 128 ++++++++++++++++-- docs/dev/debt/libSceAgcDriver.md | 2 +- 9 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/Graphics/shaders/MultisampleUpload.comp create mode 100644 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleUpload.cpp diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 7efd108e1d..969138bea5 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -79,6 +79,18 @@ foreach(agcReadbackKind IN ITEMS Uint Depth) list(APPEND agcReadbackHeaders ${agcReadbackHeader}) endforeach() +set(agcMultisampleUploadSpv ${agcTextureDetileGeneratedDir}/MultisampleUpload.spv) +set(agcMultisampleUploadHeader ${agcTextureDetileGeneratedDir}/MultisampleUpload_spv.h) +add_custom_command( + OUTPUT "${agcMultisampleUploadHeader}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTextureDetileGeneratedDir}" + COMMAND "$" -V --target-env vulkan1.1 -o "${agcMultisampleUploadSpv}" "${CMAKE_CURRENT_SOURCE_DIR}/Graphics/shaders/MultisampleUpload.comp" + COMMAND ${CMAKE_COMMAND} -DINPUT=${agcMultisampleUploadSpv} -DOUTPUT=${agcMultisampleUploadHeader} + -DSYMBOL=MULTISAMPLE_UPLOAD_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Graphics/shaders/MultisampleUpload.comp" glslang-standalone + VERBATIM +) + set(agcMeshArgumentsShaderSpv ${agcTextureDetileGeneratedDir}/MeshArguments.spv) set(agcMeshArgumentsShaderHeader ${agcTextureDetileGeneratedDir}/MeshArguments_spv.h) add_custom_command( @@ -337,6 +349,7 @@ set(agcDriverSources ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/MultisampleTextureLayout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/TextureDetiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/MultisampleReadback.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/MultisampleUpload.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Texture.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/PassHazards.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/SampleOrder.cpp @@ -346,6 +359,7 @@ set(agcDriverSources ${agcSampleOrderShaderHeaders} ${agcTextureDetileShaderHeader} ${agcReadbackHeaders} + ${agcMultisampleUploadHeader} ${agcCmaskClearShaderHeader} ${agcDepthSampleShaderHeaders} ${agcMeshArgumentsShaderHeader} @@ -2295,7 +2309,7 @@ if(BUILD_TESTING) set_tests_properties(agc_driver_queued_raw_shader PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_PIPELINED_DRAWS=8" TIMEOUT 30) add_test(NAME agc_driver_partially_resident_pending_stores COMMAND agc_driver_partially_resident_snapshot_tests --pending-stores) set_tests_properties(agc_driver_multisample_readback PROPERTIES TIMEOUT 20) - foreach(agcReadbackCase IN ITEMS uint8 uint16 uint64 uint128 depth) + foreach(agcReadbackCase IN ITEMS uint8 uint16 uint64 uint128 depth upload) add_test(NAME agc_driver_multisample_readback_${agcReadbackCase} COMMAND agc_driver_multisample_readback_tests ${agcReadbackCase}) set_tests_properties(agc_driver_multisample_readback_${agcReadbackCase} PROPERTIES TIMEOUT 20 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") if(WIN32) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 286da2ec1f..4077bfcfcc 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -316,6 +316,7 @@ struct VulkanDevice::State { bool geometryShader = false; bool sampleRateShading = false; bool storageImageMultisample = false; + bool storageImageWriteWithoutFormat = false; bool shaderClipDistance = false; bool shaderCullDistance = false; bool shaderClock = false; @@ -1693,6 +1694,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) : if (enabled.shaderStorageImageMultisample) state->capabilities.push_back(spv::CapabilityStorageImageMultisample); state->capabilities.push_back(spv::CapabilityImageMSArray); enabled.shaderStorageImageWriteWithoutFormat = available.shaderStorageImageWriteWithoutFormat; + state->storageImageWriteWithoutFormat = enabled.shaderStorageImageWriteWithoutFormat == VK_TRUE; enabled.shaderStorageImageReadWithoutFormat = available.shaderStorageImageReadWithoutFormat; // Gathers with non-constant offsets (ImageGatherExtended). enabled.shaderImageGatherExtended = available.shaderImageGatherExtended; @@ -3833,6 +3835,7 @@ Graphics::Context VulkanDevice::buildContext() const { context.fillModeNonSolid = state->fillModeNonSolid; context.maintenance5 = state->maintenance5; context.storageImageMultisample = state->storageImageMultisample; + context.storageImageWriteWithoutFormat = state->storageImageWriteWithoutFormat; context.shaderClipDistance = state->shaderClipDistance; context.shaderCullDistance = state->shaderCullDistance; context.shaderTerminateInvocation = state->shaderTerminateInvocation; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp index afaa005b1d..1ac783d39e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp @@ -241,6 +241,7 @@ struct Context { bool fillModeNonSolid = false; bool maintenance5 = false; bool storageImageMultisample = false; + bool storageImageWriteWithoutFormat = false; bool shaderClipDistance = false; bool shaderCullDistance = false; bool nullDescriptors = false; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp index 316ae0cb63..e0dc3531e4 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp @@ -57,9 +57,11 @@ namespace AgcDriver::Graphics { std::uint32_t CmaskErrors(); void DispatchMultisampleReadback(VkCommandBuffer commands, VkFormat format, VkBuffer destination, std::uint64_t offset, std::uint64_t bytes, VkImageView arrayView, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t fragments, std::uint32_t firstElement, std::uint32_t elements); void DispatchDepthSample(VkCommandBuffer commands, VkImageView depth, bool multisampled, VkImageView target, std::uint32_t width, std::uint32_t height, std::uint32_t sample); + void DispatchMultisampleImage(VkCommandBuffer commands, std::uint32_t elementBytes, VkBuffer source, std::uint64_t offset, std::uint64_t bytes, VkImageView arrayView, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t fragments, std::uint32_t firstElement, std::uint32_t elements); private: VkPipeline multisampleReadbackPipeline(std::uint32_t elementBytes, bool depth); + VkPipeline multisampleUploadPipeline(std::uint32_t elementBytes); VkPipeline pipeline(TextureTileMode tileMode, std::uint32_t elementBytes, bool retile, bool thick, bool image = false); void release() noexcept; struct DescriptorRing { @@ -100,6 +102,7 @@ namespace AgcDriver::Graphics { std::array readbackModules{}; std::vector> readbackPipelines; DescriptorRing readbackDescriptorSets; + VkShaderModule multisampleUploadModule = VK_NULL_HANDLE; }; } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/shaders/MultisampleUpload.comp b/core/libs/prx/libSceAgcDriver/Graphics/shaders/MultisampleUpload.comp new file mode 100644 index 0000000000..4aa972fc04 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Graphics/shaders/MultisampleUpload.comp @@ -0,0 +1,32 @@ +#version 450 +#extension GL_EXT_shader_image_load_formatted : require + +layout(constant_id = 0) const uint ELEMENT_BYTES = 4u; +layout(local_size_x = 64) in; +layout(set = 0, binding = 0, std430) readonly buffer Input { uint data[]; } sourceBuffer; +layout(set = 0, binding = 1) uniform uimage2DMSArray destinationImage; +layout(push_constant) uniform Push { + uint width; + uint height; + uint layers; + uint fragments; + uint firstElement; + uint elements; + uint rowThreads; +} params; + +void main() { + uint index = gl_GlobalInvocationID.y * params.rowThreads + gl_GlobalInvocationID.x; + if (index >= params.elements) return; + uint element = params.firstElement + index; + uint pixel = element / params.fragments; + ivec3 coordinate = ivec3(pixel % params.width, (pixel / params.width) % params.height, pixel / (params.width * params.height)); + uint address = index * ELEMENT_BYTES; + uvec4 value = uvec4(0u); + if (ELEMENT_BYTES < 4u) { + value.x = (sourceBuffer.data[address >> 2u] >> ((address & 3u) * 8u)) & (ELEMENT_BYTES == 1u ? 255u : 65535u); + } else { + for (uint component = 0u; component < ELEMENT_BYTES / 4u; ++component) value[component] = sourceBuffer.data[(address >> 2u) + component]; + } + imageStore(destinationImage, coordinate, int(element % params.fragments), value); +} diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleUpload.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleUpload.cpp new file mode 100644 index 0000000000..419ed7648e --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleUpload.cpp @@ -0,0 +1,68 @@ +#include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp" +#include "prx/libSceAgcDriver/Graphics/shaders/MultisampleUpload_spv.h" +#include +#include +#include +#include + +namespace AgcDriver::Graphics { + +VkPipeline TextureDetiler::multisampleUploadPipeline(std::uint32_t elementBytes) { + const auto key = (1u << 19u) | elementBytes; + for (const auto& entry : pipelines) if (entry.first == key) return entry.second; + if (multisampleUploadModule == VK_NULL_HANDLE) { + VkShaderModuleCreateInfo info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; + info.codeSize = sizeof(MULTISAMPLE_UPLOAD_SPV); + info.pCode = MULTISAMPLE_UPLOAD_SPV; + Check(context.Function("vkCreateShaderModule")(context.device, &info, nullptr, &multisampleUploadModule), "vkCreateShaderModule multisample upload"); + } + const VkSpecializationMapEntry entry{0u, 0u, sizeof(elementBytes)}; + const VkSpecializationInfo specialization{1u, &entry, sizeof(elementBytes), &elementBytes}; + VkComputePipelineCreateInfo info{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; + info.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO}; + info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + info.stage.module = multisampleUploadModule; + info.stage.pName = "main"; + info.stage.pSpecializationInfo = &specialization; + info.layout = imagePipelineLayout; + pipelines.reserve(pipelines.size() + 1u); + VkPipeline result = VK_NULL_HANDLE; + { + auto pipelineCacheLock = context.LockPipelineCacheForUse(); + Check(context.Function("vkCreateComputePipelines")(context.device, context.pipelineCache, 1u, &info, nullptr, &result), "vkCreateComputePipelines multisample upload"); + } + pipelines.emplace_back(key, result); + return result; +} + +void TextureDetiler::DispatchMultisampleImage(VkCommandBuffer commands, std::uint32_t elementBytes, VkBuffer source, std::uint64_t offset, std::uint64_t bytes, VkImageView arrayView, std::uint32_t width, std::uint32_t height, std::uint32_t layers, std::uint32_t fragments, std::uint32_t firstElement, std::uint32_t elements) { + Require(context.storageImageMultisample && context.storageImageWriteWithoutFormat, "device does not enable multisampled storage image writes without format"); + Require(commands != VK_NULL_HANDLE && source != VK_NULL_HANDLE && arrayView != VK_NULL_HANDLE, "multisample upload requires commands, buffer and array view"); + Require(std::has_single_bit(elementBytes) && elementBytes <= 16u, "multisample upload element size is unsupported"); + Require(width != 0u && width <= 16384u && height != 0u && height <= 16384u && layers != 0u && layers <= 8192u, "multisample upload extent is unsupported"); + Require(fragments == 2u || fragments == 4u || fragments == 8u, "multisample upload fragment count is unsupported"); + const auto total = static_cast(width) * height * layers * fragments; + Require(total <= std::numeric_limits::max() && elements != 0u && static_cast(firstElement) + elements <= total, "multisample upload element range is invalid"); + const auto required = (static_cast(elements) * elementBytes + 3u) & ~3ull; + const auto alignment = std::max(4u, context.limits.minStorageBufferOffsetAlignment); + Require(offset % alignment == 0u && bytes >= required && bytes <= context.limits.maxStorageBufferRange && bytes % 4u == 0u && offset <= std::numeric_limits::max() - bytes, "multisample upload buffer range is invalid"); + Require(context.limits.maxComputeWorkGroupInvocations >= 64u && context.limits.maxComputeWorkGroupSize[0] >= 64u && context.limits.maxComputeWorkGroupSize[1] >= 1u && context.limits.maxComputeWorkGroupSize[2] >= 1u && context.limits.maxComputeWorkGroupCount[2] >= 1u, "device does not support the multisample upload workgroup"); + const auto groups = (static_cast(elements) + 63u) / 64u; + const auto groupsX = std::min(groups, context.limits.maxComputeWorkGroupCount[0]); + Require(groupsX != 0u, "device has no multisample upload workgroups"); + const auto groupsY = (groups + groupsX - 1u) / groupsX; + Require(groupsY <= context.limits.maxComputeWorkGroupCount[1] && groupsX * groupsY * 64u <= std::numeric_limits::max(), "multisample upload dispatch exceeds the device limits"); + const auto target = multisampleUploadPipeline(elementBytes); + const auto set = allocateSet(true); + const VkDescriptorBufferInfo buffer{source, offset, bytes}; + const VkDescriptorImageInfo image{VK_NULL_HANDLE, arrayView, VK_IMAGE_LAYOUT_GENERAL}; + const std::array writes{{{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, 0u, 0u, 1u, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &buffer, nullptr}, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, 1u, 0u, 1u, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &image, nullptr, nullptr}}}; + context.Function("vkUpdateDescriptorSets")(context.device, 2u, writes.data(), 0u, nullptr); + context.Function("vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, target); + context.Function("vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, imagePipelineLayout, 0u, 1u, &set, 0u, nullptr); + const std::array push{width, height, layers, fragments, firstElement, elements, static_cast(groupsX * 64u)}; + context.Function("vkCmdPushConstants")(commands, imagePipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0u, sizeof(push), push.data()); + context.Function("vkCmdDispatch")(commands, static_cast(groupsX), static_cast(groupsY), 1u); +} + +} diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp index d2a6964f6b..c571bd1710 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/TextureDetiler.cpp @@ -110,6 +110,7 @@ void TextureDetiler::release() noexcept { for (const auto pool : readbackDescriptorSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); for (const auto& entry : readbackPipelines) context.Function("vkDestroyPipeline")(context.device, entry.second, nullptr); for (const auto shader : readbackModules) if (shader) context.Function("vkDestroyShaderModule")(context.device, shader, nullptr); + if (multisampleUploadModule) context.Function("vkDestroyShaderModule")(context.device, multisampleUploadModule, nullptr); if (readbackPipelineLayout) context.Function("vkDestroyPipelineLayout")(context.device, readbackPipelineLayout, nullptr); if (readbackDescriptorLayout) context.Function("vkDestroyDescriptorSetLayout")(context.device, readbackDescriptorLayout, nullptr); for (const auto pool : cmaskDescriptorSets.pools) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp index 8b6cc1b256..4e27aaf2f5 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp @@ -1,6 +1,7 @@ #include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp" #include "prx/libSceAgcDriver/Graphics/include/Resources.hpp" #include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp" +#include "prx/libSceAgcDriver/Graphics/include/MultisampleTextureLayout.hpp" #include "VulkanTestDevice.hpp" #include "MsaaReadbackVertex_spv.h" #include "MsaaReadbackUint_spv.h" @@ -69,8 +70,8 @@ void CheckSupportSelection() { static_assert(!SurfaceFits(VkImageFormatProperties{}, 4u)); } -bool SurfaceSupported(const Context& context, VkFormat format, std::uint32_t samples, std::uint32_t width = 17u, std::uint32_t height = 5u, std::uint32_t layers = 2u) { - const VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | (format == VK_FORMAT_D32_SFLOAT ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); +bool SurfaceSupported(const Context& context, VkFormat format, std::uint32_t samples, std::uint32_t width = 17u, std::uint32_t height = 5u, std::uint32_t layers = 2u, bool writable = false) { + const VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | (format == VK_FORMAT_D32_SFLOAT ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) | (writable ? VK_IMAGE_USAGE_STORAGE_BIT : 0u); VkImageFormatProperties supported{}; const auto result = context.imageFormatProperties(context.physical, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage, 0u, &supported); if (result != VK_ERROR_FORMAT_NOT_SUPPORTED) Check(result, "readback fixture format query"); @@ -97,11 +98,11 @@ struct Surface { std::vector views; std::vector framebuffers; - Surface(const Context& context, VkFormat format, std::uint32_t elementBytes, std::uint32_t samples, std::uint32_t width = 17u, std::uint32_t height = 5u, std::uint32_t layers = 2u) : context(context), format(format), elementBytes(elementBytes), samples(samples), width(width), height(height), layers(layers) { + Surface(const Context& context, VkFormat format, std::uint32_t elementBytes, std::uint32_t samples, std::uint32_t width = 17u, std::uint32_t height = 5u, std::uint32_t layers = 2u, bool writable = false) : context(context), format(format), elementBytes(elementBytes), samples(samples), width(width), height(height), layers(layers) { try { const bool depth = format == VK_FORMAT_D32_SFLOAT; const auto aspect = depth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - const VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | (depth ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); + const VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | (depth ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) | (writable ? VK_IMAGE_USAGE_STORAGE_BIT : 0u); VkImageFormatProperties supported{}; Check(context.imageFormatProperties(context.physical, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage, 0u, &supported), "readback fixture format query"); Require(SurfaceFits(supported, samples, width, height, layers), "readback fixture format does not support its sample count or extent"); @@ -282,8 +283,8 @@ struct Surface { } }; -void CheckReadback(TextureDetiler& reader, const Surface& surface, std::uint32_t first, std::uint32_t elements, std::uint32_t repetitions, std::size_t extraBytes = 0u) { - const auto expected = surface.Expected(first, elements); +std::vector CheckReadback(TextureDetiler& reader, const Surface& surface, std::uint32_t first, std::uint32_t elements, std::uint32_t repetitions, std::size_t extraBytes = 0u, std::span supplied = {}) { + const auto expected = supplied.empty() ? surface.Expected(first, elements) : std::vector(supplied.begin(), supplied.end()); const auto bytes = ((expected.size() + 3u) & ~std::size_t{3u}) + extraBytes; const auto alignment = std::max(4u, surface.context.limits.minStorageBufferOffsetAlignment); const auto offset = (16u + alignment - 1u) / alignment * alignment; @@ -291,7 +292,7 @@ void CheckReadback(TextureDetiler& reader, const Surface& surface, std::uint32_t std::fill(output.Bytes().begin(), output.Bytes().end(), std::byte{0xa5}); reader.BeginBatch(); CommandBatch batch(surface.context); - RecordMemoryBarrier(surface.context, batch.Handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + RecordMemoryBarrier(surface.context, batch.Handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); for (std::uint32_t repeat = 0u; repeat < repetitions; ++repeat) { reader.DispatchMultisampleReadback(batch.Handle(), surface.format, output.Handle(), offset, bytes, surface.arrayView, surface.width, surface.height, surface.layers, surface.samples, first, elements); if (repeat + 1u < repetitions) RecordMemoryBarrier(surface.context, batch.Handle(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); @@ -304,6 +305,7 @@ void CheckReadback(TextureDetiler& reader, const Surface& surface, std::uint32_t if (mismatch.first != expected.end()) std::printf("readback mismatch: format=%u first=%u byte=%zu expected=0x%02x actual=0x%02x\n", static_cast(surface.format), first, static_cast(mismatch.first - expected.begin()), std::to_integer(*mismatch.first), std::to_integer(*mismatch.second)); Require(std::equal(expected.begin(), expected.end(), data.begin() + offset), "multisample readback changed a rendered sample, component, layer or pixel"); for (std::size_t index = 0u; index < data.size(); ++index) Require((index >= offset && index < offset + expected.size()) || data[index] == std::byte{0xa5}, "multisample readback changed a buffer sentinel or packed tail"); + return {data.begin() + offset, data.begin() + offset + expected.size()}; } template @@ -331,6 +333,106 @@ void CheckRefusals(TextureDetiler& reader, const Surface& surface) { std::puts("multisample readback invalid-input refusals passed"); } +std::vector Upload(TextureDetiler& transfer, TextureDetiler& reader, const Surface& surface, std::vector& expected, std::uint32_t first, std::uint32_t elements, std::uint32_t repetitions = 1u, std::span supplied = {}) { + const auto bytes = static_cast(elements) * surface.elementBytes; + const auto padded = (bytes + 3u) & ~std::size_t{3}; + const auto alignment = std::max({4u, surface.elementBytes, surface.context.limits.minStorageBufferOffsetAlignment}); + const auto offset = (16u + alignment - 1u) / alignment * alignment; + Buffer source(surface.context, offset + padded + 16u, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + std::fill(source.Bytes().begin(), source.Bytes().end(), std::byte{0x5a}); + Require(supplied.empty() || supplied.size() == bytes, "multisample upload fixture source size mismatch"); + for (std::size_t index = 0; index < bytes; ++index) source.Bytes()[offset + index] = supplied.empty() ? static_cast((index * 29u + first * 17u + elements * 7u) & 255u) : supplied[index]; + const std::vector retained(source.Bytes().begin(), source.Bytes().end()); + std::copy_n(retained.begin() + offset, bytes, expected.begin() + static_cast(first) * surface.elementBytes); + const auto stride = 256u / alignment * alignment; + Require(stride != 0u, "upload fixture cannot hold an aligned chunk"); + transfer.BeginBatch(); + CommandBatch batch(surface.context); + RecordMemoryBarrier(surface.context, batch.Handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + for (std::uint32_t repeat = 0; repeat < repetitions; ++repeat) { + for (std::size_t done = 0; done < bytes; done += stride) { + const auto count = std::min(stride, bytes - done); + transfer.DispatchMultisampleImage(batch.Handle(), surface.elementBytes, source.Handle(), offset + done, (count + 3u) & ~std::size_t{3}, surface.arrayView, surface.width, surface.height, surface.layers, surface.samples, first + static_cast(done / surface.elementBytes), static_cast(count / surface.elementBytes)); + } + RecordMemoryBarrier(surface.context, batch.Handle(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + } + batch.SubmitAndWait(); + source.Invalidate(); + Require(std::equal(retained.begin(), retained.end(), source.Bytes().begin()), "multisample upload changed source bytes or sentinels"); + return CheckReadback(reader, surface, 0u, surface.width * surface.height * surface.layers * surface.samples, 1u, 0u, expected); +} + +void CheckUploadRefusals(TextureDetiler& transfer, const Surface& surface) { + Buffer source(surface.context, 256u, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); + CommandBatch batch(surface.context); + const auto call = [&](std::uint32_t bytes, std::uint32_t fragments, std::uint32_t first, std::uint32_t elements, std::uint64_t offset = 0u, std::uint64_t range = 4u) { + transfer.DispatchMultisampleImage(batch.Handle(), bytes, source.Handle(), offset, range, surface.arrayView, 1u, 1u, 1u, fragments, first, elements); + }; + for (const auto bytes : {0u, 3u, 32u}) Refused([&] { call(bytes, 4u, 0u, 1u); }, "element size is unsupported"); + for (const auto fragments : {0u, 1u, 3u, 16u}) Refused([&] { call(4u, fragments, 0u, 1u); }, "fragment count is unsupported"); + for (const auto range : {std::array{0u, 0u}, std::array{4u, 1u}, std::array{3u, 2u}, std::array{0xffffffffu, 2u}}) Refused([&] { call(4u, 4u, range[0], range[1]); }, "element range is invalid"); + for (const auto range : {std::array{1u, 4u}, std::array{0u, 0u}, std::array{0u, 3u}, std::array{0u, 260u}, std::array{~std::uint64_t{0} - 3u, 8u}}) Refused([&] { call(4u, 4u, 0u, 1u, range[0], range[1]); }, "buffer range is invalid"); + Refused([&] { transfer.DispatchMultisampleImage(VK_NULL_HANDLE, 4u, source.Handle(), 0u, 4u, surface.arrayView, 1u, 1u, 1u, 4u, 0u, 1u); }, "requires commands, buffer and array view"); + Refused([&] { transfer.DispatchMultisampleImage(batch.Handle(), 4u, VK_NULL_HANDLE, 0u, 4u, surface.arrayView, 1u, 1u, 1u, 4u, 0u, 1u); }, "requires commands, buffer and array view"); + Refused([&] { transfer.DispatchMultisampleImage(batch.Handle(), 4u, source.Handle(), 0u, 4u, VK_NULL_HANDLE, 1u, 1u, 1u, 4u, 0u, 1u); }, "requires commands, buffer and array view"); + for (const auto extent : {std::array{0u, 1u, 1u}, std::array{1u, 0u, 1u}, std::array{1u, 1u, 0u}, std::array{16385u, 1u, 1u}, std::array{1u, 16385u, 1u}, std::array{1u, 1u, 8193u}}) Refused([&] { transfer.DispatchMultisampleImage(batch.Handle(), 4u, source.Handle(), 0u, 4u, surface.arrayView, extent[0], extent[1], extent[2], 4u, 0u, 1u); }, "extent is unsupported"); + Refused([&] { transfer.DispatchMultisampleImage(batch.Handle(), 4u, source.Handle(), 0u, 4u, surface.arrayView, 16384u, 16384u, 8192u, 8u, 0u, 1u); }, "element range is invalid"); + for (const bool multisample : {false, true}) { + auto unsupported = surface.context; + if (multisample) unsupported.storageImageMultisample = false; + else unsupported.storageImageWriteWithoutFormat = false; + TextureDetiler unavailable(unsupported); + Refused([&] { unavailable.DispatchMultisampleImage(batch.Handle(), 4u, source.Handle(), 0u, 4u, surface.arrayView, 1u, 1u, 1u, 4u, 0u, 1u); }, "does not enable multisampled storage image writes without format"); + } +} + +void CheckUpload(const Context& context) { + Require(context.storageImageMultisample && context.storageImageWriteWithoutFormat, "upload fixture requires enabled multisampled formatless storage writes"); + auto limited = context; + limited.limits.maxComputeWorkGroupCount[0] = 1u; + limited.limits.maxStorageBufferRange = 256u; + TextureDetiler transfer(limited); + TextureDetiler reader(context); + std::uint32_t exercised = 0u; + for (const auto bytes : {1u, 2u, 4u, 8u, 16u}) { + const auto format = TextureDetiler::ImageElementFormat(bytes); + for (const auto samples : {2u, 4u, 8u}) { + if (!SurfaceSupported(context, format, samples, 17u, 5u, 2u, true)) continue; + Surface surface(context, format, bytes, samples, 17u, 5u, 2u, true); + surface.Render(); + const auto elements = surface.width * surface.height * surface.layers * samples; + auto expected = surface.Expected(0u, elements); + Upload(transfer, reader, surface, expected, 3u, 1u); + Upload(transfer, reader, surface, expected, 1u, elements - 3u); + Upload(transfer, reader, surface, expected, 0u, elements); + Upload(transfer, reader, surface, expected, elements - 1u, 1u, samples == 4u ? 65u : 1u); + if (bytes == 4u && samples == 4u) CheckUploadRefusals(transfer, surface); + std::printf("multisample upload: bytes=%u samples=%u layers=2 full/partial/packed/source-sentinels/chunks/reuse=passed\n", bytes, samples); + ++exercised; + } + } + Require(exercised != 0u, "no supported multisample upload subcase was exercised"); + const MultisampleTextureLayout active(TextureTileMode::kR64KBX, 8u, 4u, 17u, 5u, 2u); + if (SurfaceSupported(context, VK_FORMAT_R32G32_UINT, 4u, active.Pitch(), active.PaddedHeight(), 2u, true)) { + for (const auto xorValue : {0u, 0x8100u}) { + const MultisampleTextureLayout layout(TextureTileMode::kR64KBX, 8u, 4u, active.Pitch(), active.PaddedHeight(), 2u, xorValue); + std::vector tiled(static_cast(layout.Bytes())); + for (std::size_t byte = 0; byte < tiled.size(); ++byte) tiled[byte] = static_cast((byte * 37u + (byte >> 8u) * 13u + (byte >> 15u) * 71u + (byte >> 16u) * 101u) & 255u); + std::vector linear(static_cast(layout.LinearBytes())); + layout.Detile(tiled, linear); + Surface surface(context, VK_FORMAT_R32G32_UINT, 8u, 4u, layout.Pitch(), layout.PaddedHeight(), 2u, true); + surface.Render(); + const auto elements = surface.width * surface.height * surface.layers * surface.samples; + auto expected = surface.Expected(0u, elements); + const auto actual = Upload(transfer, reader, surface, expected, 0u, elements, 1u, linear); + std::vector retiled(tiled.size(), std::byte{0xa5}); + layout.Tile(actual, retiled); + Require(retiled == tiled, "multisample padded initialization changed color bytes, fragments, layers, XOR or padding"); + std::printf("multisample padded upload: active=17x5 backing=%ux%u layers=2 fragments=4 bytes=%zu xor=0x%x exact roundtrip passed\n", surface.width, surface.height, retiled.size(), xorValue); + } + } +} + } int main(int argc, char** argv) { @@ -343,7 +445,7 @@ int main(int argc, char** argv) { else if (mode == "uint16") { format = VK_FORMAT_R16_UINT; bytes = 2u; } else if (mode == "uint64") { format = VK_FORMAT_R32G32_UINT; bytes = 8u; } else if (mode == "uint128") { format = VK_FORMAT_R32G32B32A32_UINT; bytes = 16u; } - else Require(mode == "uint32" || mode == "depth", "unknown multisample readback test mode"); + else Require(mode == "uint32" || mode == "depth" || mode == "upload", "unknown multisample readback test mode"); if (mode == "depth") format = VK_FORMAT_D32_SFLOAT; const auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; @@ -357,6 +459,16 @@ int main(int argc, char** argv) { std::puts("skipped, the device does not support per-sample shading"); return VulkanTestSkipped; } + if (mode == "upload") { + if (!context.storageImageMultisample || !context.storageImageWriteWithoutFormat) { + std::puts("skipped, the device does not enable multisampled formatless storage writes"); + return VulkanTestSkipped; + } + CheckUpload(context); + Require(checkedPipelineCacheUses != 0u, "upload fixture did not check pipeline cache use"); + std::printf("multisample upload guarded pipeline creation checks=%u passed\n", checkedPipelineCacheUses); + return 0; + } auto limited = context; limited.limits.maxComputeWorkGroupCount[0] = 1u; TextureDetiler reader(limited); diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index 23232ad317..06024e03e5 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -218,7 +218,7 @@ - [Unclamped normalized color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - CB_COLOR_INFO targets with BLEND_CLAMP clear (bit 15) on UNORM and SNORM formats are accepted when blending is disabled (ENABLE clear or BLEND_BYPASS set). Vulkan converts unblended shader values and clamps to the normalized range automatically; blending into an unclamped normalized target remains unmodeled because Vulkan always clamps inputs prior to blending. - [Multisample image readback](../../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleReadback.cpp) fetches unsigned R8/R16/R32/R32G32/R32G32B32A32 or D32 depth samples through a 2D array view in GENERAL, in layer/y/x/fragment order. The caller owns producer completion, image transitions and resource lifetimes. It implements Vulkan per-sample fetches, not guest-memory publication, metadata expansion or PS5 layout equivalence; D32 exceptional-value preservation has not been measured. Other formats, sample counts and invalid ranges throw. - +- [Multisample image upload](../../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleUpload.cpp) does not model guest layouts, native ownership or CMASK/FMASK/DCC publication; console equivalence and large-title initialization costs remain unmeasured. It stores 1/2/4/8/16-byte elements from a storage buffer into an unsigned 2D multisample array view in layer/y/x/fragment order. It requires enabled multisample storage images and formatless image writes, 2/4/8 fragments and valid aligned buffer and dispatch ranges. The caller supplies the matching storage view in GENERAL and owns barriers, completion and resource lifetimes. - [Thick SW_4KB_S_X volumes](../../../core/libs/prx/libSceAgcDriver/Graphics/src/TextureTiling.cpp) use AMD addrlib's GFX10 non-RB+ 16-pipe `GFX10_SW_4K_S3_X_PATINFO` equations for 1, 2, 4, 8 and 16-byte elements, including packed mip tails. The layout follows [AMD PAL c5e80007](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp); it has not been measured on console hardware. Other thick swizzle modes outside the supported families still throw. Mipmapped 3D color targets remain refused by the color-register decoder. - [Viewport depth range remap](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - on a device without `VK_EXT_depth_range_unrestricted` a viewport depth range outside [0, 1] (Minecraft PPSA17221 draws its world with `PA_CL_VPORT_ZSCALE` 1, `ZOFFSET` 0 in negative-one-to-one clip space) is folded into the vertex shader: `RemapPositionDepth` scales clip z so z/w is the console's depth, the pipeline's depth clamp applies the console's `PA_SC_VPORT_ZMIN/ZMAX` clipped to [0, 1] (exact for a normalized depth target or none; Minecraft clamps its float depth target to [-1, 1], so a depth below 0 is stored as 0 there, which only a later test or read at exactly 0 can tell, and the first such draw is reported once), and two extra `gl_ClipDistance` elements restore the near and far planes depth clamping removes. The mode is a pipeline specialization constant (`PipelineSpecialization::DepthRemapBase`); scale and offset are read from the draw's push block (two words at byte 104, `DepthRemapPushOffsetBytes`, so a remapped draw's stage push data ends before it), so draws that differ only in them share one vertex module and one pipeline. The remap serves the vertex path only; mesh and tessellation draws with such a range still throw. Whether the console clamps before or after its depth bias and how it treats a clamp range narrower than the transform's are not measured. - [Benchmark rewrite failure test off Windows](../../../core/libs/prx/libSceAgcDriver/tests/PerformanceTrace.cpp) (libSceAgcDriver) - `ReplaceGzipFile` checks that a write failing at `.tmp` keeps the previous `aps5-bench.json.gz` only on non-Windows builds: the `std::ios_base::failure` thrown inside the MinGW ofstream did not unwind to the test's catch, so Windows CI covers only the failed move. From ab1e6a7a8987379da2ff4de0b0c42d9328a699be Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Fri, 16 Oct 2026 19:13:19 +0300 Subject: [PATCH 141/523] test(agc): connect multisample readback to R_X and Z_X retile --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 2 +- .../tests/execution/MultisampleReadback.cpp | 63 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 969138bea5..d4a7c32860 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -2309,7 +2309,7 @@ if(BUILD_TESTING) set_tests_properties(agc_driver_queued_raw_shader PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_PIPELINED_DRAWS=8" TIMEOUT 30) add_test(NAME agc_driver_partially_resident_pending_stores COMMAND agc_driver_partially_resident_snapshot_tests --pending-stores) set_tests_properties(agc_driver_multisample_readback PROPERTIES TIMEOUT 20) - foreach(agcReadbackCase IN ITEMS uint8 uint16 uint64 uint128 depth upload) + foreach(agcReadbackCase IN ITEMS uint8 uint16 uint64 uint128 depth upload retile) add_test(NAME agc_driver_multisample_readback_${agcReadbackCase} COMMAND agc_driver_multisample_readback_tests ${agcReadbackCase}) set_tests_properties(agc_driver_multisample_readback_${agcReadbackCase} PROPERTIES TIMEOUT 20 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") if(WIN32) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp index 4e27aaf2f5..62a82c3421 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp @@ -308,6 +308,60 @@ std::vector CheckReadback(TextureDetiler& reader, const Surface& surf return {data.begin() + offset, data.begin() + offset + expected.size()}; } +void CheckRetile(TextureDetiler& reader, const Surface& surface) { + const auto elements = surface.width * surface.height * surface.layers * surface.samples; + const auto linear = CheckReadback(reader, surface, 0u, elements, 1u); + const auto expected = surface.Expected(0u, elements); + constexpr std::array, 7> coordinates{{ + {0u, 0u, 0u}, {128u, 66u, 16u}, {64u, 32u, 1u}, {32u, 64u, 2u}, + {63u, 31u, 7u}, {127u, 65u, 15u}, {1u, 1u, 3u} + }}; + constexpr std::array modes{TextureTileMode::kR64KBX, TextureTileMode::kZ64KBX}; + constexpr std::array, 2> references{{ + {0ull, 6621216ull, 493568ull, 1016832ull, 2766588ull, 6170780ull, 1215508ull}, + {0ull, 6637700ull, 460808ull, 1000460ull, 2815728ull, 6219892ull, 1182776ull} + }}; + for (std::size_t mode = 0u; mode < modes.size(); ++mode) { + for (const auto pipeBankXor : {0u, 0x8100u}) { + const MultisampleTextureLayout layout(modes[mode], surface.elementBytes, surface.samples, surface.width, surface.height, surface.layers, pipeBankXor); + std::vector tiled(static_cast(layout.Bytes()) + 32u, std::byte{0xa5}); + auto payload = std::span(tiled).subspan(16u, layout.Bytes()); + layout.Tile(linear, payload); + std::vector covered(payload.size()); + std::size_t position = 0u; + for (std::uint32_t layer = 0u; layer < surface.layers; ++layer) { + for (std::uint32_t y = 0u; y < surface.height; ++y) { + for (std::uint32_t x = 0u; x < surface.width; ++x) { + for (std::uint32_t fragment = 0u; fragment < surface.samples; ++fragment) { + const auto address = layout.Offset(x, y, layer, fragment); + Require(address + surface.elementBytes <= payload.size(), "readback retile exceeds its allocation"); + for (std::uint32_t byte = 0u; byte < surface.elementBytes; ++byte) { + Require(!covered[address + byte], "readback retile elements overlap"); + covered[address + byte] = true; + Require(payload[address + byte] == expected[position++], "readback and retile disagree on layer, pixel or fragment order"); + } + } + } + } + } + for (std::size_t index = 0u; index < covered.size(); ++index) Require(covered[index] || payload[index] == std::byte{0xa5}, "readback retile changed tile padding"); + for (std::size_t index = 0u; index < coordinates.size(); ++index) { + const auto& coordinate = coordinates[index]; + const auto fragment = static_cast(index) % surface.samples; + const auto source = (((coordinate[2] * surface.height + coordinate[1]) * surface.width + coordinate[0]) * surface.samples + fragment) * surface.elementBytes; + const auto reference = references[mode][index]; + const auto address = (reference & ~65535ull) | ((reference & 65535ull) ^ pipeBankXor); + Require(address + surface.elementBytes <= payload.size() && std::equal(expected.begin() + source, expected.begin() + source + surface.elementBytes, payload.begin() + address), "rendered readback differs at an AMD reference fragment address"); + } + std::vector restored(expected.size()); + layout.Detile(payload, restored); + Require(restored == expected && linear == expected, "readback retile roundtrip changed rendered element bits"); + for (std::size_t index = 0u; index < tiled.size(); ++index) Require((index >= 16u && index < 16u + payload.size()) || tiled[index] == std::byte{0xa5}, "readback retile changed an allocation sentinel"); + std::printf("multisample readback retile: mode=%s xor=0x%x samples=%u layers=%u elements=%u reference-points=7 padding/sentinels/roundtrip=passed\n", mode == 0u ? "R_X" : "Z_X", pipeBankXor, surface.samples, surface.layers, elements); + } + } +} + template void Refused(TAction action, const char* expected) { std::string reason; @@ -445,7 +499,7 @@ int main(int argc, char** argv) { else if (mode == "uint16") { format = VK_FORMAT_R16_UINT; bytes = 2u; } else if (mode == "uint64") { format = VK_FORMAT_R32G32_UINT; bytes = 8u; } else if (mode == "uint128") { format = VK_FORMAT_R32G32B32A32_UINT; bytes = 16u; } - else Require(mode == "uint32" || mode == "depth" || mode == "upload", "unknown multisample readback test mode"); + else Require(mode == "uint32" || mode == "depth" || mode == "upload" || mode == "retile", "unknown multisample readback test mode"); if (mode == "depth") format = VK_FORMAT_D32_SFLOAT; const auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; @@ -472,6 +526,13 @@ int main(int argc, char** argv) { auto limited = context; limited.limits.maxComputeWorkGroupCount[0] = 1u; TextureDetiler reader(limited); + if (mode == "retile") { + if (!SurfaceSupported(context, format, 4u, 129u, 67u, 17u)) return VulkanTestSkipped; + Surface surface(context, format, bytes, 4u, 129u, 67u, 17u); + surface.Render(); + CheckRetile(reader, surface); + return 0; + } std::uint32_t checked = 0u; for (const auto samples : {2u, 4u, 8u}) { if (!SurfaceSupported(context, format, samples)) continue; From b65a553db6d8bbfeb158ecacc81305a5a16f2cb8 Mon Sep 17 00:00:00 2001 From: Romans Pokrovskis Date: Fri, 16 Oct 2026 20:34:33 +0300 Subject: [PATCH 142/523] test(agc): retile distinct D32 sample readback --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 2 +- .../tests/execution/MultisampleReadback.cpp | 12 +++++++----- .../tests/shaders/MultisampleReadback.frag | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index d4a7c32860..8c80040c35 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -2309,7 +2309,7 @@ if(BUILD_TESTING) set_tests_properties(agc_driver_queued_raw_shader PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_PIPELINED_DRAWS=8" TIMEOUT 30) add_test(NAME agc_driver_partially_resident_pending_stores COMMAND agc_driver_partially_resident_snapshot_tests --pending-stores) set_tests_properties(agc_driver_multisample_readback PROPERTIES TIMEOUT 20) - foreach(agcReadbackCase IN ITEMS uint8 uint16 uint64 uint128 depth upload retile) + foreach(agcReadbackCase IN ITEMS uint8 uint16 uint64 uint128 depth upload retile depth_retile) add_test(NAME agc_driver_multisample_readback_${agcReadbackCase} COMMAND agc_driver_multisample_readback_tests ${agcReadbackCase}) set_tests_properties(agc_driver_multisample_readback_${agcReadbackCase} PROPERTIES TIMEOUT 20 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") if(WIN32) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp index 62a82c3421..a6c84ad27f 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/MultisampleReadback.cpp @@ -276,7 +276,7 @@ struct Surface { const auto layer = pixel / (width * height); const auto value = x + y * width + layer * width * height + sample * 31u; std::array words{0x91352700u ^ value, 0x45219b00u ^ (value * 3u), 0xc7391d00u ^ (value * 5u), 0x6b43e100u ^ (value * 7u)}; - if (format == VK_FORMAT_D32_SFLOAT) words[0] = std::bit_cast(0.125f + static_cast(element + 1u) / 65536.0f); + if (format == VK_FORMAT_D32_SFLOAT) words[0] = std::bit_cast(0.125f + static_cast(element + 1u) / 1048576.0f); std::memcpy(result.data() + static_cast(index) * elementBytes, words.data(), elementBytes); } return result; @@ -357,7 +357,7 @@ void CheckRetile(TextureDetiler& reader, const Surface& surface) { layout.Detile(payload, restored); Require(restored == expected && linear == expected, "readback retile roundtrip changed rendered element bits"); for (std::size_t index = 0u; index < tiled.size(); ++index) Require((index >= 16u && index < 16u + payload.size()) || tiled[index] == std::byte{0xa5}, "readback retile changed an allocation sentinel"); - std::printf("multisample readback retile: mode=%s xor=0x%x samples=%u layers=%u elements=%u reference-points=7 padding/sentinels/roundtrip=passed\n", mode == 0u ? "R_X" : "Z_X", pipeBankXor, surface.samples, surface.layers, elements); + std::printf("multisample readback retile: format=%u mode=%s xor=0x%x samples=%u layers=%u elements=%u reference-points=7 padding/sentinels/roundtrip=passed\n", static_cast(surface.format), mode == 0u ? "R_X" : "Z_X", pipeBankXor, surface.samples, surface.layers, elements); } } } @@ -499,8 +499,8 @@ int main(int argc, char** argv) { else if (mode == "uint16") { format = VK_FORMAT_R16_UINT; bytes = 2u; } else if (mode == "uint64") { format = VK_FORMAT_R32G32_UINT; bytes = 8u; } else if (mode == "uint128") { format = VK_FORMAT_R32G32B32A32_UINT; bytes = 16u; } - else Require(mode == "uint32" || mode == "depth" || mode == "upload" || mode == "retile", "unknown multisample readback test mode"); - if (mode == "depth") format = VK_FORMAT_D32_SFLOAT; + else Require(mode == "uint32" || mode == "depth" || mode == "upload" || mode == "retile" || mode == "depth_retile", "unknown multisample readback test mode"); + if (mode == "depth" || mode == "depth_retile") format = VK_FORMAT_D32_SFLOAT; const auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex()); @@ -526,11 +526,13 @@ int main(int argc, char** argv) { auto limited = context; limited.limits.maxComputeWorkGroupCount[0] = 1u; TextureDetiler reader(limited); - if (mode == "retile") { + if (mode == "retile" || mode == "depth_retile") { if (!SurfaceSupported(context, format, 4u, 129u, 67u, 17u)) return VulkanTestSkipped; Surface surface(context, format, bytes, 4u, 129u, 67u, 17u); surface.Render(); CheckRetile(reader, surface); + Require(checkedPipelineCacheUses != 0u, "retile fixture did not check pipeline cache use"); + std::printf("multisample retile guarded pipeline creation checks=%u passed\n", checkedPipelineCacheUses); return 0; } std::uint32_t checked = 0u; diff --git a/core/libs/prx/libSceAgcDriver/tests/shaders/MultisampleReadback.frag b/core/libs/prx/libSceAgcDriver/tests/shaders/MultisampleReadback.frag index 3a72da6058..2f0e104c90 100644 --- a/core/libs/prx/libSceAgcDriver/tests/shaders/MultisampleReadback.frag +++ b/core/libs/prx/libSceAgcDriver/tests/shaders/MultisampleReadback.frag @@ -10,7 +10,7 @@ void main() { uint y = uint(gl_FragCoord.y); #if READ_DEPTH uint element = (x + y * params.width + params.layer * params.width * params.height) * params.samples + uint(gl_SampleID); - gl_FragDepth = 0.125 + float(element + 1u) / 65536.0; + gl_FragDepth = 0.125 + float(element + 1u) / 1048576.0; #else uint value = x + y * params.width + params.layer * params.width * params.height + uint(gl_SampleID) * 31u; color = uvec4(0x91352700u ^ value, 0x45219b00u ^ (value * 3u), 0xc7391d00u ^ (value * 5u), 0x6b43e100u ^ (value * 7u)); From 61fca3ae9b0fb38b5aadcc2b001f8f70475347fb Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Wed, 21 Oct 2026 15:28:50 +0200 Subject: [PATCH 143/523] fix(libSceUlt): keep the reader-writer lock state alive until sceUltReaderWriterLockDestroy has unlocked it Destroy locked the state's mutex through a reference into the map entry and then erased the entry. When that held the last shared_ptr, the state was freed before the lock guard unlocked its mutex. Take a shared_ptr copy before the guard so the state outlives it. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp b/core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp index 7e1273394a..c947260041 100644 --- a/core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp +++ b/core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp @@ -107,7 +107,8 @@ int APS5_VABI sceUltReaderWriterLockDestroy(void* rwlock) { if (it == gLocks.end()) { return ULT_ERROR_STATE; } - auto& state = *it->second; + const auto owner = it->second; + auto& state = *owner; std::lock_guard stateLock(state._mutex); if (state._readers != 0 || state._writing || state._waiters != 0) { return ULT_ERROR_BUSY; From 0bd8f6188a39e65c05b709d9bf59ba5e613de789 Mon Sep 17 00:00:00 2001 From: thuanquang Date: Mon, 19 Oct 2026 15:30:53 +0700 Subject: [PATCH 144/523] fix(agc): draw triangle strips through a geometry program with the last provoking vertex An RX 6700 XT hands a geometry shader odd strip triangle i as (i+1, i, i+2) with the last provoking vertex, which is the order the translator already uses, and since #7374 last mode takes the flat outputs of the program's primitives from their last index. Keep the last-mode refusal for fans only, which reach the program as (i+1, i+2, 0) while the hardware gives (0, i+1, i+2). TEKKEN DARK RESURRECTION (PPSA31371) stops on such a strip draw. Co-Authored-By: Claude --- .../libSceAgcDriver/Graphics/src/Pipeline.cpp | 2 +- .../prx/libSceAgcDriver/tests/Graphics.cpp | 10 ++++-- .../tests/execution/MeshProvokingVertex.cpp | 36 +++++++++++++++---- docs/dev/debt/libSceAgcDriver.md | 2 +- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp index 20ce2e4e8b..faae8726e5 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp @@ -122,7 +122,7 @@ void ValidateProvokingVertex(const Context& context, const State& state, std::sp Require(context.provokingVertexLast, "last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled"); const bool meshPrimitives = state.stages.path == ShaderPath::Geometry && state.stages.mesh; Require(!state.stages.tessellation && (meshPrimitives || (state.stages.path == ShaderPath::Vertex && !state.stages.mesh)), "last provoking vertex is unsupported for generated primitive pipelines"); - Require(!meshPrimitives || (state.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP && state.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN), "last provoking vertex is unsupported for triangle strips or fans assembled for a geometry program"); + Require(!meshPrimitives || state.topology != VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, "last provoking vertex is unsupported for triangle fans assembled for a geometry program"); Require(state.rectList || state.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || state.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || state.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP || state.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST || state.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP || state.topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, "last provoking vertex is unsupported for this primitive topology"); for (const auto& shader : shaders) { if (shader.stage != ShaderRecompiler::ShaderStage::Fragment) continue; diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 12b11af2df..a7feeeac37 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -9161,10 +9161,16 @@ void ProvokingVertexTests() { meshDraw.topology = topology; for (const auto& drawn : {std::span{}, std::span(flatShaders), std::span(flatP10Shaders), std::span(smoothShaders), std::span(slotShaders)}) ValidateProvokingVertex(supported, meshDraw, drawn); } + meshDraw.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + for (const auto& drawn : {std::span{}, std::span(flatShaders), std::span(flatP10Shaders), std::span(smoothShaders)}) ValidateProvokingVertex(supported, meshDraw, drawn); + fragment.fragmentParameters = {{0, 0, false, true, false, false, false}, {1, 1, false, true, false, false, false}, {2, 2, false, true, false, false, false}, {3, 3, false, true, false, false, false}}; + ValidateProvokingVertex(supported, meshDraw, shaders); + expectFailure([&] { ValidateProvokingVertex(supported, meshDraw, slotShaders); }, "last provoking vertex with explicit per-vertex triangle-strip interpolation is unsupported"); + meshDraw.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + expectFailure([&] { ValidateProvokingVertex(supported, meshDraw, flatShaders); }, "last provoking vertex is unsupported for triangle fans assembled for a geometry program"); for (const auto topology : {VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN}) { - meshDraw.topology = topology; - expectFailure([&] { ValidateProvokingVertex(supported, meshDraw, flatShaders); }, "last provoking vertex is unsupported for triangle strips or fans assembled for a geometry program"); auto firstDraw = meshDraw; + firstDraw.topology = topology; firstDraw.provokingVertexMode = VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT; ValidateProvokingVertex(supported, firstDraw, flatShaders); } diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/MeshProvokingVertex.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/MeshProvokingVertex.cpp index 3c7b2a9333..1cb6bea34a 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/MeshProvokingVertex.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/MeshProvokingVertex.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -23,9 +24,9 @@ constexpr std::uint32_t Width = 64; constexpr std::uint32_t Height = 64; using Image = std::array; alignas(256) Image Pixels{}; -alignas(256) constexpr std::array Indices{0, 1, 2, 0}; -alignas(256) constexpr std::array, 3> Vertices{{ - {-0.9f, -0.8f, 0, 1, 1, 0, 0, 1}, {-0.1f, -0.8f, 0, 1, 0, 1, 0, 1}, {-0.5f, 0.8f, 0, 1, 0, 0, 1, 1} +alignas(256) constexpr std::array Indices{0, 1, 2, 3}; +alignas(256) constexpr std::array, 4> Vertices{{ + {-0.9f, -0.8f, 0, 1, 1, 0, 0, 1}, {-0.1f, -0.8f, 0, 1, 0, 1, 0, 1}, {-0.5f, 0.8f, 0, 1, 0, 0, 1, 1}, {0.3f, 0.8f, 0, 1, 1, 1, 0, 1} }}; alignas(256) constexpr std::array FlatPixelCode{0xc8120002u, 0xc8160102u, 0xc81a0202u, 0xc81e0302u, 0xf800180fu, 0x07060504u, 0xbf810000u}; alignas(256) constexpr std::array SmoothPixelCode{0xc8100000u, 0xc8110001u, 0xc8140100u, 0xc8150101u, 0xc8180200u, 0xc8190201u, 0xc81c0300u, 0xc81d0301u, 0xf800180fu, 0x07060504u, 0xbf810000u}; @@ -64,7 +65,7 @@ void Write(std::vector& commands, std::uint32_t opcode, std::uint commands.insert(commands.end(), {0xc0010000u | (opcode << 8u), offset, value}); } -Image Draw(const std::uint32_t* pixelCode, bool flat, bool last) { +Image Draw(const std::uint32_t* pixelCode, bool flat, bool last, bool strip = false) { Pixels.fill(std::byte{0}); const auto color = reinterpret_cast(Pixels.data()); const auto geometry = reinterpret_cast(GeometryCode.data()); @@ -98,13 +99,14 @@ Image Draw(const std::uint32_t* pixelCode, bool flat, bool last) { {0x8, static_cast(pixel >> 8u)}, {0x9, static_cast(pixel >> 40u)}, {0xb, 0} }; std::vector commands; - Write(commands, 0x79, 0x242, 4); + Write(commands, 0x79, 0x242, strip ? 6u : 4u); Write(commands, 0x79, 0x25b, (12u << 9u) | 4u); for (const auto& [offset, value] : context) Write(commands, 0x69, offset, value); for (const auto& [offset, value] : shaders) Write(commands, 0x76, offset, value); commands.insert(commands.end(), {0xc0002a00u, 0u}); const auto address = reinterpret_cast(Indices.data()); - commands.insert(commands.end(), {0xc0042700u, 3u, static_cast(address), static_cast(address >> 32u), 3u, 0u}); + const std::uint32_t count = strip ? 4u : 3u; + commands.insert(commands.end(), {0xc0042700u, count, static_cast(address), static_cast(address >> 32u), count, 0u}); Packet packet{commands.data(), static_cast(commands.size()), 0, {}}; Require(sceAgcDriverSubmitDcb(&packet) == 0, "mesh submission failed"); AgcDriverWaitIdle_nid_postfix(); @@ -132,6 +134,25 @@ void FlatInputs() { } } +constexpr std::array, 4> SecondInside{{{26, 48}, {32, 48}, {29, 44}, {29, 52}}}; + +void CheckFlat(const Image& image, std::span> pixels, const std::array& expected, const std::string& what) { + for (const auto& [x, y] : pixels) { + for (std::uint32_t channel = 0; channel < 4; ++channel) { + Require(Channel(image, x, y, channel) == expected[channel], what + ": pixel (" + std::to_string(x) + ", " + std::to_string(y) + ") channel " + std::to_string(channel) + " is " + std::to_string(Channel(image, x, y, channel)) + ", expected " + std::to_string(expected[channel])); + } + } +} + +void StripFlatInputs() { + const auto last = Draw(FlatPixelCode.data(), true, true, true); + CheckFlat(last, Inside, {0u, 0u, 255u, 255u}, "triangle strip, last provoking vertex, triangle 0 (the color of vertex 2)"); + CheckFlat(last, SecondInside, {255u, 255u, 0u, 255u}, "triangle strip, last provoking vertex, triangle 1 (the color of vertex 3)"); + Require(Channel(last, 48, 48, 3) == 0, "triangle strip, last provoking vertex: the draw wrote outside its triangles"); + const auto first = Draw(FlatPixelCode.data(), true, false, true); + CheckFlat(first, Inside, {255u, 0u, 0u, 255u}, "triangle strip, first provoking vertex, triangle 0 (the color of vertex 0)"); +} + void SmoothInputs() { const auto first = Draw(SmoothPixelCode.data(), false, false); for (const auto& [x, y] : Inside) { @@ -169,9 +190,10 @@ int main() { Register(FlatPixelHeader, FlatPixelCode.data(), sizeof(FlatPixelCode), 1); Register(SmoothPixelHeader, SmoothPixelCode.data(), sizeof(SmoothPixelCode), 1); FlatInputs(); + StripFlatInputs(); if (smooth) SmoothInputs(); AgcDriverShutdown_nid_postfix(); - std::cout << "geometry-program triangles take flat inputs from vertex 0 with the first provoking vertex and from vertex 2 with the last" << (smooth ? ", and interpolate smooth inputs the same in both modes\n" : "; smooth inputs skipped without fragment shader barycentrics\n"); + std::cout << "geometry-program triangles take flat inputs from vertex 0 with the first provoking vertex and from vertex 2 with the last, and the second triangle of a strip takes them from vertex 3 with the last" << (smooth ? ", and interpolate smooth inputs the same in both modes\n" : "; smooth inputs skipped without fragment shader barycentrics\n"); return 0; } catch (const std::exception& error) { std::cerr << error.what() << '\n'; diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index c4ea080793..1dd66f3d53 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -134,7 +134,7 @@ - [PA_CL_CLIP_CNTL](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - `CLIP_DISABLE` (bit 16) and `DX_LINEAR_ATTR_CLIP_ENA` (bit 24) are accepted without changing pipeline state: Vulkan clips primitives against viewport guard bands and clips `NoPerspective` outputs linearly; what clearing bit 24 or setting bit 16 changes on the console beyond standard clipping is undocumented. - [10_11_11 unorm color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) render into R32_UINT attachments: the pixel shader packs each export, rounded to nearest even like format-30 image stores, which is assumed to match the CB's conversion. - [11_11_10 float color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) render into R32_UINT attachments: the pixel shader packs each export toward zero like format-43 image stores (overflow to the largest finite value, negatives to zero, NaN kept), which is assumed to match the CB's conversion; only the reversed component order (COMP_SWAP 2) is accepted. -- [PA_SU_SC_MODE_CNTL.PROVOKING_VTX_LAST](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp) (libSceAgcDriver) uses Vulkan's last-vertex mode for native primitives. Bit 19 follows [opengnm's `flatshade_first` mapping](https://github.com/blackbearreloaded/ps5-opengl/blob/db752da4c89380af228497b5b6852163a4e61069/src/gallium/ps5/ps5_screen.c#L2244); vertex selection follows [Vulkan](https://docs.vulkan.org/spec/latest/chapters/drawing.html#drawing-primitive-topologies), without console measurements. A [rect-list](../../../core/shader/recompiler/SpirvBackend/src/RectListShaders.cpp) draw in last mode gives all four corners of each rectangle the flat inputs of its third vertex through the generated control stage, as first mode gives them those of its first, so Vulkan's choice of provoking vertex for the tessellated triangles does not matter; which vertex the console takes for a rectangle is not measured either. A geometry program's primitives keep the vertex indices of its own primitive export on the [mesh path](../../../core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp), so last mode takes their flat inputs (from [Vulkan](https://docs.vulkan.org/spec/latest/chapters/vertexpostproc.html#vertexpostproc-flatshading)) and their [layer](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) from the last index of each primitive, while their per-vertex inputs keep the order of first mode ([Vulkan](https://docs.vulkan.org/spec/latest/chapters/primsrast.html#primsrast-barycentric)); that the console does the same for an exported primitive is not measured. Last mode throws without `VK_EXT_provoking_vertex`, except for the [generated barycentric tessellation stages](../../../core/shader/recompiler/SpirvBackend/src/BarycentricGeometryShader.cpp), which write the last vertex's flat values at every corner, for tessellation primitives, for triangle strips or fans assembled for a geometry program (whose odd-triangle order on the console is not measured), explicit per-vertex flat inputs, and triangle strips whose pixel shader reads a vertex slot itself (`v_interp_mov_f32`, or 16-bit interpolation, which is not checked yet), since the vertex order of odd strip triangles [depends on the device](https://docs.vulkan.org/refpages/latest/refpages/source/VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR.html). `v_interp_p1_f32` and `v_interp_p2_f32` read P10 and P20 against the guest's I and J, which come from `gl_BaryCoordKHR` in the same vertex order as the per-vertex values, so the order cancels out and strips with only those reads draw. A FLAT_SHADE input without pass-through that is read through `v_interp_mov_f32` P10 and not P20 [returns the provoking vertex's value](../../../core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp), like P0. ps5-opengl reads every flat input that way, and its driver passes the GL 4.6 CTS on a PS5 (#1413). The lane itself was not measured on the console, so with the first provoking vertex such a shader throws. +- [PA_SU_SC_MODE_CNTL.PROVOKING_VTX_LAST](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp) (libSceAgcDriver) is not measured on the console: bit 19 follows opengnm's `flatshade_first` and Vulkan's vertex selection; rect lists take flat inputs from the third vertex (first mode: first); a geometry program gets odd strip triangle i as (i+1, i, i+2) in both modes (an RX 6700 XT, first mode: (i, i+2, i+1)); in last mode its primitives take flat inputs and layer from the last index, per-vertex ones in first-mode order; a flat input without pass-through read only through `v_interp_mov_f32` P10 gives the provoking vertex's value (ps5-opengl, #1413; first mode throws). Barycentric tessellation stages give each corner the last vertex's flat values. Else last mode throws without `VK_EXT_provoking_vertex`, for tessellation, geometry-program fans, explicit per-vertex flat inputs, and strips whose pixel shader reads a vertex slot (`v_interp_mov_f32`; 16-bit interpolation unchecked). - [Dual-source blending](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) takes the MRT1 export as the second source of the MRT0 blend, as RADV and radeonsi program it; what the CB does with other export or target layouts is unknown, so they throw. - [Blending into reversed-order color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp) (libSceAgcDriver) - 8_8_8_8 and 16_16_16_16 targets with `COMP_SWAP` 2 or 3 keep the exported alpha in their first channel and red or blue in their fourth, which Vulkan would blend with the color and the alpha equation respectively. The color block is taken to blend in shader export order and apply `COMP_SWAP` afterwards: the alpha equation and every source-alpha factor use the exported alpha, and the destination alpha is the first channel. This follows the `CB_TARGET_MASK` text of the AMD [SI](https://xorg.freedesktop.org/docs/AMD/old/SI_3D_registers.pdf) (p. 229) and [CIK](https://xorg.freedesktop.org/docs/AMD/old/CIK_3D_registers_v2.pdf) (p. 238) register references ("the order of components after blending and before COMP_SWAP is applied"). RADV builds `CB_BLENDn_CONTROL` without regard to the swap ([radv_pipeline_graphics.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-26.2.2/src/amd/vulkan/radv_pipeline_graphics.c#L650-L709)) and renders R4G4B4A4, B4G4R4A4, R5G5B5A1 and B5G5R5A1 with `SWAP_STD_REV` and `SWAP_ALT_REV` ([vk_format.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-26.2.2/src/vulkan/util/vk_format.c#L43-L48), [ac_formats.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/mesa-26.2.2/src/amd/common/ac_formats.c#L609-L624)). Mesa CI's Navi10 (gfx1010) CTS run ([job 112381553](https://gitlab.freedesktop.org/mesa/mesa/-/jobs/112381553), [112381554](https://gitlab.freedesktop.org/mesa/mesa/-/jobs/112381554)) passes all 400 `blend.format..states` cases of each of those formats, including separate alpha and destination-alpha factors. It is not measured on gfx1013 or on 8_8_8_8 and 16_16_16_16 targets. Factor pairs Vulkan applies per channel are used directly. Otherwise the pixel shader only copies values: it writes the exported alpha, each channel's own export or the constants 0 and 1 into a second color at location 0 index 1 (and 1 into a channel of the first color whose source factor is ONE, moving its export into the factor), and the blend forms every factor, `1 -` and product from those copies through `SRC1_COLOR`/`SRC1_ALPHA` factors. The blend unit therefore rounds them as it rounds a plain Vulkan blend of the same export; Vulkan does not require a second-source color to be converted like the first, but on an RTX 5080 and lavapipe every reversed-order case of the execution test is bit-identical to the same draw into a `SWAP_STD` target, which the test checks. Factor combinations that copies cannot express (for example `ONE_MINUS_SRC_ALPHA` in the alpha equation with `SRC_COLOR` in the color equation), sRGB, SNORM and integer targets, constant and second-source factors, destination-alpha and `SRC_ALPHA_SATURATE` factors in the color equation, different color and alpha operations, MIN or MAX with factors other than ONE, other color exports when the blend needs the second color, and devices without `dualSrcBlend` throw. Gran Turismo 7 (PPSA01317) blends `CB_BLEND0_CONTROL` 0x65010504 into such a target (#4782). - [DB_SHADER_CONTROL.CONSERVATIVE_Z_EXPORT](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp) (libSceAgcDriver) becomes `DepthLess`/`DepthGreater`. For a pixel shader that breaks the promise, the host driver decides whether its interpolated Z is depth-tested before shading; what the console does then was not measured. From f40e1b68c88bec2007ceeaef67742902f2deae87 Mon Sep 17 00:00:00 2001 From: Dean Galvin Date: Thu, 22 Oct 2026 03:57:05 +1300 Subject: [PATCH 145/523] test(avplayer): wait for the frame in the sync tolerance check The sync tolerance test asked for the next frame once, without waiting, so on a loaded runner a decoder that had not queued frame 1 yet failed the delivery check, and the premature-delivery check passed for the same reason. Poll for the frame in both checks: with a bounded wait for the premature case and the usual wait for delivery. --- core/libs/tests/GuestAvPlayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/tests/GuestAvPlayer.cpp b/core/libs/tests/GuestAvPlayer.cpp index efa5ff1e00..68ef734c2f 100644 --- a/core/libs/tests/GuestAvPlayer.cpp +++ b/core/libs/tests/GuestAvPlayer.cpp @@ -1596,9 +1596,9 @@ void TestVideoSyncTolerance() { Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "sync tolerance first frame missing"); Check(FrameIndex(frame.timestamp) == 0, "first frame not index 0"); AvPlayer::SetAvPlayerClockForTest_nid_no_patch(player, 5.0); - Check(!sceAvPlayerGetVideoDataEx(player, &frame), "frame beyond sync tolerance delivered prematurely"); + Check(!WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }, 200), "frame beyond sync tolerance delivered prematurely"); AvPlayer::SetAvPlayerClockForTest_nid_no_patch(player, 10.0); - Check(sceAvPlayerGetVideoDataEx(player, &frame), "frame within sync tolerance was not delivered"); + Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "frame within sync tolerance was not delivered"); Check(FrameIndex(frame.timestamp) == 1, "unexpected second frame index"); Check(sceAvPlayerClose(player) == 0, "sync tolerance close failed"); std::lock_guard lock(allocations.mutex); From 0b411ccee0c07a670f85e11df1f7c39b2368a72e Mon Sep 17 00:00:00 2001 From: Maz Date: Wed, 21 Oct 2026 19:08:57 +0400 Subject: [PATCH 146/523] docs(architecture): record main-module entry register contracts Answer #9048 from the host stubs: Linux, Windows and macOS differ, and the console is not measured. --- docs/dev/ARCHITECTURE.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/dev/ARCHITECTURE.md b/docs/dev/ARCHITECTURE.md index 574244a6da..0c74e50345 100644 --- a/docs/dev/ARCHITECTURE.md +++ b/docs/dev/ARCHITECTURE.md @@ -45,6 +45,18 @@ flowchart LR - [`core/relinker/main.cpp`](../../core/relinker/main.cpp) runs the steps in this order. `--to-intel` is optional, see [USAGE.md](../user/USAGE.md). - Each library in [`core/libs/prx`](../../core/libs/prx) builds as a shared library. After the build, `nid_patcher` ([`core/libs/nid`](../../core/libs/nid)) renames every export to its NID, computed from the function name. `APS5_EXPORT("", func)` sets the NID directly when the name is unknown. Every other library keeps the names libc exports; it reads them from `libc.exports`, which `nid_patcher --write-export-list` rewrites only when that list changes. On Windows the link writes each import library with `SOURCE_DATE_EPOCH=0` and `ranlib -D` and replaces it only when its bytes change. A source edit that keeps the exports therefore relinks and re-patches only its own library. +## Main-module entry registers + +The host stubs that transfer control to the guest `e_entry` are not the same on every platform, and the console's register contract at that point is not measured here (#9048). What the stubs establish today: + +| Host | Source | `rdi` | `rsi` | `rdx` | `rbp` | Return from entry | +|---|---|---|---|---|---|---| +| Linux | [`EntryStubBuilder.cpp`](../../core/relinker/elfpatcher/src/general/EntryStubBuilder.cpp) | process stack: qword `argc`, then argv pointers, then a null | 0 | left as the host dynamic linker left it (often `rtld_fini`) | zero frame `{0, 0}` | `ud2` | +| Windows | [`WindowsEntryStubBuilder.cpp`](../../core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp) | `VirtualAlloc` block: qword `argc`, UTF-8 argv pointers, null | address of an exit trampoline that finalizes guest modules and returns | not set by the stub (last host call is `FreeLibrary`) | zero frame in the stub's stack | `ExitProcess` with the entry's return code | +| macOS | [`MacOsImage.cpp`](../../core/relinker/elfpatcher/src/macos/MacOsImage.cpp) | guest entry address into `_Aps5StartGuest_nid_no_patch` | stack block `{argc, argv…, 0, envp…, 0}` | — | — | `Aps5StartGuest` has no implementation on `main`, so the final guest-entry registers are unset | + +On Linux the argument block is the process stack itself (`mov rdi, rsp`). On Windows it is a separate allocation the stub fills with UTF-8 argument strings. Titles that read only `argc`/`argv` through `rdi` see the same layout shape on both; titles that call through `rsi` on Windows get a host exit path that Linux does not provide (`rsi` is zero there). + ## Game patches [`GamePatch.cpp`](../../core/relinker/relinker/src/patching/GamePatch.cpp) parses the patch file, a strict subset of TOML. `Conversion::Convert` applies the entries of its own target to the executable bytes right after reading them, and to each bundled module before `GuestModuleBuilder` reads it, so every later stage sees patched bytes. Entry virtual addresses are translated through the `PT_LOAD` segments, and a range may not touch the ELF header or program header table, which the output writers rewrite. The patched file ranges are then checked against every in-place writer: the `--to-intel` reports and trampoline sites, `RelinkPatch` records and syscall trap ranges. The patched virtual address ranges are checked against the 8-byte targets of the `RelaData` and `RelaPltData` relocations, which the loader or the output writers overwrite. `main.cpp` builds the manifest text before it writes any output. `main.cpp` matches targets after the conversions, aggregates the applied records into `.patches.toml`, and runs `--verify-patches` through `GamePatch::VerifyPatches`, which reads only bytes. From a9b14023dd2c4023a9da426366a3c81b02a7206b Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:55:14 +0000 Subject: [PATCH 147/523] fix(agc): retire host imports before range cuts --- core/libs/prx/libSceAgcDriver/CMakeLists.txt | 6 +- .../Graphics/src/GuestBufferMemory.cpp | 136 ++---------------- .../tests/HostImportLifetime.cpp | 5 +- .../prx/libSceAgcDriver/tests/Recorder.cpp | 51 +++---- .../tests/execution/GuestMemoryLifetime.cpp | 4 +- 5 files changed, 41 insertions(+), 161 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/CMakeLists.txt b/core/libs/prx/libSceAgcDriver/CMakeLists.txt index 177d7b1d5c..8680fd9f39 100644 --- a/core/libs/prx/libSceAgcDriver/CMakeLists.txt +++ b/core/libs/prx/libSceAgcDriver/CMakeLists.txt @@ -1450,9 +1450,9 @@ if(BUILD_TESTING) endif() if(WIN32) add_test(NAME agc_reimport_bench COMMAND agc_driver_recorder_tests --reimport-bench) - add_test(NAME agc_trimmed_import COMMAND agc_driver_recorder_tests --trimmed-import-only) - set_tests_properties(agc_reimport_bench agc_trimmed_import PROPERTIES SKIP_RETURN_CODE 77) - set_tests_properties(agc_reimport_bench agc_trimmed_import PROPERTIES ENVIRONMENT_MODIFICATION + add_test(NAME agc_host_import_cut_lifetime COMMAND agc_driver_recorder_tests --host-import-cut-only) + set_tests_properties(agc_reimport_bench agc_host_import_cut_lifetime PROPERTIES SKIP_RETURN_CODE 77) + set_tests_properties(agc_reimport_bench agc_host_import_cut_lifetime PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() add_test(NAME agc_unchanged_cpu_stamps COMMAND agc_driver_recorder_tests --unchanged-cpu-stamps-only) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index 610c437369..e66a4dc0b2 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -232,7 +232,6 @@ struct HostImports { PFN_vkDestroyBuffer destroyBuffer = nullptr; PFN_vkFreeMemory freeMemory = nullptr; std::map imports; - std::map trimmed; std::map>> parked; std::uint64_t liveBytes = 0; std::map failed; @@ -458,64 +457,6 @@ void shareDmaBufImport(const Context& context, VkBufferCreateInfo& info, ImportF } #endif -void releaseTrimmed(HostImports& state, ImportNode node) { - state.liveBytes -= node.mapped().pinnedBytes; - auto holder = std::make_shared(state.device, state.destroyBuffer, state.freeMemory, std::move(node)); - auto* recorder = Recorder::Active(); - if (recorder != nullptr) recorder->KeepThroughHold(std::move(holder)); -} - -void dropTrimmed(HostImports& state, std::uint64_t begin, std::uint64_t end) { - for (auto it = state.trimmed.begin(); it != state.trimmed.end();) { - const auto next = std::next(it); - if (it->first < end && begin < it->first + it->second.bytes) releaseTrimmed(state, state.trimmed.extract(it)); - it = next; - } -} - -void retireOrTrim(VkDevice device, HostImports& state, std::map::iterator it, const GuestAllocations::Lease& lease, std::uint64_t begin, std::uint64_t end) { - const auto& entry = it->second; - const auto entryEnd = entry.base + entry.bytes; - std::uint64_t first = 0; - std::uint64_t last = 0; - if (begin <= entry.base && end > entry.base && end < entryEnd) { - first = end; - last = entryEnd; - } else if (begin > entry.base && begin < entryEnd && end >= entryEnd) { - first = entry.base; - last = begin; - } else if (begin > entry.base && begin < end && end < entryEnd) { - const bool tail = entryEnd - end >= begin - entry.base; - first = tail ? end : entry.base; - last = tail ? entryEnd : begin; - } - const bool plain = entry.alias == nullptr && !entry.dmaBuf && entry.chunk == nullptr && !entry.joined && entry.ranges.size() == 1 && entry.backingSerial != 0 && entry.memoryType != std::numeric_limits::max() && entry.base == entry.range->address && entry.bytes == entry.range->bytes; -#ifdef _WIN32 - const bool settling = state.pendingSettles.contains(it->first); -#else - const bool settling = false; -#endif - if (first == last || !plain || settling || state.trimmed.contains(first)) { - retireImport(device, state, it, lease, false); - return; - } - RetireShadow(device, entry, [&lease](std::uint64_t from, std::uint64_t to) { return runCovers(lease, from, to); }); - ++state.epoch; - auto node = state.imports.extract(it); - auto& kept = node.mapped(); - std::shared_ptr holder(nullptr, [device = state.device, destroy = state.destroyBuffer, buffer = std::exchange(kept.buffer, VK_NULL_HANDLE)](void*) { destroy(device, buffer, nullptr); }); - kept.range.reset(); - kept.ranges.clear(); - kept.base = first; - kept.bytes = last - first; - kept.address = 0; - kept.serial = 0; - node.key() = first; - state.trimmed.insert(std::move(node)); - auto* recorder = Recorder::Active(); - if (recorder != nullptr && !recorder->Idle()) recorder->Keep(std::move(holder)); -} - VkResult createImportBuffer(const Context& context, VkExternalMemoryHandleTypeFlagBits handleType, VkDeviceSize size, VkBuffer* buffer) { const VkExternalMemoryBufferCreateInfo external{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, nullptr, static_cast(handleType)}; VkBufferCreateInfo info{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &external}; @@ -1407,9 +1348,6 @@ bool sameRun(const Entry& entry, const ImportRun& run) { bool wouldRetireImport(const HostImports& state, const ImportRun& run) { if (run.ranges.empty()) return false; - for (const auto& [base, entry] : state.trimmed) { - if (base < run.base + run.bytes && run.base < base + entry.bytes) return true; - } const auto next = state.imports.lower_bound(run.base); if (next != state.imports.end()) { if (next->first == run.base && sameRun(next->second, run)) return false; @@ -1920,46 +1858,6 @@ const GuestAllocations::Range* containingRange(const GuestAllocations::Lease& le return range->readable && begin >= range->address && end <= range->address + range->bytes ? range.get() : nullptr; } -bool reattachTrimmed(const Context& context, HostImports& state, HostImport& entry, const GuestAllocations::Lease& lease) { - const auto first = entry.base; - const auto last = entry.base + entry.bytes; - std::shared_ptr kept; - for (auto it = std::lower_bound(lease.begin(), lease.end(), first, [](const auto& range, std::uint64_t value) { return range->address < value; }); it != lease.end() && (*it)->address < last; ++it) { - if ((*it)->backingSerial == entry.backingSerial && (*it)->address + (*it)->bytes <= last && (kept == nullptr || (*it)->bytes > kept->bytes)) kept = *it; - } - if (kept == nullptr) return false; - const auto next = state.imports.lower_bound(kept->address); - if (next != state.imports.end() && next->first < kept->address + kept->bytes) return false; - if (next != state.imports.begin() && std::prev(next)->second.base + std::prev(next)->second.bytes > kept->address) return false; - HostImport candidate{kept->address, kept->bytes, VK_NULL_HANDLE, VK_NULL_HANDLE, 0}; - candidate.range = kept; - candidate.ranges = {kept}; - if (!sameRange(candidate, lease)) return false; - const auto offset = kept->address - entry.memoryBase; - VkBuffer buffer = VK_NULL_HANDLE; - if (createImportBuffer(context, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, kept->bytes, &buffer) != VK_SUCCESS) return false; - const auto refuse = [&] { - context.Function("vkDestroyBuffer")(context.device, buffer, nullptr); - return false; - }; - VkMemoryRequirements requirements{}; - context.Function("vkGetBufferMemoryRequirements")(context.device, buffer, &requirements); - if (((requirements.memoryTypeBits >> entry.memoryType) & 1u) == 0 || requirements.alignment == 0 || offset % requirements.alignment != 0 || requirements.size > entry.pinnedBytes - offset) return refuse(); - if (context.Function("vkBindBufferMemory")(context.device, buffer, entry.memory, offset) != VK_SUCCESS) return refuse(); - const VkBufferDeviceAddressInfo addressInfo{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, buffer}; - const auto address = context.Function("vkGetBufferDeviceAddressKHR")(context.device, &addressInfo); - if (address == 0) return refuse(); - entry.base = kept->address; - entry.bytes = kept->bytes; - entry.buffer = buffer; - entry.address = address; - entry.range = kept; - entry.ranges = {kept}; - entry.serial = 0; - entry.lastUse = ++state.useCounter; - return true; -} - // Drops imports whose registered range changed or disappeared: their pages may no longer back the // guest addresses. Walks the imports only when the registry changed since the last walk. void refreshImports(const Context& context, HostImports& state, const GuestAllocations::Lease& lease) { @@ -1967,17 +1865,15 @@ void refreshImports(const Context& context, HostImports& state, const GuestAlloc #ifdef _WIN32 drainSettles(state); #endif - for (auto* held : {&state.imports, &state.trimmed}) { - while (!held->empty()) { - auto node = held->extract(held->begin()); - if (state.device != VK_NULL_HANDLE && state.destroyBuffer != nullptr && state.freeMemory != nullptr) { - state.parked[state.device].push_back(std::make_shared(state.device, state.destroyBuffer, state.freeMemory, std::move(node))); - continue; - } -#ifdef _WIN32 - GuestArena::GuestArenaUnmapAlias_nid_postfix(node.mapped().alias); -#endif + while (!state.imports.empty()) { + auto node = state.imports.extract(state.imports.begin()); + if (state.device != VK_NULL_HANDLE && state.destroyBuffer != nullptr && state.freeMemory != nullptr) { + state.parked[state.device].push_back(std::make_shared(state.device, state.destroyBuffer, state.freeMemory, std::move(node))); + continue; } +#ifdef _WIN32 + GuestArena::GuestArenaUnmapAlias_nid_postfix(node.mapped().alias); +#endif } state.liveBytes = 0; state.failed.clear(); @@ -2008,16 +1904,6 @@ void refreshImports(const Context& context, HostImports& state, const GuestAlloc }; std::erase_if(state.sparseRunEnds, [&](const auto& entry) { return !starts(entry.first); }); std::erase_if(state.sparseRunJoins, [&](const auto& entry) { return !starts(entry.first); }); - while (!state.trimmed.empty()) { - auto node = state.trimmed.extract(state.trimmed.begin()); - if (!reattachTrimmed(context, state, node.mapped(), lease)) { - releaseTrimmed(state, std::move(node)); - continue; - } - node.key() = node.mapped().base; - ++state.epoch; - state.imports.insert(std::move(node)); - } const auto budget = effectiveBudget(context, state); for (auto it = state.imports.begin(); it != state.imports.end();) { if (sameRange(it->second, lease, budget)) { @@ -2895,7 +2781,7 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) { continue; } const auto next = std::next(it); - retireOrTrim(imports.device, imports, it, lease, address, end); + retireImport(imports.device, imports, it, lease, false); it = next; retiredImport = true; } @@ -2961,7 +2847,7 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) { continue; } const auto next = std::next(it); - retireOrTrim(imports.device, imports, it, lease, address, end); + retireImport(imports.device, imports, it, lease, false); it = next; retiredImport = true; } @@ -3141,9 +3027,7 @@ void ClearHostImports(VkDevice device) { GuestArena::GuestArenaUnmapAlias_nid_postfix(entry.alias); #endif } - for (const auto& [address, entry] : state.trimmed) state.freeMemory(state.device, entry.memory, nullptr); state.imports.clear(); - state.trimmed.clear(); state.liveBytes = 0; state.failed.clear(); state.failedJoins.clear(); diff --git a/core/libs/prx/libSceAgcDriver/tests/HostImportLifetime.cpp b/core/libs/prx/libSceAgcDriver/tests/HostImportLifetime.cpp index 36d9aba45e..603d4bb0a2 100644 --- a/core/libs/prx/libSceAgcDriver/tests/HostImportLifetime.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/HostImportLifetime.cpp @@ -517,7 +517,7 @@ void partialUnmap(Fixture& fixture) { { GuestAllocations::Mutation mutation; mutation.Unmap(middle, part, [&](const void* piece, std::size_t bytes, const void* original, bool last) { - Require(!imported->freed.load(), "an unmap inside a host import freed the memory of the side it keeps"); + requireFreed(imported); Require(!other->freed.load(), "partial unmap retired a disjoint host import"); Require(piece == middle && bytes == part && original == allocation.data && !last, "partial unmap changed the wrong guest extent"); }); @@ -525,9 +525,8 @@ void partialUnmap(Fixture& fixture) { Require(!RegisteredReadableCovers(reinterpret_cast(middle), part), "the middle of a partial unmap remains registered"); const auto low = fixture.Import(allocation.Address(), part); const auto high = fixture.Import(allocation.Address() + 2 * part, part); - Require(high->memory == imported->memory && !imported->freed.load(), "the side kept from an unmap inside the host import was imported again"); + Require(imported->freed.load(), "the side kept from an unmap inside the host import reused memory imported across the unmapped range"); Require(high->address == allocation.Address() + 2 * part && high->bytes == part, "the kept side of the host import does not cover its remaining range"); - Require(low->memory != imported->memory, "the smaller side of an unmap inside the host import was not imported again"); Require(!low->freed.load() && !high->freed.load() && !other->freed.load(), "remaining partial mappings could not be imported"); { GuestAllocations::Mutation mutation; diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 497adb234f..e3a76c5ed2 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -2619,15 +2619,15 @@ int reimportBench(const Device& device) { throw; } gpu.lock(); - Require(importedAgain.empty(), "the kept side was imported again instead of trimmed after the " + importedAgain + " cuts"); - std::printf("re-import bench: every cut at the head, at the tail and inside the range kept the larger side by trimming the first import\n"); + Require(importedAgain == "head, tail, inside", "a cut kept memory imported across the changed host range instead of importing the remaining side again: " + importedAgain); + std::printf("re-import bench: every cut at the head, at the tail and inside the range retired the original import before importing the remaining side\n"); return 0; } -int trimmedImportTests(const Device& device) { +int hostImportCutTests(const Device& device) { const auto& context = device.GetContext(); if (context.hostImportAlignment == 0 || context.settleQueue == VK_NULL_HANDLE || !AgcDriver::GuestMemory::WriteWatched()) { - std::cout << "skipped, the trimmed import tests require host imports, a settle queue and write watching\n"; + std::cout << "skipped, the host import cut tests require host imports, a settle queue and write watching\n"; return 77; } constexpr std::size_t bytes = 32ull << 20u; @@ -2644,14 +2644,13 @@ int trimmedImportTests(const Device& device) { std::size_t keptEnd; std::size_t smaller; std::size_t smallerEnd; - bool trimmed; }; constexpr std::array cases{{ - {"a page made inaccessible inside the import, nearer its start", inside + guard, guard, false, inside + 2 * guard, bytes, 0, inside + guard, true}, - {"a page made inaccessible inside the import, nearer its end", bytes - inside - 2 * guard, guard, false, 0, bytes - inside - 2 * guard, bytes - inside - guard, bytes, true}, - {"an unmap of a 2 MiB run inside the import", inside, run, true, inside + run, bytes, 0, inside, true}, - {"an unmap of the import's first 2 MiB run", 0, run, true, run, bytes, 0, 0, true}, - {"an unmap of 1440 KiB at the start of the import's first 2 MiB run", 0, 1440ull << 10u, true, 1440ull << 10u, bytes, 0, 0, false}, + {"a page made inaccessible inside the import, nearer its start", inside + guard, guard, false, inside + 2 * guard, bytes, 0, inside + guard}, + {"a page made inaccessible inside the import, nearer its end", bytes - inside - 2 * guard, guard, false, 0, bytes - inside - 2 * guard, bytes - inside - guard, bytes}, + {"an unmap of a 2 MiB run inside the import", inside, run, true, inside + run, bytes, 0, inside}, + {"an unmap of the import's first 2 MiB run", 0, run, true, run, bytes, 0, 0}, + {"an unmap of 1440 KiB at the start of the import's first 2 MiB run", 0, 1440ull << 10u, true, 1440ull << 10u, bytes, 0, 0}, }}; struct Backing { void* mapping = nullptr; @@ -2679,15 +2678,14 @@ int trimmedImportTests(const Device& device) { const auto check = [&](const Case& test) { const std::string name = test.name; Backing backing; - Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, bytes, run, 0, &backing.physical) == 0, "allocate the trimmed import test backing"); - Require(sceKernelMapDirectMemory(&backing.mapping, bytes, 0x33, 0, backing.physical, run) == 0, "map the trimmed import test backing"); + Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, bytes, run, 0, &backing.physical) == 0, "allocate the host import cut test backing"); + Require(sceKernelMapDirectMemory(&backing.mapping, bytes, 0x33, 0, backing.physical, run) == 0, "map the host import cut test backing"); const auto address = reinterpret_cast(backing.mapping); auto* memory = static_cast(backing.mapping); for (std::size_t offset = 0; offset < bytes; offset += page) std::memset(memory + offset, static_cast(0x40u + (offset / page) % 64u), 64); gpu.lock(); const auto* whole = HostImportFor(context, address, bytes); Require(whole != nullptr && whole->base == address && whole->bytes == bytes && whole->alias == nullptr && !whole->unwatched, name + ": the range was not imported whole, settled and watched"); - const auto original = whole->memory; auto* cut = memory + test.offset; gpu.unlock(); if (test.unmap) { @@ -2702,15 +2700,13 @@ int trimmedImportTests(const Device& device) { const auto end = address + test.keptEnd; const auto* kept = HostImportFor(context, begin, static_cast(end - begin)); Require(kept != nullptr && kept->base == begin && kept->base + kept->bytes == end, name + ": the larger side is not imported whole"); - const auto keptMemory = kept->memory; - if (test.trimmed) Require(kept->memory == original, "the larger side of " + name + " was imported again instead of kept"); - else Require(kept->memory != original, "the side of " + name + " whose 2 MiB run the unmap replaced is served from the import that pinned the run's old pages"); + Require(kept->memoryBase == begin && kept->pinnedBytes == end - begin, "the side of " + name + " is served from the import that pinned the changed host range"); requireImportServesGuest(context, recorder, *kept, begin, end, ++salt, "the larger side of " + name); if (test.smallerEnd != test.smaller) { const auto* smaller = HostImportFor(context, address + test.smaller, test.smallerEnd - test.smaller); - Require(smaller != nullptr && smaller->base == address + test.smaller && smaller->bytes == test.smallerEnd - test.smaller && smaller->memory != original, "the smaller side of " + name + " is not imported apart from the larger one"); + Require(smaller != nullptr && smaller->base == address + test.smaller && smaller->bytes == test.smallerEnd - test.smaller && smaller->memoryBase == address + test.smaller && smaller->pinnedBytes == test.smallerEnd - test.smaller, "the smaller side of " + name + " is not imported apart from the larger one"); requireImportServesGuest(context, recorder, *smaller, address + test.smaller, address + test.smallerEnd, ++salt, "the smaller side of " + name); - Require(HostImportFor(context, begin, static_cast(end - begin)) == kept && kept->memory == original, "importing the smaller side of " + name + " dropped the larger one"); + Require(HostImportFor(context, begin, static_cast(end - begin)) == kept && kept->memoryBase == begin && kept->pinnedBytes == end - begin, "importing the smaller side of " + name + " replaced the safe import of the larger one"); } if (test.unmap) { gpu.unlock(); @@ -2722,26 +2718,25 @@ int trimmedImportTests(const Device& device) { const auto from = reinterpret_cast(cut); const auto* remapped = HostImportFor(context, address, bytes); Require(remapped != nullptr && remapped->base == address && remapped->bytes == bytes, name + ": the range mapped again is not imported whole"); - Require(remapped->memory != keptMemory, "after " + name + " the pages mapped again are served from the import made before they were mapped again"); + Require(remapped->memoryBase == address && remapped->pinnedBytes == bytes, "after " + name + " the pages mapped again are served from the import that pinned the old ones"); requireImportServesGuest(context, recorder, *remapped, from, from + test.length, ++salt, "the pages mapped again after " + name); requireImportServesGuest(context, recorder, *remapped, begin, end, ++salt, "the larger side after " + name + " and a new mapping"); } cleanup(); gpu.unlock(); - std::cout << "trimmed import: " << name << ": the larger side was " << (test.trimmed ? "kept" : "imported again") << " and the GPU and the write watch see the guest's bytes" << (test.unmap ? ", also after the pages were mapped again" : "") << '\n'; + std::cout << "host import after cut: " << name << ": the changed import was retired before the remaining range was imported, and the GPU and write watch see the guest's bytes" << (test.unmap ? ", also after the pages were mapped again" : "") << '\n'; }; const auto remapWhileTrimmed = [&] { - const std::string name = "an unmap and a new mapping of the kept side while the import waits trimmed"; + const std::string name = "an unmap and a new mapping of the retained side while the old import is retired"; Backing backing; - Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, bytes, run, 0, &backing.physical) == 0, "allocate the trimmed import test backing"); - Require(sceKernelMapDirectMemory(&backing.mapping, bytes, 0x33, 0, backing.physical, run) == 0, "map the trimmed import test backing"); + Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, bytes, run, 0, &backing.physical) == 0, "allocate the host import cut test backing"); + Require(sceKernelMapDirectMemory(&backing.mapping, bytes, 0x33, 0, backing.physical, run) == 0, "map the host import cut test backing"); const auto address = reinterpret_cast(backing.mapping); auto* memory = static_cast(backing.mapping); for (std::size_t offset = 0; offset < bytes; offset += page) std::memset(memory + offset, static_cast(0x40u + (offset / page) % 64u), 64); gpu.lock(); const auto* whole = HostImportFor(context, address, bytes); Require(whole != nullptr && whole->base == address && whole->bytes == bytes && whole->alias == nullptr && !whole->unwatched, name + ": the range was not imported whole, settled and watched"); - const auto original = whole->memory; gpu.unlock(); Require(sceKernelMprotect(memory, run, 0x03) == 0, name + ": the protection of the head failed"); Require(sceKernelMunmap(memory + run, bytes - run) == 0, name + ": the unmap of the kept side failed"); @@ -2757,10 +2752,10 @@ int trimmedImportTests(const Device& device) { const auto* remapped = HostImportFor(context, begin, static_cast(end - begin)); Require(remapped != nullptr && remapped->base == begin && remapped->base + remapped->bytes == end, name + ": the pages mapped again are not imported whole"); requireImportServesGuest(context, recorder, *remapped, begin, end, ++salt, "the pages mapped again after " + name); - Require(remapped->memory != original, "after " + name + " the pages mapped again are served from the import that pinned the old ones"); + Require(remapped->memoryBase == begin && remapped->pinnedBytes == end - begin, "after " + name + " the pages mapped again are served from the import that pinned the old ones"); cleanup(); gpu.unlock(); - std::cout << "trimmed import: " << name << ": the pages mapped again were imported again and the GPU and the write watch see the guest's bytes\n"; + std::cout << "host import cut: " << name << ": the pages mapped again were imported again and the GPU and the write watch see the guest's bytes\n"; }; gpu.unlock(); try { @@ -2771,7 +2766,7 @@ int trimmedImportTests(const Device& device) { throw; } gpu.lock(); - std::cout << "Trimmed host import tests passed\n"; + std::cout << "Host import cut lifetime tests passed\n"; return 0; } #endif @@ -9999,7 +9994,7 @@ int main(int argc, char** argv) { if (argc == 2 && std::string_view(argv[1]) == "--settle-retire-only") return settleRetireTests(device) ? 0 : 77; if (argc == 2 && std::string_view(argv[1]) == "--settle-storage-image-only") return settleStorageImageTests(device) ? 0 : 77; if (argc == 2 && std::string_view(argv[1]) == "--reimport-bench") return reimportBench(device); - if (argc == 2 && std::string_view(argv[1]) == "--trimmed-import-only") return trimmedImportTests(device); + if (argc == 2 && std::string_view(argv[1]) == "--host-import-cut-only") return hostImportCutTests(device); #endif if (argc == 2 && std::string_view(argv[1]) == "--host-import-retirement-only") { const auto alignment = device.GetContext().hostImportAlignment; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp index 6c92e2171d..ed32384e66 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp @@ -112,7 +112,9 @@ void CheckMappings(AgcDriver::VulkanDevice& device) { CheckPattern(mapping); Fill(device, mapping); Require(munmap_nid_postfix(middle, BlockBytes) == 0, "guest middle unmap failed"); - Require(Imported(device, mapping), "unrelated prefix import was retired by middle unmap"); + Require(!Imported(device, mapping), "middle unmap left the original host import alive across the unmapped range"); + Fill(device, mapping); + Require(Imported(device, mapping), "the retained prefix was not imported again after middle unmap"); Require(munmap_nid_postfix(mapping, BlockBytes) == 0, "guest prefix unmap failed"); Require(!Imported(device, mapping), "prefix host import survived guest munmap"); Require(munmap_nid_postfix(middle + BlockBytes, BlockBytes) == 0, "guest suffix unmap failed"); From 787a7bc39d759d1d101c67f150242ff4c7853d2e Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:32:10 +0000 Subject: [PATCH 148/523] fix(agc): retire imports after dropping cached address spaces --- .../Graphics/src/GuestBufferMemory.cpp | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index e66a4dc0b2..6720fa9c8e 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -2743,6 +2743,7 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) { bool synced = false; bool drained = false; bool retiredImport = false; + bool droppedAddressSpace = false; // A range pinned only by the cached address space is released by dropping the cache's // reference: no GPU wait, no device lock (the mutating thread may be a driver thread holding // it). The registry rescans; a range still pinned by a build or a batch holding the space comes @@ -2751,12 +2752,24 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) { Spaces().droppedByWaiter.store(true, std::memory_order_relaxed); Spaces().waiterDrops.fetch_add(1, std::memory_order_relaxed); dropped.reset(); - auto& state = Leases(); - std::lock_guard lock(state.mutex); - ++state.stats.contentionWaits; - ++state.stats.cacheDrops; - state.stats.contentionMs += std::chrono::duration(std::chrono::steady_clock::now() - start).count(); - return true; + droppedAddressSpace = true; + auto& imports = Imports(); + const auto end = static_cast(address) + bytes; + bool overlapsImport = false; + { + std::lock_guard importsLock(imports.mutex); + const auto next = imports.imports.lower_bound(address); + const auto previous = next == imports.imports.begin() ? imports.imports.end() : std::prev(next); + overlapsImport = (next != imports.imports.end() && next->first < end) || (previous != imports.imports.end() && previous->first + previous->second.bytes > address); + } + if (!overlapsImport) { + auto& state = Leases(); + std::lock_guard lock(state.mutex); + ++state.stats.contentionWaits; + ++state.stats.cacheDrops; + state.stats.contentionMs += std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + return true; + } } if (Recorder::InCompletion()) throw std::runtime_error("guest allocation mutation cannot wait for an active GPU completion"); if (GuestMemory::GpuMutex().HeldByThisThread()) { @@ -2866,6 +2879,7 @@ bool WaitForLeasesAndImports(std::uintptr_t address, std::size_t bytes) { auto& state = Leases(); std::lock_guard lock(state.mutex); ++state.stats.contentionWaits; + if (droppedAddressSpace) ++state.stats.cacheDrops; if (synced) ++state.stats.contentionSyncs; if (drained) ++state.stats.contentionDrains; state.stats.contentionMs += std::chrono::duration(std::chrono::steady_clock::now() - start).count(); From 2a12152f2b55792bd362c34552ec66496bfe66af Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Mon, 19 Oct 2026 19:15:48 +0000 Subject: [PATCH 149/523] test(agc): clarify imports after partial unmap --- .../tests/execution/GuestMemoryLifetime.cpp | 28 ++++++++++++------- docs/dev/ARCHITECTURE.md | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp index ed32384e66..a7511c3012 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp @@ -63,16 +63,16 @@ void* Map(std::size_t bytes = BlockBytes) { return result; } -void CheckPattern(const void* pointer) { +void CheckPattern(const void* pointer, std::size_t bytes = FillBytes) { const auto* data = static_cast(pointer); - for (std::size_t offset = 0; offset < FillBytes; offset += sizeof(Pattern)) + for (std::size_t offset = 0; offset < bytes; offset += sizeof(Pattern)) Require(std::memcmp(data + offset, Pattern.data(), sizeof(Pattern)) == 0, "guest mutation lost the pending GPU fill"); } -void Fill(AgcDriver::VulkanDevice& device, void* pointer) { +void Fill(AgcDriver::VulkanDevice& device, void* pointer, std::size_t bytes = FillBytes) { std::lock_guard lock(AgcDriver::GuestMemory::GpuMutex()); - Require(device.FillBuffer(Address(pointer), FillBytes, Pattern), "Vulkan device did not import the guest allocation"); - Require(Imported(device, pointer), "GPU fill did not create a host import"); + Require(device.FillBuffer(Address(pointer), bytes, Pattern), "Vulkan device did not import the guest allocation"); + Require(Imported(device, pointer, bytes), "GPU fill did not create a host import"); } void CheckMappings(AgcDriver::VulkanDevice& device) { @@ -105,14 +105,22 @@ void CheckMappings(AgcDriver::VulkanDevice& device) { Require(!Imported(device, mapping), "host import survived guest munmap"); mapping = Map(3 * BlockBytes); - Fill(device, mapping); + Fill(device, mapping, 3 * BlockBytes); auto* middle = static_cast(mapping) + BlockBytes; Require(mprotect_nid_postfix(middle, BlockBytes, 1) == 0, "guest partial protection failed"); - Require(!Imported(device, mapping), "whole host import survived partial protection change"); - CheckPattern(mapping); - Fill(device, mapping); + Require(!Imported(device, mapping, 3 * BlockBytes), "whole host import survived partial protection change"); + CheckPattern(mapping, 3 * BlockBytes); + Require(munmap_nid_postfix(middle, BlockBytes) == 0, "guest middle unmap after protection change failed"); + Require(!Imported(device, mapping, 3 * BlockBytes), "host import still covered the protected and unmapped middle range"); + Require(munmap_nid_postfix(mapping, BlockBytes) == 0, "guest prefix unmap after protection change failed"); + Require(munmap_nid_postfix(middle + BlockBytes, BlockBytes) == 0, "guest suffix unmap after protection change failed"); + + mapping = Map(3 * BlockBytes); + Fill(device, mapping, 3 * BlockBytes); + Require(Imported(device, mapping, 3 * BlockBytes), "whole host import did not cover the three-block mapping before middle unmap"); + middle = static_cast(mapping) + BlockBytes; Require(munmap_nid_postfix(middle, BlockBytes) == 0, "guest middle unmap failed"); - Require(!Imported(device, mapping), "middle unmap left the original host import alive across the unmapped range"); + Require(!Imported(device, mapping, 3 * BlockBytes), "middle unmap left a host import spanning the unmapped range"); Fill(device, mapping); Require(Imported(device, mapping), "the retained prefix was not imported again after middle unmap"); Require(munmap_nid_postfix(mapping, BlockBytes) == 0, "guest prefix unmap failed"); diff --git a/docs/dev/ARCHITECTURE.md b/docs/dev/ARCHITECTURE.md index 574244a6da..14c028bcc0 100644 --- a/docs/dev/ARCHITECTURE.md +++ b/docs/dev/ARCHITECTURE.md @@ -130,7 +130,7 @@ flowchart LR - A draw whose `PA_SC_AA_CONFIG` selects 2, 4 or 8 samples renders into host-only multisampled images. [`MultisampleTarget.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp) keeps one image per device, surface address, format, extent and sample count (`MultisampleTargetView`); the pipeline, render pass and library keys carry `State::samples`, and a multisampled depth target is a multisampled depth surface. The guest sample planes and FMASK are never read or written, so the registry also guards the target's guest range: a CPU read (`GuestMemory::SetReadGuard`), a DMA copy, and a sampled or storage image read of it throw. A `CB_COLOR_CONTROL` resolve mode over a multisampled CB0 runs as a render pass whose resolve attachment is CB1's resident image (`ResolveMultisampleTarget`). A zero fill of the target's CMASK (`NoteColorMetadataFill`, from fill kernels and CP DMA fills) clears the image at its next use. - A texture over a depth surface samples the depth image itself when it views one 2D slice of the plane's own format (`DepthSurfaceTexture` in [`DepthSurface.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp)). A view of several slices (a 2D array or a cube) or an integer view of a D16 plane is served by a `DepthPlaneCopy`, an image the slices are copied into at every use: from the depth surface bound at each slice's address, else from guest memory. Such a texture is never kept in the resource templates (`RefreshedPerUse`). - Guest memory reaches the GPU through host imports (`VK_EXT_external_memory_host`) where the driver accepts them, and through copies otherwise ([`GuestBufferMemory.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp)). A registered range larger than the import window (64 MiB) is imported per window for buffer and fill accesses and whole for an address-based build. Live imports stay under a budget, with the least recently used import retired first (see TechnicalDebt, Host import windows and budget). -- A guest protect or unmap over part of a plain host-pointer import (one registered range, no alias, dma-buf, chunked or joined import, no pending settle) trims the import instead of retiring it (`retireOrTrim` in [`GuestBufferMemory.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp)): its buffer is destroyed and it waits in `HostImports::trimmed` with the side of its range the change left, the larger side for a change strictly inside the range, while its `VkDeviceMemory` keeps the whole range it was made for pinned (`HostImport::memoryBase` and `pinnedBytes`, which `liveBytes` and the eviction count), also after the guest protects, unmaps or maps again the pages that were cut off. The next `refreshImports` binds a new `VkBuffer` to the same memory at the offset of the largest registered range inside the kept side (`reattachTrimmed`) when that range still has the import's backing serial, overlaps no other import, is what a new import would cover and the buffer accepts the offset; otherwise, or when an import of a range overlapping the kept side is made first, the trimmed import is released and held through the recorder (`KeepThroughHold`) as a retired import is. `GuestAllocations::Range::backingSerial` tells whether a range's pages are still the ones that were imported: a new range gets a fresh serial, the untouched pieces of a split keep theirs, and every span a mutation passes to `RequireUnpinned` (a protect or unmap, a fixed mapping over registered memory, the other mappings of remapped direct memory, and for an unmap or remap the whole 2 MiB resident runs it touches, which it may demote to shared pages) gets a fresh one when the mutation ends. +- A guest protect or unmap that overlaps a plain host-pointer import retires the whole import before changing the guest range (`WaitForLeasesAndImports` in [`GuestBufferMemory.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp)). Later accesses import surviving registered ranges again; imports never keep the pages across a cut pinned in a trimmed state. `GuestAllocations::Range::backingSerial` distinguishes the original pages from replacements: untouched pieces of a split keep their serial, newly mapped or protected pieces get a fresh one, and every span passed to `RequireUnpinned` (including the other mappings of remapped direct memory and the resident runs touched by a 2 MiB unmap or remap) gets a fresh serial when the mutation ends. - A draw binds its vertex and index ranges in place from a host import, except with NVIDIA's Windows driver, which reads a host import of guest memory far slower than a buffer it allocated (Astro Bot at Crash Site on an RTX 5070 Ti: 164 against 45 ms of draws per frame), so they are copied there (`DrawInputsInPlaceUsable`, `Context::drawInputsInPlace`). `APS5_DRAW_INPUTS_IN_PLACE=1` binds them in place and `APS5_NO_DRAW_INPUTS_IN_PLACE=1` copies them on any device; the second wins. - On Windows, a host import of private `MEM_WRITE_WATCH` memory (singly mapped direct memory) stays on `GetWriteWatch` when `ProbeImportWriteProtection` shows the driver reports nothing written after the second GPU read and a GPU write, and a CPU store is collected (`decideImportWatch`). The driver marks every page of such an import written, asynchronously, until a GPU use of it completes (a read on the settle queue ends it even while main-queue work that used the import first is still running, and the first use of an earlier import of the same pages after that read marks nothing), so `settleImport` in [`GuestBufferMemory.cpp`](../../core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp) copies 4 bytes out of the new import on a second queue of the graphics family, or on a queue of another copy-capable family when the graphics family has only one (`Context::settleQueue`, own pool and mutex) inside `ImportWatched`'s window and waits for it, for imports of 1 MiB or more; the main queue can't be used there because `importAllocation` holds the imports lock and `GpuMutex` comes before it in the lock order. A smaller import is compared when it is made, so a title that maps, imports and unmaps a small buffer around every submission does not block its mapping thread. Its 4-byte read is queued instead: the next import lookup that holds `GpuMutex` while the imports match the registry (`settleUnwatchedImports`) records the queued reads into one batch on the settle queue, and once the batch's fence has signalled a later lookup returns the import's whole 64 KiB blocks to the write watch (`GuestMemory::Rewatch` drops their marks and stamps every block as a CPU store), unless a live storage image overlaps them. Such an import stays compared: an image that took its generation before the stamp would take the stamp for a CPU store over the results it produces after it, and drop them. A settled import whose range had already left the watch returns the same way, and an import retired while its read is in flight is kept with that batch and freed once the batch has completed, without waiting for it; a guest unmap of its memory waits for the batch instead. `APS5_NO_DEFERRED_SETTLES` (any value) keeps every import under 1 MiB compared. Imports through a section alias keep tracking on the guest views, and every other Windows host import is compared. A device without such a queue keeps the comparisons. On Linux the settle queue is made when the device imports dma-buf memory, and every dma-buf import of 1 MiB or more is settled right after it is made, for another reason: the kernel maps an import's pages for the GPU at the first submission that uses it, which took about 2 s for a 3 GiB import on RADV and held up the title's frame. The import's buffer is shared with the settle queue's family instead of aliased, since a sparse import is bound to many memory objects. From fa8115ce6351ec567faea190743d6c45ae5e4ebc Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 08:07:56 +0000 Subject: [PATCH 150/523] test(agc): assert whole import retires on middle unmap --- .../prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp index a7511c3012..2d737ebc4e 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/GuestMemoryLifetime.cpp @@ -121,6 +121,7 @@ void CheckMappings(AgcDriver::VulkanDevice& device) { middle = static_cast(mapping) + BlockBytes; Require(munmap_nid_postfix(middle, BlockBytes) == 0, "guest middle unmap failed"); Require(!Imported(device, mapping, 3 * BlockBytes), "middle unmap left a host import spanning the unmapped range"); + Require(!Imported(device, mapping), "middle unmap retained the prefix of the retired host import"); Fill(device, mapping); Require(Imported(device, mapping), "the retained prefix was not imported again after middle unmap"); Require(munmap_nid_postfix(mapping, BlockBytes) == 0, "guest prefix unmap failed"); From 349a25f7ff03ee4b2f11c57813d3d01712026958 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Tue, 20 Oct 2026 08:30:27 +0000 Subject: [PATCH 151/523] fix(agc): remove stale trimmed-import call --- core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index 6720fa9c8e..77e76cc5cd 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -1624,7 +1624,6 @@ const HostImport* importRunAt(const Context& context, HostImports& state, Import if (sameRun(failed->second, run)) return nullptr; failures.erase(failed); } - dropTrimmed(state, base, base + bytes); const auto budget = effectiveBudget(context, state); const auto containing = std::find_if(run.ranges.begin(), run.ranges.end(), [address](const auto& range) { return address >= range->address && address < range->address + range->bytes; }); if (const auto remembered = state.evictingRefusals.find({base, bytes}); evict && remembered != state.evictingRefusals.end()) { From c566d31d1f472506518ad9490c5347141cdebf1a Mon Sep 17 00:00:00 2001 From: MarcZX10 Date: Wed, 21 Oct 2026 16:53:05 +0000 Subject: [PATCH 152/523] test(agc): skip reimport bench below host budget --- .../libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp | 1 + .../prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp | 7 +++++++ core/libs/prx/libSceAgcDriver/tests/Recorder.cpp | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp index ceec04cf3e..fa552ac524 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp @@ -114,6 +114,7 @@ void SetHostImportDeferredSettles(bool enabled); // that heap: the other half stays for the buffers, mirrors and textures that share it. std::uint64_t HostImportBudget(const VkPhysicalDeviceMemoryProperties& memory, std::uint64_t physicalBytes); std::uint64_t HostImportLiveBytes(); +std::uint64_t HostImportBudgetRemaining(const Context& context); struct HostImportSettleCounts { std::uint64_t submitted = 0; std::uint64_t waited = 0; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index 77e76cc5cd..fd46544f36 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -3333,6 +3333,13 @@ std::uint64_t HostImportLiveBytes() { return state.liveBytes; } +std::uint64_t HostImportBudgetRemaining(const Context& context) { + auto& state = Imports(); + std::lock_guard lock(state.mutex); + const auto budget = effectiveBudget(context, state); + return budget > state.liveBytes ? budget - state.liveBytes : 0; +} + const char* ImportRequesterName(ImportRequester requester) { switch (requester) { case ImportRequester::Map: return "map"; diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index e3a76c5ed2..e78f79d58b 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -2505,6 +2505,12 @@ int reimportBench(const Device& device) { constexpr std::size_t moves = 5; constexpr std::size_t page = 4096; constexpr std::size_t alignment = 2ull << 20u; + constexpr std::size_t requiredImportBytes = bytes + inside; + const auto availableImportBytes = HostImportBudgetRemaining(context); + if (availableImportBytes < requiredImportBytes) { + std::cout << "skipped, the re-import bench needs " << (requiredImportBytes >> 20u) << " MiB of simultaneous host imports; the current budget has " << (availableImportBytes >> 20u) << " MiB available\n"; + return 77; + } struct Release { void* mapping = nullptr; std::int64_t physical = -1; From ba8b881fa63e26d86d7d3c48f94ae2252a30e026 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:43:36 +0200 Subject: [PATCH 153/523] fix(libkernel): answer a receive after SHUT_RD or with no buffer without the host soreceive_generic returns 0 for a socket whose receive side is shut and for a zero-length receive on a connected stream socket (ENOTCONN when it is not connected), without waiting. Winsock fails the first with WSAESHUTDOWN, which reached the guest as EPIPE, and blocks the second until data arrives. recv and recvfrom now answer both themselves. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 16 ++++++++++ core/libs/tests/GuestTcp.cpp | 34 ++++++++++++++++++++++ docs/dev/debt/libkernel.md | 1 + 3 files changed, 51 insertions(+) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index a9805e0391..50f4c309e9 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -673,6 +673,16 @@ int SetNonBlocking(Socket& socket) { socket.options.nonblocking = true; return 0; } +int ReceiveAnsweredHere(Socket& socket, std::uint64_t length) { + { + std::lock_guard lock(socket.modeMutex); + if (socket.receiveShut) return 0; + } + if (length != 0 || socket.type != 1) return -1; + sockaddr_storage peer{}; + socklen_t size = sizeof(peer); + return ::getpeername(socket.value, reinterpret_cast(&peer), &size) == 0 ? 0 : NativeError(); +} int SocketSendFlags(Socket& socket, int flags) { std::lock_guard lock(socket.modeMutex); return NativeSendFlags(socket.options.noSignal ? flags | GuestNoSignal : flags); @@ -1256,6 +1266,7 @@ std::int64_t APS5_VABI recv_nid_postfix(int descriptor, void* buffer, std::uint6 if (!buffer && length) return Fail(14); 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); const auto result = ::recv(socket->value, static_cast(buffer), static_cast(length), NativeReceiveFlags(flags, socket->type)); @@ -1368,6 +1379,11 @@ std::int64_t APS5_VABI recvfrom_nid_postfix(int descriptor, void* buffer, std::u if ((!buffer && length) || (address && !addressLength)) return Fail(14); 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) { + if (answered > 0) return Fail(answered); + if (address) *addressLength = 0; + return 0; + } sockaddr_storage native{}; socklen_t size = sizeof(native); if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited); diff --git a/core/libs/tests/GuestTcp.cpp b/core/libs/tests/GuestTcp.cpp index 15f8d514a6..80322f4979 100644 --- a/core/libs/tests/GuestTcp.cpp +++ b/core/libs/tests/GuestTcp.cpp @@ -178,11 +178,45 @@ static void WaitAllReceivesTheWholeRequest() { for (const int descriptor : {datagram, client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0); } +static void ReceiveAnsweredWithoutTheHost() { + constexpr int DontWait = 0x80; + 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 unconnected = socket_nid_postfix(2, 1, 0); + char byte; + Require(recv_nid_postfix(unconnected, &byte, 0, DontWait) == -1 && *__error_nid_postfix() == 57); + const int client = socket_nid_postfix(2, 1, 0); + Require(connect_nid_postfix(client, address.data(), address.size()) == 0); + const int accepted = accept_nid_postfix(listener, nullptr, nullptr); + Require(accepted >= 0); + + Require(recv_nid_postfix(accepted, &byte, 0, DontWait) == 0); + Require(recv_nid_postfix(accepted, &byte, 0, 0) == 0); + std::array sender; + sender.fill(0xaa); + std::uint32_t senderSize = sender.size(); + Require(recvfrom_nid_postfix(accepted, &byte, 0, 0, sender.data(), &senderSize) == 0 && senderSize == 0); + + Require(shutdown_nid_postfix(accepted, 0) == 0); + Require(send_nid_postfix(client, "x", 1, 0) == 1); + char received[4]; + Require(recv_nid_postfix(accepted, received, sizeof(received), 0) == 0); + Require(recv_nid_postfix(accepted, received, sizeof(received), DontWait) == 0); + senderSize = sender.size(); + Require(recvfrom_nid_postfix(accepted, received, sizeof(received), 0, sender.data(), &senderSize) == 0 && senderSize == 0); + for (const int descriptor : {unconnected, client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0); +} + int main() { StreamReceiveReportsNoSender(); ListenersShareAPortAcrossFamilies(); AcceptedSocketsFollowListenerBlocking(); WaitAllReceivesTheWholeRequest(); + ReceiveAnsweredWithoutTheHost(); UnboundSocketNameIsZero(); const int listener = socket_nid_postfix(2, 1, 6); Require(listener >= 0); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index cc3fef20ac..640ad4bc97 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -189,6 +189,7 @@ - [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`. +- [recv and recvfrom](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answer without the host a receive on a socket whose read side `shutdown` closed (0, as [`soreceive_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1556-L1562) returns at `SBS_CANTRCVMORE`) and a zero-length receive on a stream socket (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)); Winsock fails the first with `EPIPE` and blocks on the second. `recvmsg` still asks the host in both cases. Not measured on a console. - [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) check a socket address length as FreeBSD 11.4's [`getsockaddr`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_syscalls.c) and the `sa_len` test of `tcp_usr_connect` and `in_pcbconnect` do: below 2 or an `AF_INET` address below 16 or an `AF_INET6` address below 28 bytes fails with `EINVAL`, above 255 with `ENAMETOOLONG`. An address longer than `sockaddr_in` or `sockaddr_in6` is still accepted where FreeBSD answers `EINVAL`, and an address of another family still fails with `EAFNOSUPPORT`, as the console's behaviour for these is not known. None of this was measured on a console. - [read, write and vector I/O](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) on a guest socket use `recv` and `send` without flags; `sceKernelReadv` and `sceKernelWritev` gather or scatter through one socket operation, preserving stream short reads and datagram boundaries. The positioned `pread`, `pwrite`, `preadv` and `pwritev` forms, and their `sceKernel` forms, return `ESPIPE` for an open guest socket and `EBADF` for a closed one. `EPIPE` from a write raises the host's `SIGPIPE` on Linux unless the socket has `SO_NOSIGPIPE` set, and the guest cannot ignore it: `signal(SIGPIPE, SIG_IGN)` and `sigaction` record the disposition, as they do for every number from 1 to 128, but only the six signals with host equivalents (2, 4, 6, 8, 11 and 15) take effect on the host or are delivered, so the host `SIGPIPE` is not stopped; nothing is raised on Windows; a `write` longer than `INT_MAX` bytes is `EMSGSIZE` where the console sends it. AIO on a guest socket still goes to the host, which has no such descriptor From c02a5d8034d8ea197718169eaec7cdb596921ccb Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Wed, 21 Oct 2026 12:52:14 +0200 Subject: [PATCH 154/523] fix(libkernel): keep the receive-after-SHUT_RD answer on Windows only On Linux the host keeps data that was buffered before shutdown(SHUT_RD) and guest_socket_pair expects to read it, so the shortcut that answers 0 for any receive after SHUT_RD broke that test there. Limit the shortcut to Windows, where the host cannot answer it, and run the post-shutdown receives of guest_tcp on Windows only. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 2 ++ core/libs/tests/GuestTcp.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 50f4c309e9..593cac22fd 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -674,10 +674,12 @@ int SetNonBlocking(Socket& socket) { return 0; } int ReceiveAnsweredHere(Socket& socket, std::uint64_t length) { +#ifdef _WIN32 { std::lock_guard lock(socket.modeMutex); if (socket.receiveShut) return 0; } +#endif if (length != 0 || socket.type != 1) return -1; sockaddr_storage peer{}; socklen_t size = sizeof(peer); diff --git a/core/libs/tests/GuestTcp.cpp b/core/libs/tests/GuestTcp.cpp index 80322f4979..332b9ebede 100644 --- a/core/libs/tests/GuestTcp.cpp +++ b/core/libs/tests/GuestTcp.cpp @@ -201,6 +201,7 @@ static void ReceiveAnsweredWithoutTheHost() { std::uint32_t senderSize = sender.size(); Require(recvfrom_nid_postfix(accepted, &byte, 0, 0, sender.data(), &senderSize) == 0 && senderSize == 0); +#ifdef _WIN32 Require(shutdown_nid_postfix(accepted, 0) == 0); Require(send_nid_postfix(client, "x", 1, 0) == 1); char received[4]; @@ -208,6 +209,7 @@ static void ReceiveAnsweredWithoutTheHost() { Require(recv_nid_postfix(accepted, received, sizeof(received), DontWait) == 0); senderSize = sender.size(); Require(recvfrom_nid_postfix(accepted, received, sizeof(received), 0, sender.data(), &senderSize) == 0 && senderSize == 0); +#endif for (const int descriptor : {unconnected, client, accepted, listener}) Require(close_nid_postfix(descriptor) == 0); } From 32f8205390ddfc601183db4f63d54544e5c6dcf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Wed, 21 Oct 2026 20:01:26 +0200 Subject: [PATCH 155/523] docs(libkernel): say the receive-after-SHUT_RD answer is Windows only Co-Authored-By: Claude --- docs/dev/debt/libkernel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 640ad4bc97..a4988be219 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -189,7 +189,7 @@ - [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`. -- [recv and recvfrom](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answer without the host a receive on a socket whose read side `shutdown` closed (0, as [`soreceive_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1556-L1562) returns at `SBS_CANTRCVMORE`) and a zero-length receive on a stream socket (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)); Winsock fails the first with `EPIPE` and blocks on the second. `recvmsg` still asks the host in both cases. Not measured on a console. +- [recv and recvfrom](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answer without the host a zero-length receive on a stream socket (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)) and, on Windows only, a receive on a socket whose read side `shutdown` closed (0, as [`soreceive_generic`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L1556-L1562) returns at `SBS_CANTRCVMORE`); Winsock fails the second with `EPIPE` and blocks on the first. Linux and macOS still return data buffered before the `shutdown`, where FreeBSD discards it. `recvmsg` still asks the host in both cases. Not measured on a console. - [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) check a socket address length as FreeBSD 11.4's [`getsockaddr`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_syscalls.c) and the `sa_len` test of `tcp_usr_connect` and `in_pcbconnect` do: below 2 or an `AF_INET` address below 16 or an `AF_INET6` address below 28 bytes fails with `EINVAL`, above 255 with `ENAMETOOLONG`. An address longer than `sockaddr_in` or `sockaddr_in6` is still accepted where FreeBSD answers `EINVAL`, and an address of another family still fails with `EAFNOSUPPORT`, as the console's behaviour for these is not known. None of this was measured on a console. - [read, write and vector I/O](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) on a guest socket use `recv` and `send` without flags; `sceKernelReadv` and `sceKernelWritev` gather or scatter through one socket operation, preserving stream short reads and datagram boundaries. The positioned `pread`, `pwrite`, `preadv` and `pwritev` forms, and their `sceKernel` forms, return `ESPIPE` for an open guest socket and `EBADF` for a closed one. `EPIPE` from a write raises the host's `SIGPIPE` on Linux unless the socket has `SO_NOSIGPIPE` set, and the guest cannot ignore it: `signal(SIGPIPE, SIG_IGN)` and `sigaction` record the disposition, as they do for every number from 1 to 128, but only the six signals with host equivalents (2, 4, 6, 8, 11 and 15) take effect on the host or are delivered, so the host `SIGPIPE` is not stopped; nothing is raised on Windows; a `write` longer than `INT_MAX` bytes is `EMSGSIZE` where the console sends it. AIO on a guest socket still goes to the host, which has no such descriptor From 58afce1ad1a286897fc5c4f535466e3395df6528 Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Tue, 20 Oct 2026 16:43:01 +0200 Subject: [PATCH 156/523] fix(agc): follow RDNA 2's range checks for zero-stride vertex fetches --- .../src/Driver/Memory/WrittenBuffers.cpp | 3 +- .../Graphics/include/VertexInput.hpp | 14 +++-- .../prx/libSceAgcDriver/tests/Graphics.cpp | 63 +++++++++++++++---- .../prx/libSceAgcDriver/tests/VertexInput.cpp | 17 ++++- .../tests/execution/ZeroStrideVertexFetch.cpp | 26 ++++++-- docs/dev/debt/libSceAgcDriver.md | 2 +- 6 files changed, 98 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp index 8b0687ac4f..58fa8648a3 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/WrittenBuffers.cpp @@ -181,10 +181,11 @@ std::vector> QueuedDrawReadRanges(const } if (stages.empty() || stages.front().program == nullptr) return ranges; for (const auto& attribute : stages.front().program->vertexAttributes) { - if (Graphics::NullVertexDescriptor(attribute) || Graphics::VertexFetchOutOfRange(attribute)) continue; + if (Graphics::NullVertexDescriptor(attribute)) continue; const auto address = attribute.resource.fields[0] | (static_cast(attribute.resource.fields[1] & 0xffffu) << 32u); std::uint64_t bytes = 0; try { + if (Graphics::VertexFetchOutOfRange(attribute)) continue; bytes = Graphics::VertexBufferExtent(attribute); } catch (const std::runtime_error&) { continue; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp index a8d93a9a85..aad386bfc1 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp @@ -230,14 +230,16 @@ inline std::uint32_t VertexBufferOutOfBoundsSelect(const ShaderRecompiler::Verte inline bool VertexFetchOutOfRange(const ShaderRecompiler::VertexAttribute& attribute) { const auto stride = (attribute.resource.fields[1] >> 16u) & 0x3fffu; + if (stride != 0 || NullVertexDescriptor(attribute)) return false; const auto select = VertexBufferOutOfBoundsSelect(attribute); - return stride == 0 && (select == 2u || select == 3u) && attribute.resource.fields[2] == 0; + const auto records = attribute.resource.fields[2]; + if (select == 0u) return true; + if (select == 3u) return records < DecodeVertexFormat(attribute).bytes; + return records == 0; } inline std::uint64_t ZeroStrideAvailableBytes(const ShaderRecompiler::VertexAttribute& attribute) { - const auto records = attribute.resource.fields[2]; - if (VertexBufferOutOfBoundsSelect(attribute) == 2u && records != 0) return std::max(records, DecodeVertexFormat(attribute).bytes); - return records; + return VertexFetchOutOfRange(attribute) ? 0u : DecodeVertexFormat(attribute).bytes; } inline std::size_t VertexBufferExtent(const ShaderRecompiler::VertexAttribute& attribute) { @@ -261,7 +263,9 @@ inline std::size_t VertexBufferReadSize(const ShaderRecompiler::VertexAttribute& Require(attribute.fetchIndex <= 1, "unsupported vertex fetch index"); const auto index = attribute.fetchIndex == 0 ? maxIndex : firstInstance + instances - 1u; const auto bytes = DecodeVertexFormat(attribute).bytes; - Require(stride == 0 || VertexBufferOutOfBoundsSelect(attribute) <= 1u || index < records, "vertex fetch exceeds descriptor record count"); + const auto select = VertexBufferOutOfBoundsSelect(attribute); + const auto recordsUnchecked = stride == 0 ? select != 1u : select <= 1u; + Require(recordsUnchecked || index < records, "vertex fetch exceeds descriptor record count"); const auto required = static_cast(stride) * index + bytes; Require(stride != 0 || required <= ZeroStrideAvailableBytes(attribute), "vertex fetch exceeds descriptor byte range"); Require(required <= std::numeric_limits::max(), "vertex fetch exceeds addressable byte range"); diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 49ca8b83cb..428c1ce75e 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -9890,26 +9890,63 @@ void validationTests() { attribute.resource.fields[3] = 77u << 12u; attribute.fetchIndex = 1; attribute.resource.fields[1] = 0; - attribute.resource.fields[2] = 16; + const auto zeroStride = [&](std::uint32_t select, std::uint32_t records) { + attribute.resource.fields[3] = (77u << 12u) | (select << 28u); + attribute.resource.fields[2] = records; + }; + for (const auto records : {0u, 1u, 16u, 0xffffffffu}) { + zeroStride(0, records); + Require(AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 0 must read every zero-stride fetch as zero"); + Require(AgcDriver::Graphics::ZeroStrideAvailableBytes(attribute) == 0, "OOB_SELECT 0 zero-stride fetch has bytes to read"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1); }, "byte range"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferExtent(attribute); }, "empty or oversized"); + } + zeroStride(1, 16); Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 100, 2) == 16, "zero stride must repeat one value"); Require(AgcDriver::Graphics::VertexBufferReadOffset(attribute, 100, 7) == 0, "zero-stride input skipped its repeated value"); - attribute.resource.fields[2] = 8; - expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1); }, "byte range"); - Require(!AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "a structured zero-stride fetch was treated as out of range"); - attribute.resource.fields[3] = (77u << 12u) | (2u << 28u); - attribute.resource.fields[2] = 1; + Require(AgcDriver::Graphics::VertexBufferExtent(attribute) == 16, "OOB_SELECT 1 extent must cover the whole fetch"); + zeroStride(1, 0); + Require(AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 1 without records must read zero"); + zeroStride(1, 8); + Require(!AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 1 counted NUM_RECORDS in bytes"); + Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 8) == 16, "OOB_SELECT 1 must read the whole element for every index below NUM_RECORDS"); + Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1, 7) == 16, "OOB_SELECT 1 must read the whole element for the last index below NUM_RECORDS"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 9); }, "record count"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1, 8); }, "record count"); + zeroStride(1, 1); + Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1) == 16, "OOB_SELECT 1 with one record must read the element at index 0"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 2); }, "record count"); + attribute.fetchIndex = 0; + zeroStride(1, 3); + Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 2, 1) == 16, "OOB_SELECT 1 must read the element for index 2 of 3 records"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 3, 1); }, "record count"); + attribute.fetchIndex = 1; + zeroStride(2, 1); Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 100, 2) == 16, "OOB_SELECT 2 must read a zero-stride fetch whole while NUM_RECORDS is nonzero"); Require(AgcDriver::Graphics::VertexBufferExtent(attribute) == 16, "OOB_SELECT 2 extent must cover the whole fetch"); Require(!AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 2 with records was treated as out of range"); - attribute.resource.fields[2] = 0; + zeroStride(2, 0); Require(AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 2 without records must read zero"); - attribute.resource.fields[3] = (77u << 12u) | (3u << 28u); + zeroStride(3, 0); Require(AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 3 without records must read zero"); - attribute.resource.fields[2] = 8; - Require(!AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 3 with records was treated as out of range"); - expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1); }, "byte range"); - attribute.resource.fields[2] = 16; - Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1) == 16, "OOB_SELECT 3 must read a fetch inside the range"); + for (const auto records : {1u, 8u, 15u}) { + zeroStride(3, records); + Require(AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 3 must read a fetch that only partly fits as zero"); + expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 0, 1); }, "byte range"); + } + for (const auto records : {16u, 17u, 0xffffffffu}) { + zeroStride(3, records); + Require(!AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 3 with the whole fetch in range was treated as out of range"); + Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 100, 2) == 16, "OOB_SELECT 3 must read a fetch inside the range, whatever the index"); + Require(AgcDriver::Graphics::VertexBufferExtent(attribute) == 16, "OOB_SELECT 3 extent must cover only the fetch"); + } + attribute.resource.fields[3] = (22u << 12u) | (3u << 28u); + attribute.resource.fields[2] = 4; + Require(!AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 3 compared NUM_RECORDS with the wrong fetch size"); + attribute.resource.fields[2] = 3; + Require(AgcDriver::Graphics::VertexFetchOutOfRange(attribute), "OOB_SELECT 3 read a 4-byte fetch past 3 bytes"); + attribute.resource.fields[3] = (113u << 12u) | (3u << 28u); + expectFailure([&] { AgcDriver::Graphics::VertexFetchOutOfRange(attribute); }, "unsupported vertex format"); attribute.resource.fields[3] = 113u << 12u; expectFailure([&] { AgcDriver::Graphics::DecodeVertexFormat(attribute); }, "unsupported vertex format"); attribute.resource.fields[3] = 50u << 12u; diff --git a/core/libs/prx/libSceAgcDriver/tests/VertexInput.cpp b/core/libs/prx/libSceAgcDriver/tests/VertexInput.cpp index f0f999a04c..5d56b47f05 100644 --- a/core/libs/prx/libSceAgcDriver/tests/VertexInput.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/VertexInput.cpp @@ -151,10 +151,25 @@ void run() { const auto zeroStride = live.TryGet(101, input); Require(VertexFetchOutOfRange(input[0]), "zero records did not select zero fill"); input[0].resource.fields[2] = 64; - Require(live.TryGet(101, input) == zeroStride && !VertexFetchOutOfRange(input[0]) && VertexBufferExtent(input[0]) == 64, "cached zero-stride layout froze the live zero-fill decision"); + Require(live.TryGet(101, input) == zeroStride && !VertexFetchOutOfRange(input[0]) && VertexBufferExtent(input[0]) == 16, "cached zero-stride layout froze the live zero-fill decision"); input[0].resource.fields[2] = 0; Require(live.TryGet(101, input) == zeroStride && VertexFetchOutOfRange(input[0]), "record count zero reused a live fetch"); input[0] = attribute(); + input[0].resource.fields[1] = 0; + input[0].resource.fields[3] |= 3u << 28u; + input[0].resource.fields[2] = 16; + const auto wholeFetch = live.TryGet(105, input); + for (const auto records : {0u, 8u, 15u}) { + input[0].resource.fields[2] = records; + Require(live.TryGet(105, input) == wholeFetch && VertexFetchOutOfRange(input[0]), "a fetch that only partly fits was not read as zero"); + } + for (const auto records : {16u, 17u, 0xffffffffu}) { + input[0].resource.fields[2] = records; + Require(live.TryGet(105, input) == wholeFetch && !VertexFetchOutOfRange(input[0]) && VertexBufferExtent(input[0]) == 16, "a fetch inside the byte range was read as zero or sized by NUM_RECORDS"); + } + input[0].resource.fields[3] &= ~(3u << 28u); + Require(VertexFetchOutOfRange(input[0]), "OOB_SELECT 0 read a zero-stride fetch"); + input[0] = attribute(); input[0].formatComponents = 1; const auto narrow = live.TryGet(102, input); input[0].formatComponents = 2; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ZeroStrideVertexFetch.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ZeroStrideVertexFetch.cpp index 5715c39d27..e5804bf70a 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ZeroStrideVertexFetch.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ZeroStrideVertexFetch.cpp @@ -142,14 +142,28 @@ int main() { try { const auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; - constexpr std::array cases{{ - {2u, 1u, true, "OOB_SELECT 2, one record"}, + constexpr std::uint32_t Wzyx = 7u | (6u << 3u) | (1u << 6u) | (0u << 9u); + constexpr std::array cases{{ + {0u, 0u, false, "OOB_SELECT 0, no records"}, + {0u, 1u, false, "OOB_SELECT 0, one record"}, + {0u, 16u, false, "OOB_SELECT 0, records cover the element's bytes"}, + {0u, 0xffffffffu, false, "OOB_SELECT 0, NUM_RECORDS 0xffffffff"}, + {0u, 0xffffffffu, false, "OOB_SELECT 0, NUM_RECORDS 0xffffffff, selectors W Z 1 0", Wzyx}, + {1u, 0u, false, "OOB_SELECT 1, no records"}, + {1u, 3u, true, "OOB_SELECT 1, NUM_RECORDS counts the three vertices' records"}, + {1u, 4u, true, "OOB_SELECT 1, NUM_RECORDS past the vertices"}, + {1u, 0xffffffffu, true, "OOB_SELECT 1, NUM_RECORDS 0xffffffff"}, + {1u, 3u, true, "OOB_SELECT 1, NUM_RECORDS 3, selectors W Z 1 0", Wzyx}, {2u, 0u, false, "OOB_SELECT 2, no records"}, - {3u, 16u, true, "OOB_SELECT 3, whole element in range"}, + {2u, 1u, true, "OOB_SELECT 2, one record"}, + {2u, 16u, true, "OOB_SELECT 2, whole element in range"}, {3u, 0u, false, "OOB_SELECT 3, no records"}, - {0u, 16u, true, "OOB_SELECT 0, whole element in range"}, - {3u, 16u, true, "OOB_SELECT 3, selectors W Z 1 0", 7u | (6u << 3u) | (1u << 6u) | (0u << 9u)}, - {3u, 0u, false, "OOB_SELECT 3, no records, selectors W Z 1 0", 7u | (6u << 3u) | (1u << 6u) | (0u << 9u)}, + {3u, 8u, false, "OOB_SELECT 3, the element only partly fits"}, + {3u, 15u, false, "OOB_SELECT 3, the element misses its last byte"}, + {3u, 16u, true, "OOB_SELECT 3, whole element in range"}, + {3u, 0xffffffffu, true, "OOB_SELECT 3, NUM_RECORDS 0xffffffff"}, + {3u, 16u, true, "OOB_SELECT 3, selectors W Z 1 0", Wzyx}, + {3u, 8u, false, "OOB_SELECT 3, partly fits, selectors W Z 1 0", Wzyx}, }}; for (const auto& value : cases) { Draw(*device, value); diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index 449bc18609..ad2915b79d 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -45,7 +45,7 @@ - [Buffer descriptor CACHE_SWIZZLE hint](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver) - bit 30 of word 1 in a buffer resource descriptor (`words[1] & 0x40000000u`) is normalized to zero when binding guest buffers in `ShaderResources::addGuestBuffer`. On GFX10 (gfx1013), bit 30 of V# word 1 is `CACHE_SWIZZLE` (Mesa's `gfx10-rsrc.json`: STRIDE [29:16], CACHE_SWIZZLE [30], SWIZZLE_ENABLE [31]), a cache-bank hint with no effect on the address or data, which is dropped here. - [Buffer descriptors past the range limit](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver) - a buffer V# whose size is over `maxStorageBufferRange` is bound only up to the end of the readable registered allocation holding its base when that allocation ends before the limit, and as the empty placeholder buffer, like a null V#, when no registered allocation holds its base; a V# over an allocation that reaches the limit still throws. Titles leave such words in descriptor slots their shaders do not reach on the path taken (PPSA10528 binds `c8010002 2c6bec20 c0053c00 04000013`, about 33 TB from an address above the user address space). What the console returns for an access past the bound range is not known; here the device's robust buffer access decides. Follows the review of #3202, not measured on hardware. - [Vertex fetch overhang](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) - a zero-padded vertex fetch whose valid range (`num_records * stride`) ends less than one stride past the end of the readable memory at its base copies only the readable bytes and reads the rest as zeros, instead of failing the draw; a longer overhang still fails. This extends the strided buffer overhang rule above (the last record is the rounded-up `num_records`) to the staging copy and is not measured on hardware. The observed case is a fetch of 14858 records of 44 bytes that ends 8 bytes past the end of the 0xa0000-byte mapping holding it. -- [Zero-stride vertex fetch range](../../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - for a stride-0 V# with OOB_SELECT 2 the whole fetch reads memory unless NUM_RECORDS is 0; with OOB_SELECT 3 it reads zero when NUM_RECORDS is 0 and throws when the fetch only partly fits. Rules taken from the GFX10 OOB_SELECT table in Mesa [ac_descriptors.c](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/amd/common/ac_descriptors.c), not measured on hardware. Whether OOB_SELECT 3 checks each dword of a multi-dword fetch separately is unknown. OOB_SELECT 0 and 1 keep the structured record checks. +- [Zero-stride vertex fetch range](../../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a draw whose indices span NUM_RECORDS of a stride-0 `OOB_SELECT` 1 V# (some below, some at or past) throws: the pipeline binding has stride 0, so no vertex can read zero while another reads the element. An indirect draw is not checked. Unmeasured on gfx1013 (the console), with an instruction offset or SOFFSET, with a swizzled V# and in a vertex shader. Rules from #8299, measured on an RX 6700 XT (gfx1031) with raw ISA through the hardware oracle ([data](https://gist.github.com/thuanquang/3b25d677c1a32d79123dd2df1819dd83)): `buffer_load_format_x/xy/xyzw` and `buffer_load_dwordx4`, stride 0 and 16, all NUM_RECORDS. Stride 0 reads zero for `OOB_SELECT` 0 always; for 1 when index >= NUM_RECORDS (records); for 2 when NUM_RECORDS is 0; for 3 when NUM_RECORDS is under the fetch's bytes. Untyped loads check each dword under 3; only typed fetches are used here. - [Resource registration](../../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) (libSceAgcDriver) - registration always fails with `RESOURCE_REGISTRATION_UNAVAILABLE`, so the owner and resource queries, `sceAgcDriverSetResourceUserData`, `sceAgcDriverFindResourcesPublic` and `sceAgcDriverUnregisterAllResourcesForOwner` return the same error without reading their arguments. Their parameter lists follow the libSceGnmDriver counterparts and are unverified. `sceAgcDriverGetResourceRegistrationMaxNameLength` writes 0xfc and returns 0, as [prosper](https://github.com/mattias800/prosper) does from a title's registration configuration; the `uint32_t*` parameter matches the SDK wrapper `sce::Agc::ResourceRegistration::getMaxNameLength(unsigned int*)`. sharpemu writes 256 and KytyPS5 returns `RESOURCE_REGISTRATION_UNAVAILABLE`, so the value is unverified; a null output throws - [sceAgcDriverRegisterMultipleResources](../../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) uses the existing unavailable-registration state and leaves caller memory untouched. Its four arguments (uint32 handle output, uint32 owner, descriptor pointer, uint32 count) follow the original PPSA34502 / 01.010.000 codzm call. The caller builds 40-byte descriptors and explicitly accepts 0x8A6C9018 with an invalid output handle. Enabled registration and console argument-validation precedence are unverified. - [Shader headers](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - registration and ABI resolution read fixed header fields through aligned copies, so the header pointer itself may have any alignment. PPSA26344 passes headers 4 and 1 bytes past an 8-byte boundary, and PPSA01317 passes 4-byte-aligned headers to ABI resolution; console alignment requirements are unknown From bea48f5ee010915d29bf1dc8817e24f0a360aced Mon Sep 17 00:00:00 2001 From: Federico Marras Date: Wed, 21 Oct 2026 13:34:37 +0200 Subject: [PATCH 157/523] test(agc): keep the zero-stride render-target alias check on a fetch that reads --- core/libs/prx/libSceAgcDriver/tests/Recorder.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 888c3485c1..30747ce5d8 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -8940,8 +8940,8 @@ bool vertexTargetRangeTests(const Device& device, Recorder& recorder, bool impor const std::array range{std::pair{address + unit, address + 2 * unit}}; Require(StorageTexture::AnyPendingOverlaps(range), "the color producer did not leave GPU results pending"); }; - const auto attributeFor = [](std::uint64_t base, std::uint32_t stride, std::uint32_t fetchIndex) { - return ShaderRecompiler::VertexAttribute{0, 4, {{static_cast(base), static_cast(base >> 32u) | (stride << 16u), static_cast(bytes / 16), 77u << 12u}}, fetchIndex}; + const auto attributeFor = [](std::uint64_t base, std::uint32_t stride, std::uint32_t fetchIndex, std::uint32_t outOfBoundsSelect = 0) { + return ShaderRecompiler::VertexAttribute{0, 4, {{static_cast(base), static_cast(base >> 32u) | (stride << 16u), static_cast(bytes / 16), (77u << 12u) | (outOfBoundsSelect << 28u)}}, fetchIndex}; }; const auto check = [&](std::uint32_t edge) { std::vector texels(64 * 64 * 4); @@ -9012,9 +9012,12 @@ bool vertexTargetRangeTests(const Device& device, Recorder& recorder, bool impor reject([&] { Draw(context, consumer, indexed, shaders); }, "index buffer aliases the render target"); } reject([&] { Draw(context, consumer, triangle, shaders); }, "vertex buffer aliases the render target"); - vertex.vertexAttributes = {attributeFor(address, 0, fetchIndex)}; + vertex.vertexAttributes = {attributeFor(address, 0, fetchIndex, 3)}; reject([&] { Draw(context, consumer, draw, shaders); }, "vertex buffer aliases the render target"); } + vertex.vertexAttributes = {attributeFor(address, 0, 0)}; + Draw(context, consumer, triangle, shaders); + WaitForOptimizedPipelines(context.device); std::cout << "Vertex range and pending color producer tests passed (" << (imported ? "imported" : "copied") << ")\n"; return true; } From a3d739f170d9147d8fd52b5e51aa0bafca97f818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Wed, 21 Oct 2026 19:55:34 +0200 Subject: [PATCH 158/523] docs(agc): record the all-past OOB_SELECT 1 throw and the gfx1010 measurement Co-Authored-By: Claude --- docs/dev/debt/libSceAgcDriver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index ad2915b79d..44cde2dbcc 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -45,7 +45,7 @@ - [Buffer descriptor CACHE_SWIZZLE hint](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver) - bit 30 of word 1 in a buffer resource descriptor (`words[1] & 0x40000000u`) is normalized to zero when binding guest buffers in `ShaderResources::addGuestBuffer`. On GFX10 (gfx1013), bit 30 of V# word 1 is `CACHE_SWIZZLE` (Mesa's `gfx10-rsrc.json`: STRIDE [29:16], CACHE_SWIZZLE [30], SWIZZLE_ENABLE [31]), a cache-bank hint with no effect on the address or data, which is dropped here. - [Buffer descriptors past the range limit](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver) - a buffer V# whose size is over `maxStorageBufferRange` is bound only up to the end of the readable registered allocation holding its base when that allocation ends before the limit, and as the empty placeholder buffer, like a null V#, when no registered allocation holds its base; a V# over an allocation that reaches the limit still throws. Titles leave such words in descriptor slots their shaders do not reach on the path taken (PPSA10528 binds `c8010002 2c6bec20 c0053c00 04000013`, about 33 TB from an address above the user address space). What the console returns for an access past the bound range is not known; here the device's robust buffer access decides. Follows the review of #3202, not measured on hardware. - [Vertex fetch overhang](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver) - a zero-padded vertex fetch whose valid range (`num_records * stride`) ends less than one stride past the end of the readable memory at its base copies only the readable bytes and reads the rest as zeros, instead of failing the draw; a longer overhang still fails. This extends the strided buffer overhang rule above (the last record is the rounded-up `num_records`) to the staging copy and is not measured on hardware. The observed case is a fetch of 14858 records of 44 bytes that ends 8 bytes past the end of the 0xa0000-byte mapping holding it. -- [Zero-stride vertex fetch range](../../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a draw whose indices span NUM_RECORDS of a stride-0 `OOB_SELECT` 1 V# (some below, some at or past) throws: the pipeline binding has stride 0, so no vertex can read zero while another reads the element. An indirect draw is not checked. Unmeasured on gfx1013 (the console), with an instruction offset or SOFFSET, with a swizzled V# and in a vertex shader. Rules from #8299, measured on an RX 6700 XT (gfx1031) with raw ISA through the hardware oracle ([data](https://gist.github.com/thuanquang/3b25d677c1a32d79123dd2df1819dd83)): `buffer_load_format_x/xy/xyzw` and `buffer_load_dwordx4`, stride 0 and 16, all NUM_RECORDS. Stride 0 reads zero for `OOB_SELECT` 0 always; for 1 when index >= NUM_RECORDS (records); for 2 when NUM_RECORDS is 0; for 3 when NUM_RECORDS is under the fetch's bytes. Untyped loads check each dword under 3; only typed fetches are used here. +- [Zero-stride vertex fetch range](../../../core/libs/prx/libSceAgcDriver/Graphics/include/VertexInput.hpp) (libSceAgcDriver) - a draw whose highest index is at or past a nonzero NUM_RECORDS of a stride-0 `OOB_SELECT` 1 V# throws: the pipeline binding has stride 0, so vertices cannot differ. Only the highest index is checked, so a draw with every index past it throws too, where the hardware reads zero. An indirect draw is not checked. Unmeasured on gfx1013 (the console), with an instruction offset or SOFFSET, with a swizzled V# and in a vertex shader. Rules from #8299, measured on an RX 6700 XT (gfx1031), same results on gfx1010, with raw ISA ([data](https://gist.github.com/thuanquang/3b25d677c1a32d79123dd2df1819dd83)). Stride 0 reads zero for `OOB_SELECT` 0 always; for 1 when index >= NUM_RECORDS (records); for 2 when NUM_RECORDS is 0; for 3 when NUM_RECORDS is under the fetch's bytes. Untyped loads check each dword under 3; only typed fetches are used here. - [Resource registration](../../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) (libSceAgcDriver) - registration always fails with `RESOURCE_REGISTRATION_UNAVAILABLE`, so the owner and resource queries, `sceAgcDriverSetResourceUserData`, `sceAgcDriverFindResourcesPublic` and `sceAgcDriverUnregisterAllResourcesForOwner` return the same error without reading their arguments. Their parameter lists follow the libSceGnmDriver counterparts and are unverified. `sceAgcDriverGetResourceRegistrationMaxNameLength` writes 0xfc and returns 0, as [prosper](https://github.com/mattias800/prosper) does from a title's registration configuration; the `uint32_t*` parameter matches the SDK wrapper `sce::Agc::ResourceRegistration::getMaxNameLength(unsigned int*)`. sharpemu writes 256 and KytyPS5 returns `RESOURCE_REGISTRATION_UNAVAILABLE`, so the value is unverified; a null output throws - [sceAgcDriverRegisterMultipleResources](../../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) uses the existing unavailable-registration state and leaves caller memory untouched. Its four arguments (uint32 handle output, uint32 owner, descriptor pointer, uint32 count) follow the original PPSA34502 / 01.010.000 codzm call. The caller builds 40-byte descriptors and explicitly accepts 0x8A6C9018 with an invalid output handle. Enabled registration and console argument-validation precedence are unverified. - [Shader headers](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp) (libSceAgcDriver) - registration and ABI resolution read fixed header fields through aligned copies, so the header pointer itself may have any alignment. PPSA26344 passes headers 4 and 1 bytes past an 8-byte boundary, and PPSA01317 passes 4-byte-aligned headers to ABI resolution; console alignment requirements are unknown From 9998385047f557cef18cf00d3260d61f8e599cd3 Mon Sep 17 00:00:00 2001 From: Tamalero <11452991+Tamalero@users.noreply.github.com> Date: Wed, 21 Oct 2026 00:34:25 -0700 Subject: [PATCH 159/523] fix(recompiler): match image-table T# halves offset before and after readfirstlane A T# loaded as two s_load_dwordx4 halves from a pointer table, where the second half's offset is formed per lane (v_lshl_add_u32 key, 5, 16) before v_readfirstlane and the first half's after it (readfirstlane(key) << 5), was refused: the second half's +16 sat inside ReadFirstLane(Select(exec, ..., old), exec), so TryMakeTableColumn saw a different base and a zero addend. Compare such offsets in a first-lane normal form (lane value, mask, shift, addend), peeling selects the mask implies as the SRT walker already does. Co-Authored-By: Claude Opus 5.5 --- .../libSceAgcDriver/tests/ShaderMemory.cpp | 39 ++++++++++++++++ .../Optimization/src/ResourceTracker.cpp | 45 ++++++++++++++++++- docs/dev/debt/shader.md | 2 +- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp index a754f42271..3b602479d0 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp @@ -3551,6 +3551,43 @@ void verifyWaterfallImageTable() { require(!SrtWalker().ValidateRuntimeValue(planA, &firstOther), "waterfall image table: non-implied condition was accepted by validator"); } +void verifySplitWaterfallImageTable() { + using namespace ShaderRecompiler; + namespace Abi = ImageTableAbi; + static TableTexture texture; + alignas(256) static std::array root{}; + static std::array output{}; + const auto outputV = TableV(output.data(), 0u, static_cast(sizeof(output))); + std::copy(outputV.begin(), outputV.end(), root.begin() + 4); + const auto descriptor = TableT(texture.bytes.data(), TableFormat8888UNorm, TableType2D); + std::copy(descriptor.begin(), descriptor.end(), root.begin() + 0x40u / 4u); + const auto base = static_cast(reinterpret_cast(root.data())); + const std::array userData{static_cast(base), static_cast(base >> 32u)}; + // v_cmpx_gt_u32 exec, 6, v0; s17 = v_readfirstlane(v0); v1 = v_lshl_add_u32(v0, 5, addend) under that exec; + // s19 = v_readfirstlane(v1); s18 = s17 << 5; s_load_dwordx4 s[36:39] at s18 + 0x40 and s[40:43] at s19 + 0x40: + // the two halves of one T# record, with the second half's offset added before the lane read. + const auto code = [](std::uint32_t addend) { + return std::vector{0xf4080700u, 0xfa000010u, 0xf4080800u, 0xfa000020u, 0x7da80086u, 0x7e220500u, 0xd7460001u, 0x00010b00u | (addend << 18u), 0x7e260501u, 0x8f128511u, + 0xf4080900u, 0x24000040u, 0xf4080a00u, 0x26000040u, 0xf09c0f08u, 0x01090000u, 0xe0700000u, 0x80070000u, 0xbf810000u}; + }; + const auto split = code(0x90u); + auto request = TableRequest(split, userData); + request.shader.codeAddress = 0x3a000u; + const auto plan = GetResourcePlan(request); + require(plan->info.images.size() == 1u && plan->info.images[0].table == 0u, "split waterfall image table: the T# was not planned as a table"); + const auto& column = *plan->descriptorSources.at(plan->info.images[0].source).tableColumn; + require(column.address && column.stride == 32u && column.offset == 0x40u && column.dwordCount == 8u && column.foldedCarry == 16u, "split waterfall image table: the column is wrong"); + const auto compiled = CompileTable(request); + const auto& snapshot = compiled.capture->snapshot.tables.tables.at(0); + require((snapshot.codes[0] & Abi::PoisonFlag) == 0u && snapshot.words.at(snapshot.codes[0]).dwords == descriptor, "split waterfall image table: record 0 was not read whole"); + for (const std::uint32_t addend : {0xa0u, 0x88u}) { + const auto otherCode = code(addend); + auto other = TableRequest(otherCode, userData); + other.shader.codeAddress = 0x3b000u + addend * 0x100u; + expectFailure([&] { static_cast(GetResourcePlan(other)); }, "GetImageResource dword 0 is not a valid runtime value", "split waterfall image table: a second half outside the record was accepted"); + } +} + void verifyDescriptorPhis() { using namespace ShaderRecompiler; constexpr std::uint32_t Format8888UNorm = 56; @@ -7622,6 +7659,7 @@ int main(int argc, char** argv) { verifyChainedTableOffset(); verifyPointerChainImageTable(); verifyWaterfallImageTable(); + verifySplitWaterfallImageTable(); std::cout << "Image table keys, maps, records and registration passed\n"; return 0; } @@ -7685,6 +7723,7 @@ int main(int argc, char** argv) { verifyChainedTableOffset(); verifyPointerChainImageTable(); verifyWaterfallImageTable(); + verifySplitWaterfallImageTable(); verifyDescriptorPhis(); verifyUniformTableImage(); verifyLaneSelectedDescriptors(); diff --git a/core/shader/recompiler/Optimization/src/ResourceTracker.cpp b/core/shader/recompiler/Optimization/src/ResourceTracker.cpp index 6f52b4489a..0ffeb3be74 100644 --- a/core/shader/recompiler/Optimization/src/ResourceTracker.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceTracker.cpp @@ -577,6 +577,38 @@ private: return value; } + struct FirstLaneOffset { + const IrValue* lane = nullptr; + const IrValue* mask = nullptr; + std::uint32_t shift = 0; + std::uint32_t addend = 0; + }; + + static bool ParseFirstLaneOffset(IrValue* value, const IrValue* mask, FirstLaneOffset& form, std::uint32_t depth) { + std::uint32_t addend = 0; + value = SplitImmediateAddend(value, addend); + while (mask != nullptr && Detail::IsRuntimeSelect(value->Opcode()) && value->ArgumentCount() == 3u && Detail::ActiveMaskImplies(mask, value->Argument(0))) { + value = SplitImmediateAddend(value->Argument(1), addend); + } + std::uint32_t shift = 0; + if (depth != 0u && value->Opcode() == IrOpcode::ShiftLeftLogical32 && value->ArgumentCount() == 2u && immediateU32(value->Argument(1), shift) && shift < 32u) { + if (!ParseFirstLaneOffset(value->Argument(0)->Resolve(), mask, form, depth - 1u) || form.shift + shift >= 32u) return false; + form.shift += shift; + form.addend <<= shift; + } else if (depth != 0u && mask == nullptr && value->Opcode() == IrOpcode::ReadFirstLane && value->ArgumentCount() == 2u) { + if (!ParseFirstLaneOffset(value->Argument(0)->Resolve(), value->Argument(1)->Resolve(), form, depth - 1u)) return false; + } else { + if (mask == nullptr) return false; + form = FirstLaneOffset{value, mask, 0u, 0u}; + } + form.addend += addend; + return true; + } + + bool SameFirstLaneOffset(const FirstLaneOffset& left, const FirstLaneOffset& right) const { + return left.shift == right.shift && EquivalentValue(m_program.Resources(), left.mask, right.mask) && EquivalentValue(m_program.Resources(), left.lane, right.lane); + } + bool TryMakeTableColumn(IrValue& handle, bool sampler, bool r128, TableColumnPlan& plan) { const auto expected = sampler ? IrOpcode::GetSamplerResource : IrOpcode::GetImageResource; const std::uint32_t width = sampler ? 4u : 8u; @@ -602,6 +634,8 @@ private: bool folded = false; std::uint32_t first = 0; bool address = false; + FirstLaneOffset baseLane; + bool laneBase = false; for (std::uint32_t dword = 0; dword < words; dword++) { IrValue* read = ResolveInvariantPhi(m_program.Resources(), handle.Argument(dword)); if (read == nullptr) return false; @@ -613,11 +647,20 @@ private: } std::uint32_t readAddend = 0; IrValue* readBase = SplitImmediateAddend(read->Argument(1), readAddend); + bool sameBase = dword == 0u || EquivalentValue(m_program.Resources(), base, readBase); + if (!sameBase && laneBase) { + FirstLaneOffset lane; + if (ParseFirstLaneOffset(read->Argument(1)->Resolve(), nullptr, lane, phiSearchDepth) && SameFirstLaneOffset(baseLane, lane)) { + readAddend = lane.addend; + sameBase = true; + } + } if (dword == 0u) { first = memory->offset; address = addressRead; base = readBase; baseAddend = readAddend; + laneBase = ParseFirstLaneOffset(read->Argument(1)->Resolve(), nullptr, baseLane, phiSearchDepth) && baseLane.addend == baseAddend; } else if (addressRead != address || memory->offset + readAddend != first + baseAddend + dword * static_cast(sizeof(std::uint32_t))) { return false; } else if (readAddend != baseAddend) { @@ -634,7 +677,7 @@ private: if (heapHandle == nullptr) heapHandle = currentHandle; if (dword == 0u) { offset = read->Argument(1)->Resolve(); - } else if (!EquivalentValue(m_program.Resources(), base, readBase)) { + } else if (!sameBase) { return false; } plan.memory.push_back(memoryIndex); diff --git a/docs/dev/debt/shader.md b/docs/dev/debt/shader.md index 222397a960..40c427b7f5 100644 --- a/docs/dev/debt/shader.md +++ b/docs/dev/debt/shader.md @@ -41,7 +41,7 @@ - An [image table](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) snapshot holds at most 65536 keys per table, 65536 distinct images and 256 S# entries per shader; a key past a longer table's snapshot, or an entry over the budget, faults when selected. A selected column (a T# the SRT walker evaluates) holds its one record wherever it lies. - [Image tables](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) read by `s_load` through a 64-bit pointer hold the keys the key's known bits allow, at most 65536; a key past them, or an entry at a wrapping, unaligned or unmapped address, faults when selected. - An [image table](../../../core/shader/recompiler/Optimization/src/ResourceTracker.cpp) whose key is neither a V#-relative scalar read nor a value the SRT walker evaluates (for example a shifted or masked runtime-offset V# read, a per-lane value made uniform by `v_readfirstlane`, or a loop-carried phi) binds every record of the snapshot domain instead of the selected ones. So does a table read at `(key << n) & mask` with such a key: a key domain would select records by the unmasked key, so that key is not narrowed. A key the walker evaluates selects its one record instead, unless the shader may write the memory the key is read from at run time (the same buffer, or any memory when the read or a write goes through a pointer or a GPU-only descriptor); then the column binds the whole domain too. A write to the key's memory through another V# over the same bytes is not detected. A shader whose selected records would pass 64 images or 64 sampled image/sampler pairs binds, for the heaps with the most such reads, the union of their keys in one table instead. -- [Image table pointer indexing](../../../core/shader/recompiler/Optimization/src/ResourceTracker.cpp) propagates 32-bit value bounds through `ReadFirstLane` and `SelectU32` so waterfall key masks keep their bit domain. The SRT walker validates runtime select conditions whose active lane mask implies the condition through `LogicalAnd` conjunctions, assuming unselected inactive lanes need not be uniform across the full wave. +- [Image table pointer indexing](../../../core/shader/recompiler/Optimization/src/ResourceTracker.cpp) propagates 32-bit value bounds through `ReadFirstLane` and `SelectU32` so waterfall key masks keep their bit domain. The SRT walker validates runtime select conditions whose active lane mask implies the condition through `LogicalAnd` conjunctions, assuming unselected inactive lanes need not be uniform across the full wave. The same assumption lets the two `s_load`s of one T# record match when one offset is added before `v_readfirstlane` and the other after (`ReadFirstLane(Select(mask, (key << 5) + 16, old), mask)` against `ReadFirstLane(key, mask) << 5`): with no active lane the hardware reads lane 0 of the inactive arm, and the table still binds the whole record. - An [image table](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) T# of an MSAA or FMASK image, or of a color format under a depth compare, faults when selected: tables have no multisampled descriptors and no emulated color compare. - Direct [FMASK image loads](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) read uncompressed `Fmask32_S8_F8` through a single-mip 2D or 2D-array `R32_UINT` snapshot with `Z_64KB_X` addressing. Destination selectors may name X, zero or one. Other FMASK formats, CMASK compression, packed accesses and other layouts throw. The layout follows PAL's [FMASK-to-surface conversion](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/core/addrlib2.cpp#L877) and [32-bit view selectors](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/hw/gfxip/gfx9/gfx9Device.cpp#L4973). Native RDNA/PS5 image-load results and title rendering are unmeasured; compressed color sampling remains unsupported. - `image_store`, image atomics, PCK2/PCK4 and MIMG BY2/BY4 through an [image table](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) T# fault (`unsupported operation`) when an active lane executes them; the store is dropped and the load reads zeros. A T# whose words the SRT walker evaluates is bound directly instead, for every operation of the shader on it (`DirectImageHandles`), so only T#s keyed per lane or by a value the walker cannot evaluate reach a table with these operations; such a direct T# is read when the work is recorded, so a key its own dispatch writes is read before that write. From 78178e940e967076c4f942f4d51fb803ae930399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Wed, 21 Oct 2026 20:21:43 +0200 Subject: [PATCH 160/523] test(recompiler): drop code comments from split waterfall image table test Co-Authored-By: Claude --- core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp index 3b602479d0..16e54ee281 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp @@ -3563,9 +3563,6 @@ void verifySplitWaterfallImageTable() { std::copy(descriptor.begin(), descriptor.end(), root.begin() + 0x40u / 4u); const auto base = static_cast(reinterpret_cast(root.data())); const std::array userData{static_cast(base), static_cast(base >> 32u)}; - // v_cmpx_gt_u32 exec, 6, v0; s17 = v_readfirstlane(v0); v1 = v_lshl_add_u32(v0, 5, addend) under that exec; - // s19 = v_readfirstlane(v1); s18 = s17 << 5; s_load_dwordx4 s[36:39] at s18 + 0x40 and s[40:43] at s19 + 0x40: - // the two halves of one T# record, with the second half's offset added before the lane read. const auto code = [](std::uint32_t addend) { return std::vector{0xf4080700u, 0xfa000010u, 0xf4080800u, 0xfa000020u, 0x7da80086u, 0x7e220500u, 0xd7460001u, 0x00010b00u | (addend << 18u), 0x7e260501u, 0x8f128511u, 0xf4080900u, 0x24000040u, 0xf4080a00u, 0x26000040u, 0xf09c0f08u, 0x01090000u, 0xe0700000u, 0x80070000u, 0xbf810000u}; From 5266e7f770ac22a0e28e1602605a7aea06d3e1b8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:16:25 +0300 Subject: [PATCH 161/523] fix(http): recover cache and request option stub signatures verify argument widths against the local system module, including the four-register auth cache export and four-byte option outputs. keep every throwing stub body unchanged. --- core/libs/prx/libSceHttp/Export.cpp | 24 ++++++++++++------------ docs/dev/debt/libSceHttp.md | 12 ------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 5c537baf62..a8d7d7eeec 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -634,12 +634,12 @@ int APS5_VABI sceHttpsFreeCaList(int http_ctx_id, HttpsCaList* ca_list) { return 0; } -int APS5_VABI sceHttpAuthCacheImport(void) { +int APS5_VABI sceHttpAuthCacheImport(int contextId, const void* buffer, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpAuthCacheExport(void) { +int APS5_VABI sceHttpAuthCacheExport(int contextId, void* buffer, std::size_t size, std::size_t* requiredSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -687,17 +687,17 @@ int APS5_VABI sceHttpDbgGetRequestStat(void) { return 0; } -int APS5_VABI sceHttpAddRequestHeaderRaw(void) { +int APS5_VABI sceHttpAddRequestHeaderRaw(int id, const char* header, int mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpRemoveRequestHeader(void) { +int APS5_VABI sceHttpRemoveRequestHeader(int id, const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpGetAuthEnabled(void) { +int APS5_VABI sceHttpGetAuthEnabled(int id, int* enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -717,7 +717,7 @@ int APS5_VABI sceHttpGetCookie(void) { return 0; } -int APS5_VABI sceHttpGetCookieEnabled(void) { +int APS5_VABI sceHttpGetCookieEnabled(int id, int* enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -767,12 +767,12 @@ int APS5_VABI sceHttpGetCookieStats(void) { return 0; } -int APS5_VABI sceHttpSetPriorityOption(void) { +int APS5_VABI sceHttpSetPriorityOption(int id, int priority) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpSetPolicyOption(void) { +int APS5_VABI sceHttpSetPolicyOption(int id, int policy) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -788,7 +788,7 @@ int APS5_VABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int http_ctx_id, int en return SetHttpContextAcceptEncodingGzip(http_ctx_id, enable != 0) ? 0 : ERROR_INVALID_ID; } -int APS5_VABI sceHttpSetProxy(void) { +int APS5_VABI sceHttpSetProxy(int id, int mode, int authentication, const char* hostname, std::uint16_t port) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -798,7 +798,7 @@ int APS5_VABI sceHttpSetSocketCreationCallback(void) { return 0; } -int APS5_VABI sceHttpSetHttp09Enabled(void) { +int APS5_VABI sceHttpSetHttp09Enabled(int id, int enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -813,12 +813,12 @@ int APS5_VABI sceHttpGetConnectionStat(void) { return 0; } -int APS5_VABI sceHttpAddQuery(void) { +int APS5_VABI sceHttpAddQuery(int id, const char* query) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpSetDelayBuildRequestEnabled(void) { +int APS5_VABI sceHttpSetDelayBuildRequestEnabled(int id, int enabled) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 7119fff249..e743811644 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -21,8 +21,6 @@ - [sceHttpSetAuthInfoCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature and callback type follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.h) (PS4); no PS5 reference has them. The `id` is not checked, as in the other libSceHttp setters - [sceHttpWaitRequest](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the timeout is assumed to be in microseconds and a negative timeout to wait without limit; the invalid-value error for a null handle or event array or a non-positive event count is assumed, not measured. No request is ever pending, so a wait returns 0 events when its timeout passes - [sceHttpUriCopy](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - read from the disassembly of libSceHttp.sprx on firmware 13.60, not measured on a console and not compared with other emulators. The arguments are (out, src, pool, prepare, require), `prepare` before `require`, the reverse of `sceHttpUriParse`. A null source returns `ERROR_INVALID_URL`; without an output or a pool, a null `require` returns `ERROR_INVALID_VALUE`, otherwise the size is only reported. Only the scheme, hostname, path, username and password are copied, in that order, each with its terminator (a missing one becomes an empty string and takes one byte), and the port; the query, the fragment and the opaque flag stay 0. A pool that is too small clears the output and the pool and returns `ERROR_OUT_OF_MEMORY` (0x80431022, the console's out-of-size error) -- [sceHttpAuthCacheImport](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpAuthCacheExport](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgEnableProfile](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgSetPrintf](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgShowStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature @@ -32,13 +30,9 @@ - [sceHttpDbgShowMemoryPoolStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgGetRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpAddRequestHeaderRaw](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpRemoveRequestHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetAuthEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookie](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetCookieEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpAddCookie](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetCookieSendCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpCookieExport](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature @@ -48,16 +42,10 @@ - [sceHttpSetCookieMaxNum](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetCookieMaxNumPerDomain](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookieStats](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetPriorityOption](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetPolicyOption](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpRequestGetAllHeaders](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetProxy](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetHttp09Enabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetRegisteredCtxIds](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpAddQuery](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetDelayBuildRequestEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature ### Functional From 9f39eb44da1407ab6ef6b6af53279a72c1fa7e9f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:18:17 +0300 Subject: [PATCH 162/523] fix(json): correct the object erase return contract the system export leaves the return register unspecified on missing keys and tail-calls node removal on a match. declare void and check object contents instead of a fabricated removal count. guest_json passes on windows. --- core/libs/prx/libSceJson2/Export.cpp | 5 ++--- core/libs/tests/GuestJson.cpp | 7 ++++--- docs/dev/debt/libSceJson2.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 6231181390..ff9da15cec 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -730,14 +730,13 @@ ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iterator7advanceEm(ObjectIterator* } bool APS5_VABI _ZNK3sce4Json6Object5emptyEv(const Object* self) { return self->items->empty(); } std::size_t APS5_VABI _ZNK3sce4Json6Object4sizeEv(const Object* self) { return self->items->size(); } -std::size_t APS5_VABI _ZN3sce4Json6Object5eraseERKNS0_6StringE(Object* self, const String* key) { +void APS5_VABI _ZN3sce4Json6Object5eraseERKNS0_6StringE(Object* self, const String* key) { if (key == nullptr || key->text == nullptr) throw std::invalid_argument("sce::Json::Object::erase: null key"); auto it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return *pair.key.text == *key->text; }); - if (it == self->items->end()) return 0; + if (it == self->items->end()) return; Destroy(it->key); Destroy(it->value); self->items->erase(it); - return 1; } void APS5_VABI _ZN3sce4Json5ValueC1Ev(Value* self) { Construct(*self); } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index 16de775009..f6569900e7 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -145,7 +145,7 @@ void APS5_VABI _ZN3sce4Json5Array8iteratorC1ERKS2_(Iterator*, const Iterator*); Iterator* APS5_VABI _ZN3sce4Json5Array8iterator7advanceEm(Iterator*, std::size_t); void APS5_VABI _ZN3sce4Json6Object8iteratorC1ERKS2_(Iterator*, const Iterator*); Iterator* APS5_VABI _ZN3sce4Json6Object8iterator7advanceEm(Iterator*, std::size_t); -std::size_t APS5_VABI _ZN3sce4Json6Object5eraseERKNS0_6StringE(Object*, const String*); +void APS5_VABI _ZN3sce4Json6Object5eraseERKNS0_6StringE(Object*, const String*); } static void Check(bool value, int line) { @@ -697,9 +697,10 @@ static void ObjectOperations() { _ZN3sce4Json6Object8iteratorD1Ev(©); Require(_ZNK3sce4Json6Object4sizeEv(&object) == 3); - Require(_ZN3sce4Json6Object5eraseERKNS0_6StringE(&object, &k2) == 1); + _ZN3sce4Json6Object5eraseERKNS0_6StringE(&object, &k2); + Require(_ZNK3sce4Json6Object4sizeEv(&object) == 2); + _ZN3sce4Json6Object5eraseERKNS0_6StringE(&object, &k2); Require(_ZNK3sce4Json6Object4sizeEv(&object) == 2); - Require(_ZN3sce4Json6Object5eraseERKNS0_6StringE(&object, &k2) == 0); Iterator findResult{}, end{}; _ZNK3sce4Json6Object4findERKNS0_6StringE(&findResult, &object, &k2); diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 3a89284e01..015e476dac 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -10,7 +10,7 @@ - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown - [sce::Json::String size, capacity, resize, compare and operator==(const String&), append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - member of `Value` returning `bool` as a conversion operator, with no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: evaluates to `false` for `TypeNull` and boolean `false`, and `true` for boolean `true`. For all other value types it currently returns `true`; whether the console converts them, throws, or checks container emptiness is unverified -- [sce::Json::Object::insert and sce::Json::Object::erase](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - signatures and behaviors have no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: `insert` returns an iterator through a result pointer before `this` as `Object::begin` does, and takes a const Pair reference. If the key exists, it updates the existing value and returns an iterator to it; otherwise it appends a deep copy of the pair and returns an iterator to the new element; whether the console updates existing keys, appends duplicates, or ignores duplicate insertion is unverified. `erase` removes the item with matching key and returns 1 if found and removed, or 0 if not; whether the console returns size_t, bool, or an iterator is unverified +- [sce::Json::Object::insert](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - signatures and behaviors have no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: `insert` returns an iterator through a result pointer before `this` as `Object::begin` does, and takes a const Pair reference. If the key exists, it updates the existing value and returns an iterator to it; otherwise it appends a deep copy of the pair and returns an iterator to the new element; whether the console updates existing keys, appends duplicates, or ignores duplicate insertion is unverified. ### Functional From 6f1e594cebbaeeb93fc683f37aa5243c7e74243a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:20:15 +0300 Subject: [PATCH 163/523] fix(np): recover session signaling query argument lists follow five exported dispatchers in the local system module. retain opaque payload pointers and document unresolved layouts instead of guessing their fields. throwing stub bodies remain unchanged. --- core/libs/prx/libSceNpSessionSignaling/Export.cpp | 10 +++++----- docs/dev/debt/libSceNpSessionSignaling.md | 5 ++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index 7dfa95ff73..8c60a0db57 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -79,7 +79,7 @@ int APS5_VABI sceNpSessionSignalingRequestPrepare(uint32_t contextId, uint32_t* return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } -int APS5_VABI sceNpSessionSignalingGetMemoryInfo(void) { +int APS5_VABI sceNpSessionSignalingGetMemoryInfo(void* memoryInfo) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -89,22 +89,22 @@ int APS5_VABI sceNpSessionSignalingGetConnectionStatistics(void) { return 0; } -int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress(void) { +int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress(std::uint32_t contextId, const void* peerAddress, void* connectionId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpSessionSignalingGetGroupFromSessionId(void) { +int APS5_VABI sceNpSessionSignalingGetGroupFromSessionId(std::uint32_t contextId, const void* sessionId, void* groupId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpSessionSignalingGetGroupInfo() { +int APS5_VABI sceNpSessionSignalingGetGroupInfo(std::uint32_t contextId, std::uint32_t groupId, void* groupInfo) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpSessionSignalingGetConnectionFromNetAddress2() { +int APS5_VABI sceNpSessionSignalingGetConnectionFromNetAddress2(std::uint32_t contextId, std::uint32_t addressType, std::uint32_t address, std::uint16_t port, void* connectionId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNpSessionSignaling.md b/docs/dev/debt/libSceNpSessionSignaling.md index f5f1396a51..d37e80e5ca 100644 --- a/docs/dev/debt/libSceNpSessionSignaling.md +++ b/docs/dev/debt/libSceNpSessionSignaling.md @@ -2,9 +2,8 @@ ### Unknown function info -- [sceNpSessionSignalingGetConnectionFromPeerAddress](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - imported by PPSA01289 and added without an implementation; unknown signature -- [sceNpSessionSignalingGetMemoryInfo](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - unknown signature - [sceNpSessionSignalingGetConnectionStatistics](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - unknown signature - [sceNpSessionSignalingGetConnectionStatus](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - `(int32 context id, int32 connection id, int32* status, SceNetInAddr* peer address, uint16* peer port)` is ASTRO BOT's (PPSA21564 01.007.000) call at 0x30622B: the context id from a 32-bit field, the connection id, three stack outputs, a negative result taken as an error, then the address passed to `sceNetInetNtop(AF_INET, ...)` and the port to `sceNetNtohs`. It matches PS4's `sceNpSignalingGetConnectionStatus`. The title reaches it only for a connection id from the signaling library, which offline never hands one out. No session ever activates offline, so every connection id answers `CONN_NOT_FOUND` (0x8055330C, from [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/64d198190fa05d37cbb1fa29c50f967c376fce55/src/core/libraries/np/np_error.h)) without writing the outputs, and a null status answers `INVALID_ARGUMENT`. The context id is not checked, as contexts are not tracked. The console's answer for an unknown context or connection is unverified -- PPSA31327 imports declared without parameters, signatures unknown: [sceNpSessionSignalingGetGroupInfo, sceNpSessionSignalingGetConnectionFromNetAddress2](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling), imported by Yakuza 0 Director's Cut and not called before the main menu. - [sceNpSessionSignalingCreateJoinActivate, sceNpSessionSignalingGetConnectionFromNetAddress, sceNpSessionSignalingGetCustomProperty, sceNpSessionSignalingGetGroupFromPeerAddress, sceNpSessionSignalingInitializeWithPort, sceNpSessionSignalingJoinActivate and sceNpSessionSignalingSetCustomProperty](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSessionSignaling.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. + +- [sceNpSessionSignalingGetMemoryInfo, sceNpSessionSignalingGetConnectionFromPeerAddress, sceNpSessionSignalingGetGroupFromSessionId, sceNpSessionSignalingGetGroupInfo, sceNpSessionSignalingGetConnectionFromNetAddress2](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) - argument registers and scalar widths follow the system module exports `lbXTXRG5nyM`, `dJJ0UPrrsok`, `UGcXquhVYrY`, `WtOs6cUbrx8` and `hbNcE4fnBDs`. Pointer payloads remain opaque: their layouts and the address-type values require tracing the virtual callees. All five exports remain throwing stubs. From 7e8fede88c44d668e1f259b1060414db13ca6857 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:21:24 +0300 Subject: [PATCH 164/523] fix(rtc): restore the complete system timezone table decode all 70 entries at 0x4b80 used by the lookup at 0x15d0. preserve lookup order and the parser's uppercase second-letter check. cover missing zones, case handling and prefix matching in guest_rtc on windows. --- core/libs/prx/libSceRtc/Export.cpp | 22 ++++++++++++++-------- core/libs/tests/GuestRtc.cpp | 16 ++++++++++++++++ docs/dev/debt/libSceRtc.md | 1 - 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceRtc/Export.cpp b/core/libs/prx/libSceRtc/Export.cpp index 43d049c976..7364077dff 100644 --- a/core/libs/prx/libSceRtc/Export.cpp +++ b/core/libs/prx/libSceRtc/Export.cpp @@ -270,14 +270,20 @@ bool parseZoneName(const char* cursor, std::int64_t& offsetMinutes) { const char* name; std::int64_t minutes; } zones[] = { - {"WETDST", 60}, {"METDST", 120}, {"EETDST", 180}, {"SWT", 60}, {"MEZ", 60}, {"AESST", 660}, {"ACSST", 630}, - {"CAST", 570}, {"CADT", 630}, {"SAST", 570}, {"SADT", 630}, {"WST", 480}, {"WDT", 540}, - {"GMT", 0}, {"WET", 0}, {"BST", 60}, {"WAT", -60}, {"AST", -240}, {"ADT", -180}, {"EST", -300}, {"EDT", -240}, - {"CST", -360}, {"CDT", -300}, {"MST", -420}, {"MDT", -360}, {"PST", -480}, {"PDT", -420}, {"YST", -540}, {"YDT", -480}, - {"HDT", -540}, {"CAT", -600}, {"AHST", -600}, {"NST", -150}, {"NFT", -150}, {"NDT", -90}, {"IDLW", -720}, {"CET", 60}, {"MET", 60}, - {"DNT", 60}, {"NOR", 60}, {"SET", 60}, {"MEWT", 60}, {"MEST", 120}, {"CEST", 120}, {"SST", 120}, {"FWT", 120}, {"FST", 60}, {"EET", 120}, {"IST", 120}, - {"WAST", 420}, {"WADT", 480}, {"CCT", 480}, {"AWST", 480}, {"JST", 540}, {"KST", 540}, {"ACST", 570}, {"EAST", 600}, - {"GST", 600}, {"AEST", 600}, {"NZT", 720}, {"NZST", 720}, {"NZDT", 780}, {"IDLE", 720}, {"JT", 450}, + {"GMT", 0}, {"EST", -300}, {"EDT", -240}, {"CST", -360}, {"CDT", -300}, + {"MST", -420}, {"MDT", -360}, {"PST", -480}, {"PDT", -420}, {"NZDT", 780}, + {"NZST", 720}, {"IDLE", 720}, {"NZT", 720}, {"AESST", 660}, {"ACSST", 630}, + {"CADT", 630}, {"SADT", 630}, {"AEST", 600}, {"EAST", 600}, {"GST", 600}, + {"LIGT", 600}, {"ACST", 570}, {"SAST", 570}, {"CAST", 570}, {"AWSST", 540}, + {"JST", 540}, {"KST", 540}, {"WDT", 540}, {"MT", 510}, {"AWST", 480}, + {"CCT", 480}, {"WADT", 480}, {"WST", 480}, {"JT", 450}, {"WAST", 420}, + {"IT", 210}, {"BT", 180}, {"EETDST", 180}, {"EET", 120}, {"CETDST", 120}, {"CEST", 120}, + {"FWT", 120}, {"IST", 120}, {"MEST", 120}, {"METDST", 120}, {"SST", 120}, + {"BST", 60}, {"CET", 60}, {"DNT", 60}, {"FST", 60}, {"MET", 60}, + {"MEWT", 60}, {"MEZ", 60}, {"NOR", 60}, {"SET", 60}, {"SWT", 60}, + {"WETDST", 60}, {"WET", 0}, {"WAT", -60}, {"NDT", -90}, {"ADT", -180}, + {"NFT", -150}, {"NST", -150}, {"AST", -240}, {"YDT", -480}, {"HDT", -540}, + {"YST", -540}, {"AHST", -600}, {"CAT", -600}, {"NT", -660}, {"IDLW", -720}, }; for (const auto& zone : zones) { const char* name = cursor; diff --git a/core/libs/tests/GuestRtc.cpp b/core/libs/tests/GuestRtc.cpp index a5585f843d..53e55fd327 100644 --- a/core/libs/tests/GuestRtc.cpp +++ b/core/libs/tests/GuestRtc.cpp @@ -438,6 +438,8 @@ int main() { const char* name; std::int64_t minutes; } additionalZones[] = { + {"LIGT", 600}, {"AWSST", 540}, {"CETDST", 120}, + {"DNT", 60}, {"NOR", 60}, {"SET", 60}, {"NFT", -150}, {"SWT", 60}, {"MEZ", 60}, {"WETDST", 60}, {"METDST", 120}, {"EETDST", 180}, {"AESST", 660}, {"ACSST", 630}, {"CAST", 570}, {"CADT", 630}, {"SAST", 570}, {"SADT", 630}, {"WST", 480}, {"WDT", 540}, }; @@ -463,6 +465,20 @@ int main() { Require(tick.tick == at(2000, 3, 1, 8, 9, 10) - static_cast(zone.minutes * 60000000)); } + const struct { + const char* name; + std::int64_t minutes; + } secondLetterZones[] = { + {"MT", 0}, {"mt", 510}, {"IT", 0}, {"it", 210}, + {"BT", 0}, {"bt", 180}, {"NT", 0}, {"nt", -660}, + }; + for (const auto& zone : secondLetterZones) { + char text[80]; + std::snprintf(text, sizeof(text), "Thu, 29 Feb 2024 12:34:56 %s", zone.name); + Require(sceRtcParseDateTime(&tick, text) == 0); + Require(tick.tick == leapDayTick - 789000ull - static_cast(zone.minutes * 60000000)); + } + RtcTick source{leapDayTick}; RtcTick result{}; Require(sceRtcTickAddSeconds(&result, &source, 3600) == 0 && result.tick == leapDayTick + 3600000000ull); diff --git a/docs/dev/debt/libSceRtc.md b/docs/dev/debt/libSceRtc.md index cf5399b601..8b683a05c6 100644 --- a/docs/dev/debt/libSceRtc.md +++ b/docs/dev/debt/libSceRtc.md @@ -6,7 +6,6 @@ ### Unknown function info -- [sceRtcParseDateTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - the zone name table matches every name of 1 to 3 uppercase letters as a payload process on PS5 firmware 13.60 parsed them in the RFC 2822 form; of the longer names only real ones were tried (ACDT, AEST, CEST, NZDT, MESZ and others). Any other name of 4 or more letters is assumed to take the military letter offset of its first character, so a longer name the console's table holds but nobody measured gets the wrong offset - [sceRtcParseDateTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - a blank zone after seconds is accepted. With fully allocated input storage on PS5 firmware 13.42 and again on 13.60, the same NUL-terminated text returned `BAD_PARSE` when the byte after its terminator was zero and succeeded when that byte was uppercase `T`. This dependence on storage after the terminator is not modeled; controlled backing-byte measurements in a title process are unavailable. - [sceRtcFormatRFC2822, sceRtcFormatRFC2822LocalTime, sceRtcFormatRFC3339LocalTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - only shadPS4 (PS4) has bodies. A null tick formats the current time, as shadPS4 does and as these three and `sceRtcFormatRFC3339` did in a payload process on a PS5 (firmware 13.60); offsets outside ±1439 minutes return `INVALID_VALUE` like `sceRtcFormatRFC3339`, also with a null tick there; the local-time variants use the host's offset at the given instant (shadPS4 uses `sceKernelGettimezone`, the current offset) - [sceRtcGetDosTime, sceRtcSetDosTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - bit layout from shadPS4 (PS4, the standard FAT date/time). A year outside 1980-2107 makes `GetDosTime` return `INVALID_YEAR` after writing 0 for an earlier one and `0xff9fbf7d` (2107-12-31 23:59:58, the largest encodable time) for a later one, and `SetDosTime` decodes the year as the unsigned 7-bit FAT field (1980-2107) without validating the fields, as the disassembly-derived [fpPS4](https://github.com/red-prig/fpPS4) reimplementation of the PS4 library does; not confirmed on a PS5 title, and shadPS4 instead masks out-of-range years into the packed value and sign-extends the 16-bit date From 2a9e6d8defc722166c4aba2b23730b4aebbb68c8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:23:02 +0300 Subject: [PATCH 165/523] fix(screenshot): recover overlay image stub signatures verify the path and two 32-bit coordinates in the overlay loader and the fourth origin argument in the forwarding export. retain both throwing bodies and remove the resolved signature debt. --- core/libs/prx/libSceScreenShot/Export.cpp | 4 ++-- docs/dev/debt/libSceScreenShot.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceScreenShot/Export.cpp b/core/libs/prx/libSceScreenShot/Export.cpp index ae99fefcf6..3f898a1f74 100644 --- a/core/libs/prx/libSceScreenShot/Export.cpp +++ b/core/libs/prx/libSceScreenShot/Export.cpp @@ -5,11 +5,11 @@ extern "C" { -int APS5_VABI sceScreenShotSetOverlayImage() { +int APS5_VABI sceScreenShotSetOverlayImage(const char* path, int x, int y) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceScreenShotSetOverlayImageWithOrigin() { +int APS5_VABI sceScreenShotSetOverlayImageWithOrigin(const char* path, int x, int y, int origin) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceScreenShot.md b/docs/dev/debt/libSceScreenShot.md index 568cb899b6..2e1f886b38 100644 --- a/docs/dev/debt/libSceScreenShot.md +++ b/docs/dev/debt/libSceScreenShot.md @@ -3,4 +3,3 @@ ### Unknown function info - [sceScreenShotCapture, sceScreenShotDisable, sceScreenShotDisableNotification, sceScreenShotEnable, sceScreenShotEnableNotification, sceScreenShotGetAppInfo, sceScreenShotGetDrcParam, sceScreenShotIsDisabled, sceScreenShotIsVshScreenCaptureDisabled, sceScreenShotSetDrcParam and sceScreenShotSetParam](../../../core/libs/prx/libSceScreenShot/Export.cpp) (libSceScreenShot) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceScreenShot.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. -- [sceScreenShotSetOverlayImage and sceScreenShotSetOverlayImageWithOrigin](../../../core/libs/prx/libSceScreenShot/Export.cpp) (libSceScreenShot) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceScreenShot.c). The PS4 compatibility implementation also exposes the names but declares no parameters; no PS5 prototype was found. The declarations have no parameters solely to resolve known imports and make no ABI claim From d402d576fb1dbf266467554573806b2500f1e5a1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:24:54 +0300 Subject: [PATCH 166/523] fix(voiceqos): recover packet and attribute stub signatures restore packet size pointers, attribute buffers, debug query arguments and hq initialization arguments from the system module. correct the writable remote attribute output and align existing stub tests with the recovered declarations. both guest_voice_qos tests pass on windows. --- core/libs/prx/libSceVoiceQoS/Export.cpp | 8 ++++---- core/libs/tests/GuestVoiceQoS.cpp | 10 +++++----- docs/dev/debt/libSceVoiceQoS.md | 3 +-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceVoiceQoS/Export.cpp b/core/libs/prx/libSceVoiceQoS/Export.cpp index 96053a9e69..3e02a10932 100644 --- a/core/libs/prx/libSceVoiceQoS/Export.cpp +++ b/core/libs/prx/libSceVoiceQoS/Export.cpp @@ -148,7 +148,7 @@ int APS5_VABI sceVoiceQoSDisconnect(int32_t connectionId) { return Locked([&](QoS& qos) { return qos.connections.erase(connectionId) != 0 ? 0 : SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; }); } -int APS5_VABI sceVoiceQoSSetConnectionAttribute() { +int APS5_VABI sceVoiceQoSSetConnectionAttribute(std::int32_t connectionId, std::int32_t attributeId, const void* value, std::int32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -180,7 +180,7 @@ int APS5_VABI sceVoiceQoSGetLocalEndpointAttribute(int32_t localId, int32_t attr return Locked([&](QoS& qos) { return GetAttribute(qos.locals, localId, attributeId, value, size); }); } -int APS5_VABI sceVoiceQoSGetRemoteEndpointAttribute(int32_t remoteId, int32_t attributeId, const void* value, int32_t size) { +int APS5_VABI sceVoiceQoSGetRemoteEndpointAttribute(int32_t remoteId, int32_t attributeId, void* value, int32_t size) { (void)remoteId; (void)attributeId; (void)value; @@ -237,12 +237,12 @@ int APS5_VABI sceVoiceQoSGetStatus(int32_t connectionId, int32_t statusId, void* return 0; } -int APS5_VABI sceVoiceQoSDebugGetStatus() { +int APS5_VABI sceVoiceQoSDebugGetStatus(std::int32_t connectionId, std::int32_t statusId, void* value, std::int32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVoiceQoSInitHQ() { +int APS5_VABI sceVoiceQoSInitHQ(void* memory, std::uint32_t memorySize, std::int32_t appType) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestVoiceQoS.cpp b/core/libs/tests/GuestVoiceQoS.cpp index 49d2671371..9be6bbae28 100644 --- a/core/libs/tests/GuestVoiceQoS.cpp +++ b/core/libs/tests/GuestVoiceQoS.cpp @@ -16,15 +16,15 @@ int APS5_VABI sceVoiceQoSDisconnect(std::int32_t); int APS5_VABI sceVoiceQoSSetLocalEndpointAttribute(std::int32_t, std::int32_t, const void*, std::int32_t); int APS5_VABI sceVoiceQoSSetRemoteEndpointAttribute(std::int32_t, std::int32_t, const void*, std::int32_t); int APS5_VABI sceVoiceQoSGetLocalEndpointAttribute(std::int32_t, std::int32_t, void*, std::int32_t); -int APS5_VABI sceVoiceQoSGetRemoteEndpointAttribute(std::int32_t, std::int32_t, const void*, std::int32_t); +int APS5_VABI sceVoiceQoSGetRemoteEndpointAttribute(std::int32_t, std::int32_t, void*, std::int32_t); int APS5_VABI sceVoiceQoSGetConnectionAttribute(std::int32_t, std::int32_t, void*, std::int32_t); int APS5_VABI sceVoiceQoSGetLocalEndpoint(std::int32_t, std::int32_t*); int APS5_VABI sceVoiceQoSGetRemoteEndpoint(std::int32_t, std::int32_t*); int APS5_VABI sceVoiceQoSEnableChat(); int APS5_VABI sceVoiceQoSDisableChat(); int APS5_VABI sceVoiceQoSGetStatus(std::int32_t, std::int32_t, void*, std::int32_t); -int APS5_VABI sceVoiceQoSDebugGetStatus(); -int APS5_VABI sceVoiceQoSInitHQ(); +int APS5_VABI sceVoiceQoSDebugGetStatus(std::int32_t, std::int32_t, void*, std::int32_t); +int APS5_VABI sceVoiceQoSInitHQ(void*, std::uint32_t, std::int32_t); int APS5_VABI sceVoiceQoSSetMode(); int APS5_VABI sceVoiceQoSTerminate(); int APS5_VABI sceVoiceQoSReadPacket(std::int32_t, void*, std::uint32_t*); @@ -157,8 +157,8 @@ int main(int argc, char** argv) { Require(NotImplemented([] { sceVoiceQoSEnableChat(); })); Require(NotImplemented([] { sceVoiceQoSDisableChat(); })); Require(NotImplemented([] { sceVoiceQoSGetStatus(1, 1, nullptr, 0); })); - Require(NotImplemented([] { sceVoiceQoSDebugGetStatus(); })); - Require(NotImplemented([] { sceVoiceQoSInitHQ(); })); + Require(NotImplemented([] { sceVoiceQoSDebugGetStatus(0, 200, nullptr, 4); })); + Require(NotImplemented([] { sceVoiceQoSInitHQ(nullptr, 0xc0000, 0x80000); })); Require(NotImplemented([] { sceVoiceQoSSetMode(); })); Require(NotImplemented([] { sceVoiceQoSTerminate(); })); } diff --git a/docs/dev/debt/libSceVoiceQoS.md b/docs/dev/debt/libSceVoiceQoS.md index fa6732187f..ba43885880 100644 --- a/docs/dev/debt/libSceVoiceQoS.md +++ b/docs/dev/debt/libSceVoiceQoS.md @@ -7,9 +7,8 @@ ### Unknown function info -- [sceVoiceQoSSetConnectionAttribute](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS, NID `KCGOxg8iX7s`) is a throwing stub declared without parameters, like the other endpoint calls of the library: PPSA04159 fails to load without it and its signature is unknown. The name hashes to the imported NID (`tools/nid_names.py`) - [sceVoiceQoSInit](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `0x804E0905` for a second initialization comes from [prosper](https://github.com/mattias800/prosper)'s reading of the module and was not measured: on a PS5 a valid call blocks inside a homebrew payload, so it never returns there. PPSA26344's two call sites (`0xE307BF`, `0x151C45D`) pass an allocated pointer, a 0x40000-byte block and app type 0x10000000; the app type and block are not used. `sceVoiceQoSCreateLocalEndpoint` takes four arguments (`int* id, userId, deviceInId, deviceOutId`) as declared in fpPS4 (`src/ps4_libscevoice.pas`, `ps4_sceVoiceQoSCreateLocalEndpoint`); signatures for `DeleteLocalEndpoint(id)`, `CreateRemoteEndpoint(int* id)`, `DeleteRemoteEndpoint(id)`, `Connect(int* id, local, remote)`, `Disconnect(connection)`, `SetLocalEndpointAttribute(id, attribute, value, size)`, `SetRemoteEndpointAttribute(id, attribute, value, size)`, and `GetLocalEndpointAttribute(id, attribute, value, size)` were verified against call sites in Aliens: Fireteam Elite (PPSA03393, e.g. `0x76D132`, `0x76D16E`, `0x76CECB`). Endpoints and connections are tracked by allocated id; duplicate connections and deleting an endpoint while referenced in a connection are permitted as the console is not known to reject them; endpoint attributes are stored as given (including empty or non-positive sizes), as their ids and defaults are unknown. The not-initialized error 0x804E0901 is assumed from the 0x804E09xx numbering, and unknown ids return `ARGUMENT_INVALID`. Aliens: Fireteam Elite (PPSA03393) uses all implemented calls on its load path; it stops with `not implemented` when any unimplemented call throws -- [libSceVoiceQoS firmware exports](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `sceVoiceQoSGetRemoteEndpointAttribute`, `sceVoiceQoSGetConnectionAttribute` and `sceVoiceQoSGetStatus` follow public PS4 SDK prototypes in [Orbital](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json#L2436-L2441) and remain throwing stubs as endpoint and connection attribute storage is unverified on hardware. `sceVoiceQoSEnableChat`, `sceVoiceQoSDisableChat`, `sceVoiceQoSDebugGetStatus`, `sceVoiceQoSInitHQ`, `sceVoiceQoSSetMode`, and `sceVoiceQoSTerminate` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws +- [libSceVoiceQoS firmware exports](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `sceVoiceQoSGetRemoteEndpointAttribute`, `sceVoiceQoSGetConnectionAttribute` and `sceVoiceQoSGetStatus` follow public PS4 SDK prototypes in [Orbital](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json#L2436-L2441) and remain throwing stubs as endpoint and connection attribute storage is unverified on hardware. `sceVoiceQoSSetMode` and `sceVoiceQoSTerminate` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws ### Functional From b078879d07078d469ae27d744f36fb3ced957809 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:36:16 +0300 Subject: [PATCH 167/523] fix(np): recover session signaling query structures --- .../prx/libSceNpSessionSignaling/Export.cpp | 34 ++++++++++++++++--- docs/dev/debt/libSceNpSessionSignaling.md | 2 -- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index 8c60a0db57..4266f0fcf4 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -10,6 +10,30 @@ static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND = static_cast static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE = static_cast(0x80552D06); static std::atomic g_nextContext{1}; +struct SessionSignalingMemoryInfo { + std::uint64_t poolSize; + std::uint64_t usedSize; + std::uint64_t peakUsedSize; +}; + +struct SessionSignalingPeerAddress { + std::uint64_t accountId; + std::uint32_t platform; +}; + +struct SessionSignalingGroupInfo { + std::uint32_t connectionIds[64]; + std::uint64_t connectionCount; +}; + +static_assert(sizeof(SessionSignalingMemoryInfo) == 24); +static_assert(offsetof(SessionSignalingMemoryInfo, usedSize) == 8); +static_assert(offsetof(SessionSignalingMemoryInfo, peakUsedSize) == 16); +static_assert(sizeof(SessionSignalingPeerAddress) == 16); +static_assert(offsetof(SessionSignalingPeerAddress, platform) == 8); +static_assert(sizeof(SessionSignalingGroupInfo) == 264); +static_assert(offsetof(SessionSignalingGroupInfo, connectionCount) == 256); + extern "C" { int APS5_VABI sceNpSessionSignalingInitialize(void* param) { @@ -79,7 +103,7 @@ int APS5_VABI sceNpSessionSignalingRequestPrepare(uint32_t contextId, uint32_t* return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } -int APS5_VABI sceNpSessionSignalingGetMemoryInfo(void* memoryInfo) { +int APS5_VABI sceNpSessionSignalingGetMemoryInfo(SessionSignalingMemoryInfo* memoryInfo) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -89,22 +113,22 @@ int APS5_VABI sceNpSessionSignalingGetConnectionStatistics(void) { return 0; } -int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress(std::uint32_t contextId, const void* peerAddress, void* connectionId) { +int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress(std::uint32_t contextId, const SessionSignalingPeerAddress* peerAddress, std::uint32_t* connectionId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpSessionSignalingGetGroupFromSessionId(std::uint32_t contextId, const void* sessionId, void* groupId) { +int APS5_VABI sceNpSessionSignalingGetGroupFromSessionId(std::uint32_t contextId, const char* sessionId, std::uint32_t* groupId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpSessionSignalingGetGroupInfo(std::uint32_t contextId, std::uint32_t groupId, void* groupInfo) { +int APS5_VABI sceNpSessionSignalingGetGroupInfo(std::uint32_t contextId, std::uint32_t groupId, SessionSignalingGroupInfo* groupInfo) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNpSessionSignalingGetConnectionFromNetAddress2(std::uint32_t contextId, std::uint32_t addressType, std::uint32_t address, std::uint16_t port, void* connectionId) { +int APS5_VABI sceNpSessionSignalingGetConnectionFromNetAddress2(std::uint32_t contextId, std::uint32_t groupId, std::uint32_t address, std::uint16_t port, SessionSignalingGroupInfo* groupInfo) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNpSessionSignaling.md b/docs/dev/debt/libSceNpSessionSignaling.md index d37e80e5ca..b797d16a65 100644 --- a/docs/dev/debt/libSceNpSessionSignaling.md +++ b/docs/dev/debt/libSceNpSessionSignaling.md @@ -5,5 +5,3 @@ - [sceNpSessionSignalingGetConnectionStatistics](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - unknown signature - [sceNpSessionSignalingGetConnectionStatus](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - `(int32 context id, int32 connection id, int32* status, SceNetInAddr* peer address, uint16* peer port)` is ASTRO BOT's (PPSA21564 01.007.000) call at 0x30622B: the context id from a 32-bit field, the connection id, three stack outputs, a negative result taken as an error, then the address passed to `sceNetInetNtop(AF_INET, ...)` and the port to `sceNetNtohs`. It matches PS4's `sceNpSignalingGetConnectionStatus`. The title reaches it only for a connection id from the signaling library, which offline never hands one out. No session ever activates offline, so every connection id answers `CONN_NOT_FOUND` (0x8055330C, from [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/64d198190fa05d37cbb1fa29c50f967c376fce55/src/core/libraries/np/np_error.h)) without writing the outputs, and a null status answers `INVALID_ARGUMENT`. The context id is not checked, as contexts are not tracked. The console's answer for an unknown context or connection is unverified - [sceNpSessionSignalingCreateJoinActivate, sceNpSessionSignalingGetConnectionFromNetAddress, sceNpSessionSignalingGetCustomProperty, sceNpSessionSignalingGetGroupFromPeerAddress, sceNpSessionSignalingInitializeWithPort, sceNpSessionSignalingJoinActivate and sceNpSessionSignalingSetCustomProperty](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSessionSignaling.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. - -- [sceNpSessionSignalingGetMemoryInfo, sceNpSessionSignalingGetConnectionFromPeerAddress, sceNpSessionSignalingGetGroupFromSessionId, sceNpSessionSignalingGetGroupInfo, sceNpSessionSignalingGetConnectionFromNetAddress2](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) - argument registers and scalar widths follow the system module exports `lbXTXRG5nyM`, `dJJ0UPrrsok`, `UGcXquhVYrY`, `WtOs6cUbrx8` and `hbNcE4fnBDs`. Pointer payloads remain opaque: their layouts and the address-type values require tracing the virtual callees. All five exports remain throwing stubs. From 39cfd3e2ad73d354c8968e4f27d339ef9aba0f4c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:37:52 +0300 Subject: [PATCH 168/523] fix(np): recover connection statistics output layout --- core/libs/prx/libSceNpSessionSignaling/Export.cpp | 13 ++++++++++++- docs/dev/debt/libSceNpSessionSignaling.md | 1 - 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index 4266f0fcf4..290ef13d06 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -21,6 +21,13 @@ struct SessionSignalingPeerAddress { std::uint32_t platform; }; +struct SessionSignalingConnectionStatistics { + std::uint32_t peakConnectionCount; + std::uint32_t connectionCount; + std::uint32_t connectingCount; + std::uint32_t connectedCount; +}; + struct SessionSignalingGroupInfo { std::uint32_t connectionIds[64]; std::uint64_t connectionCount; @@ -31,6 +38,10 @@ static_assert(offsetof(SessionSignalingMemoryInfo, usedSize) == 8); static_assert(offsetof(SessionSignalingMemoryInfo, peakUsedSize) == 16); static_assert(sizeof(SessionSignalingPeerAddress) == 16); static_assert(offsetof(SessionSignalingPeerAddress, platform) == 8); +static_assert(sizeof(SessionSignalingConnectionStatistics) == 16); +static_assert(offsetof(SessionSignalingConnectionStatistics, connectionCount) == 4); +static_assert(offsetof(SessionSignalingConnectionStatistics, connectingCount) == 8); +static_assert(offsetof(SessionSignalingConnectionStatistics, connectedCount) == 12); static_assert(sizeof(SessionSignalingGroupInfo) == 264); static_assert(offsetof(SessionSignalingGroupInfo, connectionCount) == 256); @@ -108,7 +119,7 @@ int APS5_VABI sceNpSessionSignalingGetMemoryInfo(SessionSignalingMemoryInfo* mem return 0; } -int APS5_VABI sceNpSessionSignalingGetConnectionStatistics(void) { +int APS5_VABI sceNpSessionSignalingGetConnectionStatistics(SessionSignalingConnectionStatistics* statistics) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNpSessionSignaling.md b/docs/dev/debt/libSceNpSessionSignaling.md index b797d16a65..80cb116b9a 100644 --- a/docs/dev/debt/libSceNpSessionSignaling.md +++ b/docs/dev/debt/libSceNpSessionSignaling.md @@ -2,6 +2,5 @@ ### Unknown function info -- [sceNpSessionSignalingGetConnectionStatistics](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - unknown signature - [sceNpSessionSignalingGetConnectionStatus](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - `(int32 context id, int32 connection id, int32* status, SceNetInAddr* peer address, uint16* peer port)` is ASTRO BOT's (PPSA21564 01.007.000) call at 0x30622B: the context id from a 32-bit field, the connection id, three stack outputs, a negative result taken as an error, then the address passed to `sceNetInetNtop(AF_INET, ...)` and the port to `sceNetNtohs`. It matches PS4's `sceNpSignalingGetConnectionStatus`. The title reaches it only for a connection id from the signaling library, which offline never hands one out. No session ever activates offline, so every connection id answers `CONN_NOT_FOUND` (0x8055330C, from [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/64d198190fa05d37cbb1fa29c50f967c376fce55/src/core/libraries/np/np_error.h)) without writing the outputs, and a null status answers `INVALID_ARGUMENT`. The context id is not checked, as contexts are not tracked. The console's answer for an unknown context or connection is unverified - [sceNpSessionSignalingCreateJoinActivate, sceNpSessionSignalingGetConnectionFromNetAddress, sceNpSessionSignalingGetCustomProperty, sceNpSessionSignalingGetGroupFromPeerAddress, sceNpSessionSignalingInitializeWithPort, sceNpSessionSignalingJoinActivate and sceNpSessionSignalingSetCustomProperty](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSessionSignaling.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From 35943b3e7b5e08242e1489a8bb98ba53dc5a5851 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:39:36 +0300 Subject: [PATCH 169/523] fix(http): recover cookie buffer and limit signatures --- core/libs/prx/libSceHttp/Export.cpp | 16 ++++++++-------- docs/dev/debt/libSceHttp.md | 8 -------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index a8d7d7eeec..24ae6e9d30 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -712,7 +712,7 @@ int APS5_VABI sceHttpGetEpollId(void) { return 0; } -int APS5_VABI sceHttpGetCookie(void) { +int APS5_VABI sceHttpGetCookie(int contextId, const char* url, char* cookie, std::size_t* requiredSize, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -722,7 +722,7 @@ int APS5_VABI sceHttpGetCookieEnabled(int id, int* enabled) { return 0; } -int APS5_VABI sceHttpAddCookie(void) { +int APS5_VABI sceHttpAddCookie(int contextId, const char* url, const char* cookie, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -732,32 +732,32 @@ int APS5_VABI sceHttpSetCookieSendCallback(void) { return 0; } -int APS5_VABI sceHttpCookieExport(void) { +int APS5_VABI sceHttpCookieExport(int contextId, void* buffer, std::size_t size, std::size_t* requiredSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCookieImport(void) { +int APS5_VABI sceHttpCookieImport(int contextId, const void* buffer, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpSetCookieTotalMaxSize(void) { +int APS5_VABI sceHttpSetCookieTotalMaxSize(int contextId, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpSetCookieMaxSize(void) { +int APS5_VABI sceHttpSetCookieMaxSize(int contextId, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpSetCookieMaxNum(void) { +int APS5_VABI sceHttpSetCookieMaxNum(int contextId, std::uint32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpSetCookieMaxNumPerDomain(void) { +int APS5_VABI sceHttpSetCookieMaxNumPerDomain(int contextId, std::uint32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index e743811644..3e1bf029fa 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -32,15 +32,7 @@ - [sceHttpDbgGetRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetCookie](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpAddCookie](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetCookieSendCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpCookieExport](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpCookieImport](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetCookieTotalMaxSize](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetCookieMaxSize](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetCookieMaxNum](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetCookieMaxNumPerDomain](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookieStats](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpRequestGetAllHeaders](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature From 2239c0b717a6a9311aff526fca780aaf11116e1c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:41:19 +0300 Subject: [PATCH 170/523] fix(http): recover request metadata and memory stats signatures --- core/libs/prx/libSceHttp/Export.cpp | 23 ++++++++++++++++++++--- docs/dev/debt/libSceHttp.md | 3 --- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 24ae6e9d30..b923302dac 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -31,6 +31,23 @@ struct RequestStatusRegistration { }; static std::mutex g_statusMutex; static std::map g_statusCallbacks; +struct HttpMemoryPoolStats { + std::size_t poolSize; + std::size_t peakUsedSize; + std::size_t usedSize; +}; + +struct HttpRequestHeader { + const char* name; + const char* value; +}; + +static_assert(sizeof(HttpMemoryPoolStats) == 24); +static_assert(offsetof(HttpMemoryPoolStats, peakUsedSize) == 8); +static_assert(offsetof(HttpMemoryPoolStats, usedSize) == 16); +static_assert(sizeof(HttpRequestHeader) == 16); +static_assert(offsetof(HttpRequestHeader, value) == 8); + struct HttpsCaList { void* certs; int certsNum; @@ -657,7 +674,7 @@ int APS5_VABI sceHttpDbgShowStat(void) { return 0; } -int APS5_VABI sceHttpGetMemoryPoolStats(void) { +int APS5_VABI sceHttpGetMemoryPoolStats(int contextId, HttpMemoryPoolStats* stats) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -777,7 +794,7 @@ int APS5_VABI sceHttpSetPolicyOption(int id, int policy) { return 0; } -int APS5_VABI sceHttpRequestGetAllHeaders(void) { +int APS5_VABI sceHttpRequestGetAllHeaders(int requestId, HttpRequestHeader* headers, std::uint32_t* headerCount, char* contentLength, std::size_t* contentLengthSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -803,7 +820,7 @@ int APS5_VABI sceHttpSetHttp09Enabled(int id, int enabled) { return 0; } -int APS5_VABI sceHttpGetRegisteredCtxIds(void) { +int APS5_VABI sceHttpGetRegisteredCtxIds(int contextId, int* netPoolId, int* sslContextId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 3e1bf029fa..d34df8365e 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -24,7 +24,6 @@ - [sceHttpDbgEnableProfile](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgSetPrintf](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgShowStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetMemoryPoolStats](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgShowConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgShowRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgShowMemoryPoolStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature @@ -34,9 +33,7 @@ - [sceHttpGetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetCookieSendCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetCookieStats](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpRequestGetAllHeaders](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetRegisteredCtxIds](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature ### Functional From 96c0eeb46b1033719ec84703bf7d3a38702080c1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:43:25 +0300 Subject: [PATCH 171/523] fix(keycode): recover keyboard data conversion signature --- core/libs/prx/libSceConvertKeycode/Export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceConvertKeycode/Export.cpp b/core/libs/prx/libSceConvertKeycode/Export.cpp index 9b9e92908f..6d6a3f0048 100644 --- a/core/libs/prx/libSceConvertKeycode/Export.cpp +++ b/core/libs/prx/libSceConvertKeycode/Export.cpp @@ -38,7 +38,7 @@ int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint64_t a0, std::uint64_t return kErrInvalidAddress; } -int APS5_VABI sceConvertKeycodeGetCharacterFromKeyboardData(void) { +int APS5_VABI sceConvertKeycodeGetCharacterFromKeyboardData(const KeyboardData* keyData, std::int32_t index, std::uint32_t state, std::uint32_t keyboardType, char16_t* character) { NotImplemented_nid_no_patch(__func__); return 0; } From 91c3ff3041a282e00e4264091dfec8f9ba5df674 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:45:15 +0300 Subject: [PATCH 172/523] fix(hmd): recover device setting and scalar query signatures --- core/libs/prx/libSceHmd/Export.cpp | 48 +++++++++++++++--------------- docs/dev/debt/libSceHmd.md | 24 --------------- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index ba66e13820..b1b86f3bcc 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -69,7 +69,7 @@ std::int32_t APS5_VABI sceHmdInternalMmapIsConnect() { return 0; } -int APS5_VABI sceHmdInternalGetPUVersion(void) { +int APS5_VABI sceHmdInternalGetPUVersion(std::uint32_t* version) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -79,7 +79,7 @@ int APS5_VABI sceHmdInternalEventInitialize(void) { return 0; } -int APS5_VABI sceHmdInternalSetVirtualDisplaySize(void) { +int APS5_VABI sceHmdInternalSetVirtualDisplaySize(std::uint8_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -89,7 +89,7 @@ int APS5_VABI sceHmdInternalSocialScreenSetOutput(void) { return 0; } -int APS5_VABI sceHmdInternalGetVirtualDisplayDepth(void) { +int APS5_VABI sceHmdInternalGetVirtualDisplayDepth(std::uint8_t* depth) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -109,12 +109,12 @@ int APS5_VABI sceHmdReprojectionUnsetCallback(void) { return 0; } -int APS5_VABI sceHmdInternalSetCrashReportCommand(void) { +int APS5_VABI sceHmdInternalSetCrashReportCommand(std::uint8_t command) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalGetDebugTextMode(void) { +int APS5_VABI sceHmdInternalGetDebugTextMode(std::uint8_t* mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -137,12 +137,12 @@ int APS5_VABI sceHmdReprojectionStartWideNear(void) { return 0; } -int APS5_VABI sceHmdInternalGetPUSerialNumber(void) { +int APS5_VABI sceHmdInternalGetPUSerialNumber(char* serialNumber) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalGetDebugSocialScreenMode(void) { +int APS5_VABI sceHmdInternalGetDebugSocialScreenMode(std::uint8_t* mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -182,7 +182,7 @@ int APS5_VABI sceHmdReprojectionSetCallback(void) { return 0; } -int APS5_VABI sceHmdInternalDfuCheckAfterPvt(void) { +int APS5_VABI sceHmdInternalDfuCheckAfterPvt(std::uint8_t* afterPvt) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -202,7 +202,7 @@ int APS5_VABI sceHmdDistortionSetOutputMinColor(void) { return 0; } -int APS5_VABI sceHmdInternalSetVirtualDisplayDepth(void) { +int APS5_VABI sceHmdInternalSetVirtualDisplayDepth(std::uint8_t depth) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -268,7 +268,7 @@ int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } -int APS5_VABI sceHmdInternalSetDebugSocialScreenMode(void) { +int APS5_VABI sceHmdInternalSetDebugSocialScreenMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -386,7 +386,7 @@ int APS5_VABI sceHmdReprojectionAddDisplayBuffer(void) { return 0; } -int APS5_VABI sceHmdInternalSetIpdSettingEnableForSystemService(void) { +int APS5_VABI sceHmdInternalSetIpdSettingEnableForSystemService(std::uint8_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -515,7 +515,7 @@ int APS5_VABI sceHmdInternalDfuStart(void) { return 0; } -int APS5_VABI sceHmdInternalSetVirtualDisplayHeight(void) { +int APS5_VABI sceHmdInternalSetVirtualDisplayHeight(std::uint8_t height) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -545,7 +545,7 @@ int APS5_VABI sceHmdInternalSeparateModeClose(void) { return 0; } -int APS5_VABI sceHmdInternalSetDebugTextMode(void) { +int APS5_VABI sceHmdInternalSetDebugTextMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -560,7 +560,7 @@ int APS5_VABI sceHmdInternalSetHmuPowerControlForDebug(void) { return 0; } -int APS5_VABI sceHmdInternalGetVirtualDisplayHeight(void) { +int APS5_VABI sceHmdInternalGetVirtualDisplayHeight(std::uint8_t* height) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -650,7 +650,7 @@ int APS5_VABI sceHmdInternalSetDeviceConnection(void) { return 0; } -int APS5_VABI sceHmdInternalGetVirtualDisplaySize(void) { +int APS5_VABI sceHmdInternalGetVirtualDisplaySize(std::uint8_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -694,7 +694,7 @@ int APS5_VABI sceHmdInternalCrashReportOpen(void) { return 0; } -int APS5_VABI sceHmdInternalSetDemoMode(void) { +int APS5_VABI sceHmdInternalSetDemoMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -719,12 +719,12 @@ int APS5_VABI sceHmdReprojectionClearUserEventStart(void) { return 0; } -int APS5_VABI sceHmdInternalSetUserType(void) { +int APS5_VABI sceHmdInternalSetUserType(std::uint8_t userType) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalGetDemoMode(void) { +int APS5_VABI sceHmdInternalGetDemoMode(std::uint8_t* mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -739,7 +739,7 @@ int APS5_VABI sceHmdInternalGetCrashDumpInfo(void) { return 0; } -int APS5_VABI sceHmdInternalGetTv4kCapability(void) { +int APS5_VABI sceHmdInternalGetTv4kCapability(std::uint8_t* capability) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -776,7 +776,7 @@ int APS5_VABI sceHmdInternal3dAudioClose(void) { return 0; } -int APS5_VABI sceHmdInternalSetBrightness(void) { +int APS5_VABI sceHmdInternalSetBrightness(std::uint8_t brightness) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -796,7 +796,7 @@ int APS5_VABI sceHmdInternalGetStatusReport(void) { return 0; } -int APS5_VABI sceHmdInternalGetBrightness(void) { +int APS5_VABI sceHmdInternalGetBrightness(std::uint8_t* brightness) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -831,12 +831,12 @@ int APS5_VABI sceHmdInternalAnotherScreenGetFadeState(void) { return 0; } -int APS5_VABI sceHmdInternalSetDebugGpo(void) { +int APS5_VABI sceHmdInternalSetDebugGpo(std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalGetIpdSettingEnableForSystemService(void) { +int APS5_VABI sceHmdInternalGetIpdSettingEnableForSystemService(std::uint8_t* enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -846,7 +846,7 @@ int APS5_VABI sceHmdReprojectionDebugGetLastInfoMultilayer(void) { return 0; } -int APS5_VABI sceHmdInternalGetPuRevision(void) { +int APS5_VABI sceHmdInternalGetPuRevision(std::uint32_t* revision) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index ee1012aafa..ce4cc7fc2b 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -7,20 +7,13 @@ ### Unknown function info - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count -- [sceHmdInternalGetPUVersion](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalEventInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetVirtualDisplaySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetOutput](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetVirtualDisplayDepth](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetEyeStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionUnsetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetCrashReportCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDebugTextMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalRegisterEventCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetPUSerialNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDebugSocialScreenMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetM2LedBrightness](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetIPD](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspect](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -28,11 +21,9 @@ - [sceHmdInternalAnotherScreenSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetRequiredPUPVersion](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuCheckAfterPvt](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetInertialSensor](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetUserEventStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionSetOutputMinColor](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetVirtualDisplayDepth](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -44,7 +35,6 @@ - [sceHmdInternalAnotherScreenSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetPuPositionParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitializeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDebugSocialScreenMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuPowerStatusForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetDisplayBuffers](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -66,7 +56,6 @@ - [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetM2LedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetIpdSettingEnableForSystemService](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetLedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioSendData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -90,16 +79,13 @@ - [sceHmdReprojectionFinalize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetVirtualDisplayHeight](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuSetMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetForcedCrash](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuCheckPartialUpdateAvailable](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDebugTextMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetHmuPowerControlForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetVirtualDisplayHeight](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVsh](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -117,7 +103,6 @@ - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDeviceConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetVirtualDisplaySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionUnsetDisplayBuffers](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuOpticalParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -125,34 +110,25 @@ - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetUserEventEnd](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDemoMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuSend](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionClearUserEventStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetUserType](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDemoMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMapSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetCrashDumpInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetTv4kCapability](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuReset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetCount](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetBrightness](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenGetFadeState](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetBrightness](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalizeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetPortStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStop](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetFadeState](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDebugGpo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetIpdSettingEnableForSystemService](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetPuRevision](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 0dd86fcff760af24434ac023112453b795d9223f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:46:27 +0300 Subject: [PATCH 173/523] fix(hmd): recover reprojection event and color signatures --- core/libs/prx/libSceHmd/Export.cpp | 8 ++++---- docs/dev/debt/libSceHmd.md | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index b1b86f3bcc..1d529744da 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -192,12 +192,12 @@ int APS5_VABI sceHmdInternalResetInertialSensor(void) { return 0; } -int APS5_VABI sceHmdReprojectionSetUserEventStart(void) { +int APS5_VABI sceHmdReprojectionSetUserEventStart(std::int64_t eventQueue, std::int32_t eventId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdDistortionSetOutputMinColor(void) { +int APS5_VABI sceHmdDistortionSetOutputMinColor(float red, float green, float blue) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -355,7 +355,7 @@ int APS5_VABI sceHmdInternalIsCommonDlgVr2d(void) { return 0; } -int APS5_VABI sceHmdReprojectionSetOutputMinColor(void) { +int APS5_VABI sceHmdReprojectionSetOutputMinColor(float red, float green, float blue) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -684,7 +684,7 @@ int APS5_VABI sceHmdReprojectionStartWithOverlay(void) { return 0; } -int APS5_VABI sceHmdReprojectionSetUserEventEnd(void) { +int APS5_VABI sceHmdReprojectionSetUserEventEnd(std::int64_t eventQueue, std::int32_t eventId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index ce4cc7fc2b..c6d6c7bb75 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -22,8 +22,6 @@ - [sceHmdInternalGetRequiredPUPVersion](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetInertialSensor](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionSetUserEventStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionSetOutputMinColor](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -51,7 +49,6 @@ - [sceHmdInternalGetDebugMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsCommonDlgVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionSetOutputMinColor](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetPortConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetM2LedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -108,7 +105,6 @@ - [sceHmdInternalSeparateModeSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVrTracker](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionSetUserEventEnd](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 419a34cadb409255ee13ae90f623acc2ba619aa9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:47:13 +0300 Subject: [PATCH 174/523] fix(netctl): recover callback and event stub signatures --- core/libs/prx/libSceNetCtl/Export.cpp | 28 +++++++++++++-------------- docs/dev/debt/libSceNetCtl.md | 14 -------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index c8e3080177..0170ff2ead 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -522,32 +522,32 @@ int APS5_VABI sceNetCtlUnregisterCallbackV6(int cid) { return UnregisterCallback(g_callbacksV6, cid); } -int APS5_VABI sceNetCtlRegisterCallbackForNpToolkit(void) { +int APS5_VABI sceNetCtlRegisterCallbackForNpToolkit(NetCtlCallback callback, void* argument, int* callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlRegisterCallbackIpcInt(void) { +int APS5_VABI sceNetCtlRegisterCallbackIpcInt(NetCtlCallback callback, void* argument, int* callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlRegisterCallbackForLibIpcInt(void) { +int APS5_VABI sceNetCtlRegisterCallbackForLibIpcInt(NetCtlCallback callback, void* argument, int* callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlUnregisterCallbackForNpToolkit(void) { +int APS5_VABI sceNetCtlUnregisterCallbackForNpToolkit(int callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlUnregisterCallbackIpcInt(void) { +int APS5_VABI sceNetCtlUnregisterCallbackIpcInt(int callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlUnregisterCallbackForLibIpcInt(void) { +int APS5_VABI sceNetCtlUnregisterCallbackForLibIpcInt(int callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -562,17 +562,17 @@ int APS5_VABI sceNetCtlCheckCallbackForLibIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlClearEventIpcInt(void) { +int APS5_VABI sceNetCtlClearEventIpcInt(int event) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlClearEventForNpToolkit(void) { +int APS5_VABI sceNetCtlClearEventForNpToolkit(int event) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlClearEventForLibIpcInt(void) { +int APS5_VABI sceNetCtlClearEventForLibIpcInt(int event) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -662,12 +662,12 @@ int APS5_VABI sceNetCtlSetErrorNotificationEnabledIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlRegisterCallbackV6IpcInt(void) { +int APS5_VABI sceNetCtlRegisterCallbackV6IpcInt(NetCtlCallback callback, void* argument, int* callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlUnregisterCallbackV6IpcInt(void) { +int APS5_VABI sceNetCtlUnregisterCallbackV6IpcInt(int callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -825,17 +825,17 @@ int APS5_VABI sceNetBweCheckCallbackIpcInt(void) { return 0; } -int APS5_VABI sceNetBweClearEventIpcInt(void) { +int APS5_VABI sceNetBweClearEventIpcInt(int event) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetBweRegisterCallbackIpcInt(void) { +int APS5_VABI sceNetBweRegisterCallbackIpcInt(NetCtlCallback callback, void* argument, int* callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetBweUnregisterCallbackIpcInt(void) { +int APS5_VABI sceNetBweUnregisterCallbackIpcInt(int callbackId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index f9fb92ef5c..0c6fe50b9f 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -8,17 +8,8 @@ - [sceNetCtlGetInfoV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `Jy1EO5GdlcM`) - imported by PPSA08587. `(int code, SceNetCtlInfoV6* info)` with codes 1-5 (IP address, link-local address, default route, primary and secondary DNS) comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero), whose source is unknown; shadPS4 and fpPS4 only stub it. A null `info` answers `INVALID_ADDR`, a code outside 1-5 `INVALID_CODE`, and a host without an IPv6 address `NOT_CONNECTED`, as `sceNetCtlGetStateV6` reports it. The layout of each answer is unverified, so every code throws on a host with an IPv6 address - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - answers from the host interface that holds an IPv4 address, preferring the one that carries the default route: its address, netmask, gateway, MTU and first two IPv4 DNS servers. The device is reported as wired, the link as connected, the IP configuration as DHCP and the HTTP proxy as off, whatever the host uses, and the Ethernet address is the one `sceNetGetMacAddress` reports. A gateway or DNS server the interface does not have is reported as `0.0.0.0`; what a console reports then is unknown. The DHCP host name (12), the PPPoE user name (13), the HTTP proxy server (20) and the Wi-Fi BSSID and SSID (5, 6) are empty strings, as that configuration sets none of them; the remaining Wi-Fi codes (7-10) and the HTTP proxy port (21) report 0; this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/network.cpp) and is not measured on hardware -- [sceNetCtlRegisterCallbackForNpToolkit](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlRegisterCallbackIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlRegisterCallbackForLibIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlUnregisterCallbackForNpToolkit](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlUnregisterCallbackIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlUnregisterCallbackForLibIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlCheckCallbackForNpToolkit](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlCheckCallbackForLibIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlClearEventIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlClearEventForNpToolkit](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlClearEventForLibIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetResultIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetStateIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetState2IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -36,8 +27,6 @@ - [sceNetCtlGetScanInfoBssidForSsidListScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetScanInfoForSsidListScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlSetErrorNotificationEnabledIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlRegisterCallbackV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlUnregisterCallbackV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetResultV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `H5yARg37U5g`) - `(int eventType, int* errorCode)` comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/NetCtl.cs#L246-L251), whose source is unknown; shadPS4 only stubs it. Callbacks registered with `sceNetCtlRegisterCallbackV6` receive no event, so event types 1-3 write 0 to `errorCode`; what a console writes before the first event is unmeasured. A null `errorCode` answers `INVALID_ADDR`, as `sceNetCtlGetResult` does, and other event types throw, as their error code on a console is unknown - [sceNetCtlGetResultV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetStateV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -70,9 +59,6 @@ - [sceNetCtlApAppInitWpaKeyForQa](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRestart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweCheckCallbackIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweClearEventIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweRegisterCallbackIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweUnregisterCallbackIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweStartInternetConnectionTestIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweFinishInternetConnectionTestIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature From e5a43c86bca6b8aa67a1894f21349e0ff8d0ae4b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:47:46 +0300 Subject: [PATCH 175/523] fix(netctl): recover state and result query signatures --- core/libs/prx/libSceNetCtl/Export.cpp | 18 +++++++++--------- docs/dev/debt/libSceNetCtl.md | 9 --------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index 0170ff2ead..1819d066c5 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -577,17 +577,17 @@ int APS5_VABI sceNetCtlClearEventForLibIpcInt(int event) { return 0; } -int APS5_VABI sceNetCtlGetResultIpcInt(void) { +int APS5_VABI sceNetCtlGetResultIpcInt(int event, int* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetStateIpcInt(void) { +int APS5_VABI sceNetCtlGetStateIpcInt(int* state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetState2IpcInt(void) { +int APS5_VABI sceNetCtlGetState2IpcInt(int* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -597,12 +597,12 @@ int APS5_VABI sceNetCtlGetInfoIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlGetEtherLinkMode(void) { +int APS5_VABI sceNetCtlGetEtherLinkMode(int* mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetWifiType(void) { +int APS5_VABI sceNetCtlGetWifiType(int* type) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -657,7 +657,7 @@ int APS5_VABI sceNetCtlGetScanInfoForSsidListScanIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlSetErrorNotificationEnabledIpcInt(void) { +int APS5_VABI sceNetCtlSetErrorNotificationEnabledIpcInt(int enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -681,12 +681,12 @@ int APS5_VABI sceNetCtlGetResultV6(int event_type, int* error_code) { return 0; } -int APS5_VABI sceNetCtlGetResultV6IpcInt(void) { +int APS5_VABI sceNetCtlGetResultV6IpcInt(int event, int* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetStateV6IpcInt(void) { +int APS5_VABI sceNetCtlGetStateV6IpcInt(int* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -736,7 +736,7 @@ int APS5_VABI sceNetCtlGetNetEvConfigInfoIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlGetConnectionElapsedTimeIpcInt(void) { +int APS5_VABI sceNetCtlGetConnectionElapsedTimeIpcInt(std::uint64_t* elapsedTime) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 0c6fe50b9f..1c0958c89b 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -10,12 +10,7 @@ - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - answers from the host interface that holds an IPv4 address, preferring the one that carries the default route: its address, netmask, gateway, MTU and first two IPv4 DNS servers. The device is reported as wired, the link as connected, the IP configuration as DHCP and the HTTP proxy as off, whatever the host uses, and the Ethernet address is the one `sceNetGetMacAddress` reports. A gateway or DNS server the interface does not have is reported as `0.0.0.0`; what a console reports then is unknown. The DHCP host name (12), the PPPoE user name (13), the HTTP proxy server (20) and the Wi-Fi BSSID and SSID (5, 6) are empty strings, as that configuration sets none of them; the remaining Wi-Fi codes (7-10) and the HTTP proxy port (21) report 0; this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/network.cpp) and is not measured on hardware - [sceNetCtlCheckCallbackForNpToolkit](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlCheckCallbackForLibIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetResultIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetStateIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetState2IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetEtherLinkMode](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetWifiType](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlConnectIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlConnectWithRetryIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlConnectConfIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -26,10 +21,7 @@ - [sceNetCtlGetScanInfoForSsidScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetScanInfoBssidForSsidListScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetScanInfoForSsidListScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlSetErrorNotificationEnabledIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetResultV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `H5yARg37U5g`) - `(int eventType, int* errorCode)` comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/NetCtl.cs#L246-L251), whose source is unknown; shadPS4 only stubs it. Callbacks registered with `sceNetCtlRegisterCallbackV6` receive no event, so event types 1-3 write 0 to `errorCode`; what a console writes before the first event is unmeasured. A null `errorCode` answers `INVALID_ADDR`, as `sceNetCtlGetResult` does, and other event types throw, as their error code on a console is unknown -- [sceNetCtlGetResultV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetStateV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetInfoV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetNatInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlSetStunWithPaddingFlagIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -39,7 +31,6 @@ - [sceNetCtlEnableBandwidthManagementIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetBandwidthInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetNetEvConfigInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetConnectionElapsedTimeIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpClearEvent](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpRegisterCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature From 9ccfc42192df3c27d969ddfe4a7759086f758880 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:51:16 +0300 Subject: [PATCH 176/523] fix(net): recover socket and interface control signatures --- core/libs/prx/libSceNet/Export.cpp | 46 +++++++++++++++--------------- docs/dev/debt/libSceNet.md | 23 --------------- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index d6a9bd0b15..d3fecf8937 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2207,7 +2207,7 @@ int APS5_VABI sceNetGetRouteInfo(void) { return 0; } -int APS5_VABI sceNetGetsockoptEx(void) { +int APS5_VABI sceNetGetsockoptEx(int socketId, int level, int option, void* value, std::uint32_t* valueSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2243,7 +2243,7 @@ int APS5_VABI sceNetInetPtonEx(int af, const char* src, void* dst, int) { return 1; } -int APS5_VABI sceNetIoctl(void) { +int APS5_VABI sceNetIoctl(int socketId, std::uint32_t command, void* argument) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2274,12 +2274,12 @@ int APS5_VABI sceNetSetDnsInfoToKernel(void) { return 0; } -int APS5_VABI sceNetSetsockoptEx(void) { +int APS5_VABI sceNetSetsockoptEx(int socketId, int level, int option, const void* value, std::uint32_t valueSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetSocketInternal(void) { +int APS5_VABI sceNetSocketInternal(const char* name, int domain, int type, int protocol) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2309,7 +2309,7 @@ int APS5_VABI sceNetSyncWait(void) { return 0; } -int APS5_VABI sceNetSysctl(void) { +int APS5_VABI sceNetSysctl(const int* name, std::uint32_t nameLength, void* oldValue, std::size_t* oldSize, const void* newValue, std::size_t newSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2384,27 +2384,27 @@ int APS5_VABI sceNetShowRoute6ForBuffer(void) { return 0; } -int APS5_VABI sceNetConfigGetIfFlags(void) { +int APS5_VABI sceNetConfigGetIfFlags(std::uint32_t interfaceId, std::uint32_t* flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigSetIfFlags(void) { +int APS5_VABI sceNetConfigSetIfFlags(std::uint32_t interfaceId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigUnsetIfFlags(void) { +int APS5_VABI sceNetConfigUnsetIfFlags(std::uint32_t interfaceId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigDelIfaddr(void) { +int APS5_VABI sceNetConfigDelIfaddr(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigDownInterface(void) { +int APS5_VABI sceNetConfigDownInterface(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2429,12 +2429,12 @@ int APS5_VABI sceNetConfigSetIfLinkLocalAddr6(void) { return 0; } -int APS5_VABI sceNetConfigUpInterface(void) { +int APS5_VABI sceNetConfigUpInterface(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigUpInterfaceWithFlags(void) { +int APS5_VABI sceNetConfigUpInterfaceWithFlags(std::uint32_t interfaceId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2464,7 +2464,7 @@ int APS5_VABI sceNetConfigGetIfaddr(void) { return 0; } -int APS5_VABI sceNetConfigSetIfmtu(void) { +int APS5_VABI sceNetConfigSetIfmtu(std::uint32_t interfaceId, int mtu) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2549,7 +2549,7 @@ int APS5_VABI sceNetDhcpStart(void) { return 0; } -int APS5_VABI sceNetDhcpStop(void) { +int APS5_VABI sceNetDhcpStop(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2609,7 +2609,7 @@ int APS5_VABI sceNetAddrConfig6Start(void) { return 0; } -int APS5_VABI sceNetAddrConfig6Stop(void) { +int APS5_VABI sceNetAddrConfig6Stop(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2634,7 +2634,7 @@ int APS5_VABI sceNetDhcpdStart(void) { return 0; } -int APS5_VABI sceNetDhcpdStop(void) { +int APS5_VABI sceNetDhcpdStop(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2644,7 +2644,7 @@ int APS5_VABI sceNetDuplicateIpStart(void) { return 0; } -int APS5_VABI sceNetDuplicateIpStop(void) { +int APS5_VABI sceNetDuplicateIpStop(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2669,7 +2669,7 @@ int APS5_VABI sceNetConfigWlanAdhocJoin(void) { return 0; } -int APS5_VABI sceNetConfigWlanAdhocLeave(void) { +int APS5_VABI sceNetConfigWlanAdhocLeave(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2709,7 +2709,7 @@ int APS5_VABI sceNetConfigWlanApStart(void) { return 0; } -int APS5_VABI sceNetConfigWlanApStop(void) { +int APS5_VABI sceNetConfigWlanApStop(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2724,7 +2724,7 @@ int APS5_VABI sceNetConfigWlanBackgroundScanStart(void) { return 0; } -int APS5_VABI sceNetConfigWlanBackgroundScanStop(void) { +int APS5_VABI sceNetConfigWlanBackgroundScanStop(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2754,7 +2754,7 @@ int APS5_VABI sceNetConfigWlanInfraGetRssiInfo(void) { return 0; } -int APS5_VABI sceNetConfigWlanInfraLeave(void) { +int APS5_VABI sceNetConfigWlanInfraLeave(int interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2774,7 +2774,7 @@ int APS5_VABI sceNetConfigWlanSetDeviceConfig(void) { return 0; } -int APS5_VABI sceNetConfigEtherGetLinkMode(void) { +int APS5_VABI sceNetConfigEtherGetLinkMode(std::uint32_t interfaceId, int* mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2784,7 +2784,7 @@ int APS5_VABI sceNetConfigEtherPostPlugInOutEvent(void) { return 0; } -int APS5_VABI sceNetConfigEtherSetLinkMode(void) { +int APS5_VABI sceNetConfigEtherSetLinkMode(std::uint32_t interfaceId, int mode) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index fa35cc1780..eb9de70d66 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -43,23 +43,18 @@ - [sceNetGetNameToIndex](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t* output)` signature and the `EINVAL` failure for a null output follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the value comes from the host's `std::random_device`, as `kern.rng_pseudo` in libkernel does. Unverified on hardware - [sceNetGetRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetsockoptEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetSystemTime](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unverified on PS5 hardware: the `void(uint64_t*)` signature, process-relative microseconds and ignored null output follow [shadPS4 net.cpp](https://github.com/shadps4-emu/shadPS4/blob/d615b3887ba29025a59242e1a26f5b68bb176158/src/core/libraries/net/net.cpp#L1654-L1659). The clock is the same as `sceKernelGetProcessTime`, including `APS5_TIME_SCALE` - [sceNetGetSystemMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInitParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. -- [sceNetIoctl](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetMemoryAllocate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int64_t size, int flags)` signature, flag 2 as zero fill and the null result for a size of 0 or less follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp). The block comes from the guest heap, and an allocation failure returns null without setting an errno. Any other flag bit throws, since its meaning is not known. Unverified on hardware - [sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(void* memory)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the block returns to the guest heap and a null pointer is ignored. Unverified on hardware - [sceNetSetDnsInfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSetsockoptEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSocketInternal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSyncCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSyncDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSyncGet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSyncSignal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSyncWait](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSysctl](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -74,23 +69,15 @@ - [sceNetGetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDns6InfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetShowRoute6ForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigGetIfFlags](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetIfFlags](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigUnsetIfFlags](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDownInterface](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetDefaultRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetDefaultRouteDev](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetIfLinkLocalAddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigUpInterface](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigUpInterfaceWithFlags](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigFlushRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigFlushRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetIfmtu](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddArp](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddArpWithInterface](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddMRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -107,7 +94,6 @@ - [sceNetDhcpGetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetInfoEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDhcpStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigDelIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigDelDefaultRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -119,19 +105,15 @@ - [sceNetConfigDelRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetAddrConfig6Stop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6GetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDhcpdStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDuplicateIpStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocClearWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocGetWakeOnWlanInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocLeave](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocPspEmuClearWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocPspEmuGetWakeOnWlanInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocPspEmuSetWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -139,22 +121,17 @@ - [sceNetConfigWlanAdhocSetExtInfoElement](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocSetWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanApStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanApStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanBackgroundScanQuery](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanBackgroundScanStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanBackgroundScanStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanDiagGetDeviceInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanDiagSetAntenna](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanDiagSetTxFixedRate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanGetDeviceConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanInfraGetRssiInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanInfraLeave](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanInfraScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanScan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanSetDeviceConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigEtherGetLinkMode](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigEtherPostPlugInOutEvent](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigEtherSetLinkMode](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetBandwidthControlSetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 45997cbfe91cbb60b9bcf95ff476d02964958fa6 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:54:50 +0300 Subject: [PATCH 177/523] fix(net): recover address and route configuration signatures --- core/libs/prx/libSceNet/Export.cpp | 44 +++++++++++++++++------------- docs/dev/debt/libSceNet.md | 19 ------------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index d3fecf8937..029f0b6255 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -46,6 +46,12 @@ // Guest socket descriptors retain PS5 semantics while their transport is backed by host sockets. +struct NetIpv6Address { + std::uint8_t bytes[16]; +}; + +static_assert(sizeof(NetIpv6Address) == 16); + namespace { // FreeBSD errno numbers as reported through sceNetErrnoLoc (SCE_NET_ERROR_* is 0x80410100 + errno). constexpr int NET_ENOENT = 2; @@ -2409,17 +2415,17 @@ int APS5_VABI sceNetConfigDownInterface(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigSetDefaultRoute(void) { +int APS5_VABI sceNetConfigSetDefaultRoute(const std::uint32_t* gateway) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigSetDefaultRouteDev(void) { +int APS5_VABI sceNetConfigSetDefaultRouteDev(const std::uint32_t* gateway) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigSetIfaddr(void) { +int APS5_VABI sceNetConfigSetIfaddr(int interfaceId, const std::uint32_t* address, const std::uint32_t* broadcastAddress, const std::uint32_t* netmask) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2469,37 +2475,37 @@ int APS5_VABI sceNetConfigSetIfmtu(std::uint32_t interfaceId, int mtu) { return 0; } -int APS5_VABI sceNetConfigAddArp(void) { +int APS5_VABI sceNetConfigAddArp(const NetEtherAddr* etherAddress, const std::uint32_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigAddArpWithInterface(void) { +int APS5_VABI sceNetConfigAddArpWithInterface(int interfaceId, const NetEtherAddr* etherAddress, const std::uint32_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigAddMRoute(void) { +int APS5_VABI sceNetConfigAddMRoute(const std::uint32_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigAddRoute(void) { +int APS5_VABI sceNetConfigAddRoute(const std::uint32_t* destination, const std::uint32_t* gateway, const std::uint32_t* netmask, int hostRoute) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigAddRouteWithInterface(void) { +int APS5_VABI sceNetConfigAddRouteWithInterface(int interfaceId, const std::uint32_t* destination, const std::uint32_t* gateway, const std::uint32_t* netmask, int hostRoute) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigDelArp(void) { +int APS5_VABI sceNetConfigDelArp(const NetEtherAddr* etherAddress, const std::uint32_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigDelArpWithInterface(void) { +int APS5_VABI sceNetConfigDelArpWithInterface(int interfaceId, const NetEtherAddr* etherAddress, const std::uint32_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2514,17 +2520,17 @@ int APS5_VABI sceNetConfigDelDefaultRouteDev(void) { return 0; } -int APS5_VABI sceNetConfigDelMRoute(void) { +int APS5_VABI sceNetConfigDelMRoute(const std::uint32_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigDelRoute(void) { +int APS5_VABI sceNetConfigDelRoute(const std::uint32_t* destination, const std::uint32_t* gateway, const std::uint32_t* netmask) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigGetDefaultRoute(void) { +int APS5_VABI sceNetConfigGetDefaultRoute(std::uint32_t* gateway) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2559,7 +2565,7 @@ int APS5_VABI sceNetConfigAddIfaddr6(void) { return 0; } -int APS5_VABI sceNetConfigDelIfaddr6(void) { +int APS5_VABI sceNetConfigDelIfaddr6(int interfaceId, const NetIpv6Address* address) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2569,7 +2575,7 @@ int APS5_VABI sceNetConfigDelDefaultRoute6(void) { return 0; } -int APS5_VABI sceNetConfigSetIfaddr6(void) { +int APS5_VABI sceNetConfigSetIfaddr6(int interfaceId, const NetIpv6Address* address, std::uint8_t prefixLength) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2579,7 +2585,7 @@ int APS5_VABI sceNetConfigSetIfaddr6WithFlags(void) { return 0; } -int APS5_VABI sceNetConfigSetDefaultRoute6(void) { +int APS5_VABI sceNetConfigSetDefaultRoute6(int interfaceId, const NetIpv6Address* gateway) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2589,17 +2595,17 @@ int APS5_VABI sceNetConfigGetIfaddr6(void) { return 0; } -int APS5_VABI sceNetConfigGetDefaultRoute6(void) { +int APS5_VABI sceNetConfigGetDefaultRoute6(NetIpv6Address* gateway) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigDelRoute6(void) { +int APS5_VABI sceNetConfigDelRoute6(const NetIpv6Address* destination, std::uint8_t prefixLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigAddRoute6(void) { +int APS5_VABI sceNetConfigAddRoute6(const NetIpv6Address* destination, const NetIpv6Address* gateway, std::uint8_t prefixLength) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index eb9de70d66..5894d4eb0e 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -69,41 +69,22 @@ - [sceNetGetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDns6InfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetShowRoute6ForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetDefaultRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetDefaultRouteDev](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetIfLinkLocalAddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigFlushRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigFlushRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddArp](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddArpWithInterface](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddMRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddRouteWithInterface](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelArp](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelArpWithInterface](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigDelDefaultRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigDelDefaultRouteDev](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelMRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigGetDefaultRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetAutoipInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetInfoEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigDelDefaultRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetIfaddr6WithFlags](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetDefaultRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigGetDefaultRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6GetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 6d2aebd4698138cbd9fa89d080b594895e22f200 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:56:01 +0300 Subject: [PATCH 178/523] fix(json): restore the default special float format --- core/libs/prx/libSceJson2/Export.cpp | 1 + core/libs/tests/GuestJson.cpp | 2 +- docs/dev/debt/libSceJson2.md | 6 +----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index ff9da15cec..be2706ad6a 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -981,6 +981,7 @@ static_assert(sizeof(InitParameter2) == 0x28 && offsetof(InitParameter2, special void APS5_VABI _ZN3sce4Json14InitParameter2C1Ev(InitParameter2* self) { *self = {}; + self->specialFloatFormatType = 2; } void APS5_VABI _ZN3sce4Json14InitParameter2C2Ev(InitParameter2* self) { diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index f6569900e7..9bbb99f743 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -877,7 +877,7 @@ static void InitParameters() { construct(parameter); StoredInitParameter2 stored = Stored(parameter); Require(stored.allocator == nullptr && stored.userData == nullptr && stored.fileBufferSize == 0); - Require(stored.specialFloatFormatType == 0); + Require(stored.specialFloatFormatType == 2); Require(stored.reserved[0] == 0 && stored.reserved[1] == 0 && stored.reserved[2] == 0); int allocParam = 0; diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 015e476dac..3877f521fc 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -4,14 +4,10 @@ - [sce::Json::Parser::parse with a read callback](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the callback's contract is taken from PPSA04540's: it returns 0 with the next character, and at the end of its data writes `'\0'` and returns -1. Characters are read until the callback returns non-zero or gives `'\0'`, and the text read is then parsed as by the `(text, size)` overload, so trailing characters after the value are rejected as there. Whether the console stops reading at the end of the top-level value, and what it returns when the callback fails before the end, are unknown - [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer or zero size returns `JsonErrorParse` (0x80920101) without modifying the destination Value; whether the console distinguishes a null pointer from a syntax error with a separate parameter error code is unknown. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown -- [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - layouts inferred, not known. `InitParameter2` is taken as the 40-byte object of [SharpEmu](https://github.com/sharpemu/sharpemu/blob/1811cef94435db7655209fb39f65ddf258b93fd6/src/SharpEmu.Libs/Json/JsonExports.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/eda6ba37ece127ab0396378404d99182e3e8a530/src/libs/libJson2.cpp): the allocator, its user data, the file buffer size, the 32-bit special float format type at 0x18 and 12 bytes both zero, and both constructors zero all of it. `InitParameterRtti2::setAllocatorRtti` writes the allocation parameters and user data where `setAllocator` writes the allocator, as both references do; whether the console also marks the object as using RTTI allocation is unknown. `InitParameterRtti`'s constructor writes the allocation parameters, user data and file buffer size to the first 24 bytes, an order inferred from its mangled arguments. The values of `SpecialFloatFormatType` and what they select are unknown, so `setSpecialFloatFormatType` stores any value and nothing reads it: `Initializer::initialize(InitParameter2)` ignores every field as `initialize(InitParameter)` does, serializing a non-finite real throws whatever the type, and whether the type lets the parser accept NaN or infinity is unknown. The setters return nothing; whether the console's return `this` is unknown +- [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - `InitParameterRtti` and RTTI allocation remain unverified. `InitParameterRtti2::setAllocatorRtti` writes the allocation parameters and user data where `setAllocator` writes the allocator, as both references do; whether the console also marks the object as using RTTI allocation is unknown. `InitParameterRtti`'s constructor writes the allocation parameters, user data and file buffer size to the first 24 bytes, an order inferred from its mangled arguments. The values of `SpecialFloatFormatType` and what they select are unknown, so `setSpecialFloatFormatType` stores any value and nothing reads it: `Initializer::initialize(InitParameter2)` ignores every field as `initialize(InitParameter)` does, serializing a non-finite real throws whatever the type, and whether the type lets the parser accept NaN or infinity is unknown. The setters return nothing; whether the console's return `this` is unknown - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown - [sce::Json::String size, capacity, resize, compare and operator==(const String&), append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - member of `Value` returning `bool` as a conversion operator, with no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: evaluates to `false` for `TypeNull` and boolean `false`, and `true` for boolean `true`. For all other value types it currently returns `true`; whether the console converts them, throws, or checks container emptiness is unverified - [sce::Json::Object::insert](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - signatures and behaviors have no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: `insert` returns an iterator through a result pointer before `this` as `Object::begin` does, and takes a const Pair reference. If the key exists, it updates the existing value and returns an iterator to it; otherwise it appends a deep copy of the pair and returns an iterator to the new element; whether the console updates existing keys, appends duplicates, or ignores duplicate insertion is unverified. - -### Functional - -- [sce::Json::InitParameter2 base-object constructor](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - forwards to the complete-object constructor and uses its inferred layout and default field values; constructor equivalence and initialization bytes remain unverified on the console From fc3db1540261f91aa0c2925bfee8c01ac76f65f0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:56:54 +0300 Subject: [PATCH 179/523] fix(json): preserve existing values on duplicate insertion --- core/libs/prx/libSceJson2/Export.cpp | 1 - core/libs/tests/GuestJson.cpp | 8 ++++++++ docs/dev/debt/libSceJson2.md | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index be2706ad6a..9ca8b9d125 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -1083,7 +1083,6 @@ void APS5_VABI _ZN3sce4Json6Object4PairD1Ev(Pair* self) { ObjectIterator* APS5_VABI _ZN3sce4Json6Object6insertERKNS1_4PairE(ObjectIterator* result, Object* self, const Pair* pair) { auto it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& p) { return *p.key.text == *pair->key.text; }); if (it != self->items->end()) { - AssignNode(NodeOf(it->value), NodeOf(pair->value)); result->it = it; return result; } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index 9bbb99f743..c29cbc39b3 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -552,6 +552,14 @@ static void ObjectInsert() { Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(&p->key), "score") == 0); Require(*_ZNK3sce4Json5Value10getIntegerEv(&p->value) == 100); + Require(_ZN3sce4Json5Value3setEl(&pair.value, 200) == 0); + Iterator duplicate{}; + Require(_ZN3sce4Json6Object6insertERKNS1_4PairE(&duplicate, &object, &pair) == &duplicate); + Require(_ZNK3sce4Json6Object8iteratoreqERKS2_(&duplicate, &inserted)); + Require(_ZNK3sce4Json6Object4sizeEv(&object) == 1); + Require(*_ZNK3sce4Json5Value10getIntegerEv(&_ZNK3sce4Json6Object8iteratordeEv(&duplicate)->value) == 100); + _ZN3sce4Json6Object8iteratorD1Ev(&duplicate); + String otherKey{}; _ZN3sce4Json6StringC1EPKc(&otherKey, "other"); Value otherValue{}; diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 3877f521fc..6db5c83e5b 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -10,4 +10,4 @@ - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown - [sce::Json::String size, capacity, resize, compare and operator==(const String&), append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - member of `Value` returning `bool` as a conversion operator, with no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: evaluates to `false` for `TypeNull` and boolean `false`, and `true` for boolean `true`. For all other value types it currently returns `true`; whether the console converts them, throws, or checks container emptiness is unverified -- [sce::Json::Object::insert](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - signatures and behaviors have no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: `insert` returns an iterator through a result pointer before `this` as `Object::begin` does, and takes a const Pair reference. If the key exists, it updates the existing value and returns an iterator to it; otherwise it appends a deep copy of the pair and returns an iterator to the new element; whether the console updates existing keys, appends duplicates, or ignores duplicate insertion is unverified. +- [sce::Json::Object::insert](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - new keys are appended in insertion order; the system library uses key order. Duplicate insertion preserves the existing value. From 2b94ddfa7e43db56e8dc65239baf015ab4982ad2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 11:58:03 +0300 Subject: [PATCH 180/523] fix(net): recover ipv6 interface configuration signatures --- core/libs/prx/libSceNet/Export.cpp | 14 +++++++++++--- docs/dev/debt/libSceNet.md | 6 ------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 029f0b6255..7cddc02c41 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -52,6 +52,14 @@ struct NetIpv6Address { static_assert(sizeof(NetIpv6Address) == 16); +struct NetIpv6InterfaceAddress { + NetIpv6Address address; + std::uint8_t prefixLength; + std::uint8_t reserved[3]; +}; + +static_assert(sizeof(NetIpv6InterfaceAddress) == 20); + namespace { // FreeBSD errno numbers as reported through sceNetErrnoLoc (SCE_NET_ERROR_* is 0x80410100 + errno). constexpr int NET_ENOENT = 2; @@ -2560,7 +2568,7 @@ int APS5_VABI sceNetDhcpStop(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigAddIfaddr6(void) { +int APS5_VABI sceNetConfigAddIfaddr6(int interfaceId, const NetIpv6Address* address, std::uint8_t prefixLength) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2580,7 +2588,7 @@ int APS5_VABI sceNetConfigSetIfaddr6(int interfaceId, const NetIpv6Address* addr return 0; } -int APS5_VABI sceNetConfigSetIfaddr6WithFlags(void) { +int APS5_VABI sceNetConfigSetIfaddr6WithFlags(int interfaceId, const NetIpv6Address* address, std::uint8_t prefixLength, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2590,7 +2598,7 @@ int APS5_VABI sceNetConfigSetDefaultRoute6(int interfaceId, const NetIpv6Address return 0; } -int APS5_VABI sceNetConfigGetIfaddr6(void) { +int APS5_VABI sceNetConfigGetIfaddr6(int interfaceId, NetIpv6InterfaceAddress* addresses, std::uint32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 5894d4eb0e..93f860806d 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -75,16 +75,10 @@ - [sceNetConfigFlushRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelDefaultRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelDefaultRouteDev](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetAutoipInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetInfoEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigDelDefaultRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetIfaddr6WithFlags](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigGetIfaddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6GetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 7b1451b99be7f5ea2f285c2f9253db4460fea38a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:00:02 +0300 Subject: [PATCH 181/523] fix(ajm): recover vorbis packet parameter layouts --- core/libs/prx/libSceAjm.native/Export.cpp | 35 +++++++++++++++++++++-- docs/dev/debt/libSceAjm.native.md | 1 - 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAjm.native/Export.cpp b/core/libs/prx/libSceAjm.native/Export.cpp index 2df65a236d..2a7d6a4f1f 100644 --- a/core/libs/prx/libSceAjm.native/Export.cpp +++ b/core/libs/prx/libSceAjm.native/Export.cpp @@ -12,6 +12,37 @@ static void AjmStub(const char* name) { NotImplemented_nid_no_patch(name); } +struct WVorbisHeaderPacketParameter { + std::size_t size; + std::int32_t channels; + std::uint32_t sampleRate; + std::int32_t bytesPerSecond; + std::int32_t shortBlockExponent; + std::int32_t longBlockExponent; + std::uint32_t reserved; + const void* setupPacket; + std::size_t setupPacketSize; + void* output; + std::size_t outputCapacity; + std::size_t outputSize; +}; + +struct WVorbisSetupPacketParameter { + std::size_t size; + std::int32_t channels; + std::uint32_t reserved; + const void* input; + std::size_t inputSize; + void* output; + std::size_t outputCapacity; + std::size_t outputSize; +}; + +static_assert(sizeof(WVorbisHeaderPacketParameter) == 72); +static_assert(offsetof(WVorbisHeaderPacketParameter, setupPacket) == 32); +static_assert(sizeof(WVorbisSetupPacketParameter) == 56); +static_assert(offsetof(WVorbisSetupPacketParameter, input) == 16); + extern "C" { int APS5_VABI sceAjmBatchJobEncode(AjmBatchInfo* info, uint32_t instance, const void* pcm_input, size_t pcm_input_size, void* bitstream_output, size_t bitstream_output_size, void* result) { @@ -32,12 +63,12 @@ const char* APS5_VABI sceAjmStrError(int error) { return nullptr; } -int APS5_VABI sceAjmDecWVorbisCreateHeaderPacket(void) { +int APS5_VABI sceAjmDecWVorbisCreateHeaderPacket(WVorbisHeaderPacketParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAjmDecWVorbisCreateSetupPacket(void) { +int APS5_VABI sceAjmDecWVorbisCreateSetupPacket(WVorbisSetupPacketParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAjm.native.md b/docs/dev/debt/libSceAjm.native.md index f8982027f0..2f7798c679 100644 --- a/docs/dev/debt/libSceAjm.native.md +++ b/docs/dev/debt/libSceAjm.native.md @@ -6,7 +6,6 @@ - [sceAjmInstanceExtend, sceAjmInstanceSwitch](../../../core/libs/prx/libSceAjm.native/Export.cpp) (libSceAjm.native) - their signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/ajm/ajm.h#L19-L20) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/Ajm.cs#L100-L104), not measured on a console; these throwing declarations only resolve imports. - [sceAjmInstanceCreate](../../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - a full 14-bit instance slot namespace returns `0x80930007` (`OUT_OF_RESOURCES`), as [shadPS4's InstanceCreate](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/ajm/ajm_context.cpp) does for its own instance pool (PS4). The 16384 slots per codec prefix are this runtime's existing ID layout, not a measured console capacity; the PS5 capacity and exhaustion result are unverified. - [ATRAC9 vibration decoding](../../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native): channel configurations 6 (mono) and 7 (dual mono) use the pinned [vgmstream decoder](https://github.com/vgmstream/vgmstream/blob/7c520e8f9b078dfde2f84f1b5bcbad0c6c415806/src/coding/libs/atrac9_dec.c), including its 12/24/48 kHz profile limits. Synthetic fixtures cover interleaving, reset after malformed input, partial superframes and PCM16/PCM32/float output; console PCM rounding and physical haptic output are unverified. Ordinary channel configurations and discrete multichannel streams still use LibAtrac9. Decoder creation and decoding run under the existing AJM instance lock because the dependency rebuilds shared Huffman tables during initialization. -- [sceAjmDecWVorbisCreateHeaderPacket, sceAjmDecWVorbisCreateSetupPacket](../../../core/libs/prx/libSceAjm.native/Export.cpp) (libSceAjmWVorbis, implemented in libSceAjm.native) - unknown signatures - [sceAjmBatchJobGetInfo](../../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - writes the result and format sideband of a run job without buffers with the `SIDEBAND_FORMAT` flag (0x400000000000), as [KytyPS5](https://github.com/KytyPS5/KytyPS5) does, not confirmed on a PS5 title; the format reports bitrate 0 for ATRAC9 as in KytyPS5, while [shadPS4](https://github.com/shadps4-emu/shadPS4) derives it from the superframe size; before the instance is initialized it returns the not-initialized result (1) and no format, like the other run jobs here, while KytyPS5 returns result 0 with the format of the uninitialized decoder - [sceAjmDecMp3ParseFrame](../../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - signature, result layout and rate tables taken from the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation of the PS4 library, not confirmed on a PS5 title: the layer field is not checked and MPEG-2.5 stops at 64 kbps; the original file length (`parse_ofl`) layout (Xing/Info + LAME tag, VBRI, Fraunhofer `0xB4` block with its CRC, encoder delay = samples per frame + LAME delay + 529) also comes from shadPS4 only, and the Fraunhofer block has no other public description - [sceAjmBatchJobGetCodecInfo, sceAjmBatchJobGetGaplessDecode](../../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) - the `RUN_GET_CODEC_INFO` flag (0x800), the ATRAC9 codec info layout and its next frame size are taken from the [shadPS4](https://github.com/shadps4-emu/shadPS4) and [RPCSX](https://github.com/RPCSX/rpcsx) reimplementations, not confirmed on a PS5 title; a run job without buffers returns result 0, as in shadPS4. The gapless sideband reports the total and skip counts the title set and does not count them down: RPCSX does the same, shadPS4 counts them down and BryKytyPS5 returns its current counts From 8d3314c8240b1ce17ef583cf4df72e88dbd22657 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:00:27 +0300 Subject: [PATCH 182/523] fix(rudp): recover remote address and event pump signatures --- core/libs/prx/libSceRudp/Export.cpp | 6 +++--- core/libs/tests/GuestRudpPump.cpp | 15 ++++++++------- docs/dev/debt/libSceRudp.md | 5 ++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 56df07b0ab..923b4e9a76 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -289,7 +289,7 @@ int APS5_VABI sceRudpGetContextStatus(int ctx_id, void* status, uint32_t size) { return 0; } -int APS5_VABI sceRudpGetRemoteInfo() { +int APS5_VABI sceRudpGetRemoteInfo(int contextId, void* address, std::uint32_t* addressSize, std::uint16_t* virtualPort) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -364,12 +364,12 @@ int APS5_VABI sceRudpNetFlush(void) { return g_inited ? 0 : RUDP_ERROR_NOT_INITIALIZED; } -int APS5_VABI sceRudpProcessEvents(void) { +int APS5_VABI sceRudpProcessEvents(std::uint64_t timeout) { std::lock_guard lk(g_mutex); return g_inited ? 0 : RUDP_ERROR_NOT_INITIALIZED; } -int APS5_VABI sceRudpNetReceived(void) { +int APS5_VABI sceRudpNetReceived(int socketId, const void* data, std::size_t dataSize, const void* sourceAddress, std::uint32_t sourceAddressSize) { std::lock_guard lk(g_mutex); return g_inited ? 0 : RUDP_ERROR_NOT_INITIALIZED; } diff --git a/core/libs/tests/GuestRudpPump.cpp b/core/libs/tests/GuestRudpPump.cpp index d9e350ae23..2cc7f5586f 100644 --- a/core/libs/tests/GuestRudpPump.cpp +++ b/core/libs/tests/GuestRudpPump.cpp @@ -1,6 +1,7 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include #include #include #include @@ -8,10 +9,10 @@ extern "C" { int APS5_VABI sceRudpInit_nid_postfix(void*, int); int APS5_VABI sceRudpEnd(); -int APS5_VABI sceRudpProcessEvents(); +int APS5_VABI sceRudpProcessEvents(std::uint64_t timeout); int APS5_VABI sceRudpNetFlush(); int APS5_VABI sceRudpGetMaxSegmentSize(std::uint16_t*); -int APS5_VABI sceRudpNetReceived(); +int APS5_VABI sceRudpNetReceived(int socketId, const void* data, std::size_t dataSize, const void* sourceAddress, std::uint32_t sourceAddressSize); int APS5_VABI sceRudpEnableInternalIOThread2(std::uint32_t, std::uint32_t, std::uint64_t); } @@ -27,9 +28,9 @@ int main() { constexpr int notInitialized = static_cast(0x80770001); constexpr int invalidArgument = static_cast(0x80770004); std::uint16_t segmentSize = 0x5a5a; - CHECK(sceRudpProcessEvents() == notInitialized); + CHECK(sceRudpProcessEvents(0) == notInitialized); CHECK(sceRudpNetFlush() == notInitialized); - CHECK(sceRudpNetReceived() == notInitialized); + CHECK(sceRudpNetReceived(0, nullptr, 0, nullptr, 0) == notInitialized); CHECK(sceRudpEnableInternalIOThread2(0, 0, 0) == notInitialized); CHECK(sceRudpGetMaxSegmentSize(&segmentSize) == notInitialized); CHECK(segmentSize == 0x5a5a); @@ -37,9 +38,9 @@ int main() { alignas(8) static unsigned char pool[0xC58]; CHECK(sceRudpInit_nid_postfix(pool, sizeof(pool)) == 0); - CHECK(sceRudpProcessEvents() == 0); + CHECK(sceRudpProcessEvents(0) == 0); CHECK(sceRudpNetFlush() == 0); - CHECK(sceRudpNetReceived() == 0); + CHECK(sceRudpNetReceived(0, nullptr, 0, nullptr, 0) == 0); CHECK(sceRudpEnableInternalIOThread2(0x4000, 0, 0x3) == 0); CHECK(sceRudpGetMaxSegmentSize(nullptr) == invalidArgument); CHECK(sceRudpGetMaxSegmentSize(&segmentSize) == 0); @@ -53,6 +54,6 @@ int main() { CHECK(sceRudpGetMaxSegmentSize(&segmentSize) == notInitialized); CHECK(segmentSize == 0x5a5a); - CHECK(sceRudpNetReceived() == notInitialized); + CHECK(sceRudpNetReceived(0, nullptr, 0, nullptr, 0) == notInitialized); return 0; } diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index f8abf0989f..d6337281b5 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -2,15 +2,14 @@ ### Silent stubs -- [sceRudpProcessEvents, sceRudpNetFlush and sceRudpNetReceived](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host, and every `sceRudpWrite`, `sceRudpRead` and `sceRudpGetStatus` call here reports a reset or idle context without queueing data, so no context ever holds pending data to process, send or receive; the three calls return success after `sceRudpInit` and `RUDP_ERROR_NOT_INITIALIZED` before it, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) returns OK from its stubs. What the console's `sceRudpNetReceived` returns is unverified. PPSA03490 calls `sceRudpProcessEvents(0)` followed by `sceRudpNetFlush()` from its P2P service thread and ignores the first result; whether the real calls take parameters is unknown +- [sceRudpProcessEvents, sceRudpNetFlush and sceRudpNetReceived](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host, and every `sceRudpWrite`, `sceRudpRead` and `sceRudpGetStatus` call here reports a reset or idle context without queueing data, so no context ever holds pending data to process, send or receive; the three calls return success after `sceRudpInit` and `RUDP_ERROR_NOT_INITIALIZED` before it, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) returns OK from its stubs. What the console's `sceRudpNetReceived` returns is unverified. PPSA03490 calls `sceRudpProcessEvents(0)` followed by `sceRudpNetFlush()` from its P2P service thread and ignores the first result. - [sceRudpEnableInternalIOThread2](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) takes `(uint32_t stack_size, uint32_t priority, uint64_t affinity)`, taken from the managed `SceRudp.Rudp.EnableInternalIOThread(uint stackSize, uint priority, ulong affinity)` in PPSA06888's Unity metadata; the first two match `sceRudpEnableInternalIOThread`. It returns success after `sceRudpInit` and `RUDP_ERROR_NOT_INITIALIZED` before it, as `sceRudpEnableInternalIOThread` does, without starting any thread. Whether the console reports other errors, and whether it applies the affinity, is unverified. - [sceRudpActivate](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host to activate, so it returns success without doing anything, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) does for its own `sceRudpActivate`. PPSA01325 calls it with no arguments during start-up; what the console activates, and whether it reports an error before `sceRudpInit`, is unverified ### Unknown function info - [sceRudpGetStatus](../../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`. Measured on a PS5 (firmware 13.60): the status is 0xF8 bytes; before `sceRudpInit` every call returns `RUDP_ERROR_NOT_INITIALIZED`, and after it a null pointer or a byte count of 0 or above 0xF8 returns `RUDP_ERROR_INVALID_ARGUMENT`. The layout matches `SceRudpStatus` in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp) with `currentContexts` at 0x64 and `allocs`, `frees`, `memCurrent` and `memPeak` at 0xC8 to 0xD4. Only `currentContexts` is reported: the traffic counters stay 0 because nothing is sent, and the allocator counters stay 0 because the library does not allocate from the title's pool (the console reports 6 allocations and 0x1260 bytes right after `sceRudpInit`, and 4 more allocations and 0x3F0 bytes per context). The console also writes `currentContexts` and the allocator counters when the byte count is smaller than their offset; only the requested bytes are written here. -- [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. The `uint16_t*` parameter is inferred: the measurement does not show how many bytes the console writes, PPSA03490 passes a pointer to a 16-bit value and [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.cpp) declares the PS3 `cellRudpSetMaxSegmentSize(u16 mss)`, so only 16 bits are written +- [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. - [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - full PS5 option contracts are unverified; defaults and validation follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have unknown meanings; last-error option 14 is not maintained. Their setters store all 32 bits per context and return 0 for valid four-byte buffers, preserving acceptance on `main`; their getters throw. Static inspection of PPSA06888 v01.001.008's bundled Unity transport shows its context-set constructor configuring IDs 2, 3, 5 and 6; runtime usage and other paths remain unverified. The plugin wrappers pass four-byte values. No context can connect, so stored settings cannot affect traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. -- PPSA06888 imports with unknown signatures: [sceRudpGetRemoteInfo](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp), imported by its bundled `Media/Plugins/SceRudp.prx` and not called during start-up. The third argument is a pointer to a 4-byte buffer seeded from the byte its wrapper reads through the second argument, which does not show what the arguments mean. The managed `SceRudp.Context.GetRemoteInfo()` in the title's Unity metadata takes no parameters and returns a `SceRudp.AddrAndVport`; its native arguments are not visible there. Names from the NID table of [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp), which declares them without parameters too - [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. The signatures, the 8-byte `SceRudpPollEvent`, the operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once - [sceRudpAccept, sceRudpFlush, sceRudpGetLocalInfo, sceRudpListen, sceRudpPollCancel and sceRudpSetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRudp.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From fbb28d6d62f68ef06b3275c7c1eb13f8892319bd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:00:28 +0300 Subject: [PATCH 183/523] docs(debt): remove the resolved keyboard conversion signature --- docs/dev/debt/libSceAudioOut.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceAudioOut.md b/docs/dev/debt/libSceAudioOut.md index f6e6901f1f..d86f3e1808 100644 --- a/docs/dev/debt/libSceAudioOut.md +++ b/docs/dev/debt/libSceAudioOut.md @@ -4,7 +4,7 @@ - [sceAudioOut2ContextAdvance and sceAudioOut2ContextPush](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) - with an SDL device, Advance snapshots each port's PCM and gain; Push consumes completed grains in order and a blocking Push waits for Advance when none is ready. Inferred from PPSA26358, which rewrites the same 512-frame, 12-channel float buffer on a producer thread and pushes on another thread. Console buffer ownership, snapshot timing and empty-queue errors are unverified. Completed grains remain queued until Push consumes them, so memory use grows with an unconsumed PCM backlog. The console backlog limit is unverified. A nonblocking Push without a completed grain throws; this is a runtime restriction, not a console measurement. Context destruction concurrent with active calls remains unsupported. Without an SDL device the existing wall-clock path remains. -- [sceAudioOut2UserGetSupportedAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp) (libSceAudioOut) - the user handle and two uint32 output pointers follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. The masks describe this runtime: no context attributes and port attributes PCM (bit 0) and gain (bit 1). Null outputs and unknown or destroyed user handles throw; console validation order and error values are unknown. Users are tracked for this query, but port ownership and busy-user destruction are not implemented. The other PPSA23566 import, [sceConvertKeycodeGetCharacterFromKeyboardData](../../../core/libs/prx/libSceConvertKeycode/Export.cpp), remains declared without parameters, with an unknown signature. Names from the shadPS4 aerolib NID list +- [sceAudioOut2UserGetSupportedAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp) (libSceAudioOut) - the user handle and two uint32 output pointers follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. The masks describe this runtime: no context attributes and port attributes PCM (bit 0) and gain (bit 1). Null outputs and unknown or destroyed user handles throw; console validation order and error values are unknown. Users are tracked for this query, but port ownership and busy-user destruction are not implemented. - [sceAudioOutExOpen, sceAudioOutExConfigureOutput, sceAudioOutExClose](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - the HDMI bitstream calls follow [PS5-Audio-Passthrough-Research](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/docs/FINDINGS.md#L31-L80), which disassembles firmware 12.70's libSceAudioOut and logs the calls on a console. `sceAudioOutExOpen(user, mode)` returns `0x80260015` for a mode above 10 or outside mask `0x61F`; otherwise it opens a port of internal type 6 in S16 stereo, 256 frames at 48000 Hz for modes 0, 1, 2 and 9 and 1024 frames at 192000 Hz for modes 3, 4 and 10. `sceAudioOutExConfigureOutput(control, flags, mode, target, option)` returns `0x80268009` when `control` is not 0, takes modes 0 to 10 and 0xFF and targets 1 to 3 and 0xFF, and returned 0 on the console; `sceAudioOutExClose` returned 0. The argument registers match [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L26-L28) and PPSA99051's [bitstream_out.cpp](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/sample-app/src/platform/ps5/bitstream_out.cpp#L29-L32). Audio reaches the host through SDL as PCM and the host has no HDMI bitstream output, so an Ex port has no host device: `sceAudioOutOutput` paces it at its own rate and drops its IEC 61937 bursts, and `sceAudioOutExConfigureOutput` changes nothing. Nonzero `flags` or `option`, other modes or targets, `sceAudioOutExClose` of a handle that is not an Ex port, and `sceAudioOutGetPortState` on an Ex port throw, since the console's answers are unknown - [sceAudioOutExGetMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) reports a sink without audio descriptors: it zeroes the buffer and returns 0, as `sceAudioOutSysGetHdmiMonitorInfo` does, since audio reaches the host through SDL as PCM and there is no EDID to describe. The arguments `(0, 0, info, size)` and the descriptor count as a 32-bit value at +0x98, with 8-byte descriptors from +0xA0, follow [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L62-L117) (PPSA99505), which passes a 0x400-byte buffer and read that layout from one TV; with no descriptor listed it decodes the audio itself instead of opening a bitstream port. The meaning of the first two arguments and the structure's size are unknown: nonzero first or second arguments and sizes below 0xA0, which cannot hold the count, throw, and every byte of the declared size is written as 0. A null buffer returns `0x80260004` like the other AudioOut calls; the console's code is not measured - [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - assumed to take `(user_id, output)`: [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp) reads `(user_id, output, latency_us)`, but the known calls set just edi and esi. The validation follows KytyPS5: an output above 2 returns `0x80268001` and every user id is accepted, where before only `(0xFF, 2)` (PPSA26344) and `(0xFF, 1)` (PPSA14632) were; the latency value of the three-argument reading is never received, and there is no 3D rendering stage to apply anything to From d64e3c4ea73010f4c35a2e14184e34e87cf68460 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:02:16 +0300 Subject: [PATCH 184/523] fix(tts): recover status and speech parameter signatures --- core/libs/prx/libSceTextToSpeech2/Export.cpp | 37 +++++++++++++++++--- core/libs/tests/GuestTextToSpeech2.cpp | 18 +++++----- docs/dev/debt/libSceTextToSpeech2.md | 3 +- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceTextToSpeech2/Export.cpp b/core/libs/prx/libSceTextToSpeech2/Export.cpp index 09950022bc..375bf561f4 100644 --- a/core/libs/prx/libSceTextToSpeech2/Export.cpp +++ b/core/libs/prx/libSceTextToSpeech2/Export.cpp @@ -9,13 +9,40 @@ constexpr std::int32_t SCE_KERNEL_ERROR_EOPNOTSUPP = static_cast(0 } +struct TextToSpeechInitParameter { + std::uint32_t version; + std::int32_t threadPriority; + std::uint64_t cpuAffinity; + std::uint8_t reserved[48]; +}; + +struct TextToSpeechOpenParameter { + std::uint32_t language; + std::uint32_t voiceType; + std::uint8_t reserved[56]; +}; + +struct TextToSpeechSpeakParameter { + const char16_t* text; + std::uint32_t length; + std::uint32_t mode; + std::uint32_t volume; + std::int32_t speed; + std::uint8_t reserved[40]; +}; + +static_assert(sizeof(TextToSpeechInitParameter) == 64); +static_assert(sizeof(TextToSpeechOpenParameter) == 64); +static_assert(sizeof(TextToSpeechSpeakParameter) == 64); +static_assert(offsetof(TextToSpeechSpeakParameter, reserved) == 24); + extern "C" { -int APS5_VABI sceTextToSpeech2GetSystemStatus() { +int APS5_VABI sceTextToSpeech2GetSystemStatus(std::int32_t* status) { return SCE_KERNEL_ERROR_EOPNOTSUPP; } -std::int32_t APS5_VABI sceTextToSpeech2Initialize(const void* param) { +std::int32_t APS5_VABI sceTextToSpeech2Initialize(const TextToSpeechInitParameter* param) { (void)param; return SCE_KERNEL_ERROR_EOPNOTSUPP; } @@ -28,15 +55,15 @@ int APS5_VABI sceTextToSpeech2Close() { return SCE_KERNEL_ERROR_EOPNOTSUPP; } -int APS5_VABI sceTextToSpeech2GetSpeechStatus() { +int APS5_VABI sceTextToSpeech2GetSpeechStatus(std::int32_t* status) { return SCE_KERNEL_ERROR_EOPNOTSUPP; } -int APS5_VABI sceTextToSpeech2Open() { +int APS5_VABI sceTextToSpeech2Open(const TextToSpeechOpenParameter* parameter) { return SCE_KERNEL_ERROR_EOPNOTSUPP; } -int APS5_VABI sceTextToSpeech2Speak() { +int APS5_VABI sceTextToSpeech2Speak(const TextToSpeechSpeakParameter* parameter) { return SCE_KERNEL_ERROR_EOPNOTSUPP; } diff --git a/core/libs/tests/GuestTextToSpeech2.cpp b/core/libs/tests/GuestTextToSpeech2.cpp index 9bac0096e0..93de9dc755 100644 --- a/core/libs/tests/GuestTextToSpeech2.cpp +++ b/core/libs/tests/GuestTextToSpeech2.cpp @@ -6,26 +6,26 @@ extern "C" { std::int32_t APS5_VABI sceTextToSpeech2Initialize(const void* param); -int APS5_VABI sceTextToSpeech2Open(); +int APS5_VABI sceTextToSpeech2Open(const void* parameter); int APS5_VABI sceTextToSpeech2Cancel(); int APS5_VABI sceTextToSpeech2Close(); -int APS5_VABI sceTextToSpeech2GetSpeechStatus(); -int APS5_VABI sceTextToSpeech2Speak(); +int APS5_VABI sceTextToSpeech2GetSpeechStatus(std::int32_t* status); +int APS5_VABI sceTextToSpeech2Speak(const void* parameter); int APS5_VABI sceTextToSpeech2Terminate(); -int APS5_VABI sceTextToSpeech2GetSystemStatus(); +int APS5_VABI sceTextToSpeech2GetSystemStatus(std::int32_t* status); } int main() { constexpr std::int32_t unsupported = static_cast(0x8002002D); - const std::uint32_t param[12]{0x2000000, 0x26c}; + const std::uint32_t param[16]{0x2000000, 0x26c}; if (sceTextToSpeech2Initialize(param) != unsupported) { std::puts("TextToSpeech2: initialization did not report the unsupported operation"); std::abort(); } - if (sceTextToSpeech2Open() != unsupported || sceTextToSpeech2Cancel() != unsupported || - sceTextToSpeech2Close() != unsupported || sceTextToSpeech2GetSpeechStatus() != unsupported || - sceTextToSpeech2Speak() != unsupported || sceTextToSpeech2Terminate() != unsupported || - sceTextToSpeech2GetSystemStatus() != unsupported) { + if (sceTextToSpeech2Open(nullptr) != unsupported || sceTextToSpeech2Cancel() != unsupported || + sceTextToSpeech2Close() != unsupported || sceTextToSpeech2GetSpeechStatus(nullptr) != unsupported || + sceTextToSpeech2Speak(nullptr) != unsupported || sceTextToSpeech2Terminate() != unsupported || + sceTextToSpeech2GetSystemStatus(nullptr) != unsupported) { std::puts("TextToSpeech2: later calls did not report the unsupported operation"); std::abort(); } diff --git a/docs/dev/debt/libSceTextToSpeech2.md b/docs/dev/debt/libSceTextToSpeech2.md index fb8dc582dc..58ba1298f6 100644 --- a/docs/dev/debt/libSceTextToSpeech2.md +++ b/docs/dev/debt/libSceTextToSpeech2.md @@ -6,5 +6,4 @@ ### Unknown function info -- [sceTextToSpeech2Initialize](../../../core/libs/prx/libSceTextToSpeech2/Export.cpp) (libSceTextToSpeech2) - the console's error for an unavailable speech engine is unknown; it returns the kernel's `EOPNOTSUPP` (0x8002002D). PPSA01317 passes a param that starts with 0x2000000 and only tests the result for non-zero; the other exports it imports are declared without parameters and return the same error -- [sceTextToSpeech2GetSystemStatus](../../../core/libs/prx/libSceTextToSpeech2/Export.cpp) (libSceTextToSpeech2) - unknown signature; returns `EOPNOTSUPP` like `sceTextToSpeech2Initialize` +- [sceTextToSpeech2Initialize](../../../core/libs/prx/libSceTextToSpeech2/Export.cpp) (libSceTextToSpeech2) - the console's error for an unavailable speech engine is unknown; it returns the kernel's `EOPNOTSUPP` (0x8002002D). PPSA01317 passes a param that starts with 0x2000000 and only tests the result for non-zero. From fe0540a4e52a31864871e246f2f35c65186c9f39 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:03:20 +0300 Subject: [PATCH 185/523] fix(hmd): recover eye geometry and led control signatures --- core/libs/prx/libSceHmd/Export.cpp | 30 ++++++++++++++++++++++-------- docs/dev/debt/libSceHmd.md | 7 ------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 1d529744da..493e492595 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -16,6 +16,20 @@ std::int32_t Initialize(const Hmd::InitializeParam* param) { } +struct HmdEyeDistances { + std::uint32_t left; + std::uint32_t right; +}; + +struct HmdEyeOffset { + float x; + float y; + float z; +}; + +static_assert(sizeof(HmdEyeDistances) == 8); +static_assert(sizeof(HmdEyeOffset) == 12); + extern "C" { std::int32_t APS5_VABI sceHmdInitialize(const Hmd::InitializeParam* param) { @@ -147,12 +161,12 @@ int APS5_VABI sceHmdInternalGetDebugSocialScreenMode(std::uint8_t* mode) { return 0; } -int APS5_VABI sceHmdInternalSetM2LedBrightness(void) { +int APS5_VABI sceHmdInternalSetM2LedBrightness(std::int32_t handle, std::uint32_t ledMask, const std::uint8_t* brightness) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalSetIPD(void) { +int APS5_VABI sceHmdInternalSetIPD(std::int32_t mode, HmdEyeDistances distances) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -226,12 +240,12 @@ int APS5_VABI sceHmdReprojectionClearUserEventEnd(void) { return 0; } -int APS5_VABI sceHmdInternalGetIPD(void) { +int APS5_VABI sceHmdInternalGetIPD(HmdEyeDistances* distances) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalSetVRMode(void) { +int APS5_VABI sceHmdInternalSetVRMode(std::int32_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -261,7 +275,7 @@ int APS5_VABI sceHmdReprojectionInitializeCapture(void) { return 0; } -int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset) { +int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, HmdEyeOffset* leftOffset, HmdEyeOffset* rightOffset) { (void)handle; (void)leftOffset; (void)rightOffset; @@ -302,7 +316,7 @@ int APS5_VABI sceHmdInternalMirroringModeSetAspectDebug(void) { return 0; } -int APS5_VABI sceHmdInternalGetPuBuildNumber(void) { +int APS5_VABI sceHmdInternalGetPuBuildNumber(std::uint32_t* buildNumber) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -311,7 +325,7 @@ int APS5_VABI sceHmdDistortionGetCompoundEyeCorrectionCommand(void) { return static_cast(0x81110009); } -int APS5_VABI sceHmdInternalSetS3dPassMode(void) { +int APS5_VABI sceHmdInternalSetS3dPassMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -340,7 +354,7 @@ int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void) { return 0x100; } -int APS5_VABI sceHmdInternalGetDebugMode(void) { +int APS5_VABI sceHmdInternalGetDebugMode(std::uint8_t* mode) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index c6d6c7bb75..2e04c6b1e6 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -14,8 +14,6 @@ - [sceHmdReprojectionUnsetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalRegisterEventCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetM2LedBrightness](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetIPD](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspect](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuGetStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -26,8 +24,6 @@ - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionClearUserEventEnd](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetIPD](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetVRMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportReadDataSize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -39,14 +35,11 @@ - [sceHmdInternalAnotherScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCreateSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspectDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetPuBuildNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetS3dPassMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStopCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsCommonDlgMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetSensorCalibrationData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDebugMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalIsCommonDlgVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetPortConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 51ef9e8111e6cc1dea90e5aba2dad4fb15540b3b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:03:40 +0300 Subject: [PATCH 186/523] docs(netctl): close verified parameterless ipc signatures --- docs/dev/debt/libSceNetCtl.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 1c0958c89b..4f3c132d59 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -8,13 +8,8 @@ - [sceNetCtlGetInfoV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `Jy1EO5GdlcM`) - imported by PPSA08587. `(int code, SceNetCtlInfoV6* info)` with codes 1-5 (IP address, link-local address, default route, primary and secondary DNS) comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero), whose source is unknown; shadPS4 and fpPS4 only stub it. A null `info` answers `INVALID_ADDR`, a code outside 1-5 `INVALID_CODE`, and a host without an IPv6 address `NOT_CONNECTED`, as `sceNetCtlGetStateV6` reports it. The layout of each answer is unverified, so every code throws on a host with an IPv6 address - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - answers from the host interface that holds an IPv4 address, preferring the one that carries the default route: its address, netmask, gateway, MTU and first two IPv4 DNS servers. The device is reported as wired, the link as connected, the IP configuration as DHCP and the HTTP proxy as off, whatever the host uses, and the Ethernet address is the one `sceNetGetMacAddress` reports. A gateway or DNS server the interface does not have is reported as `0.0.0.0`; what a console reports then is unknown. The DHCP host name (12), the PPPoE user name (13), the HTTP proxy server (20) and the Wi-Fi BSSID and SSID (5, 6) are empty strings, as that configuration sets none of them; the remaining Wi-Fi codes (7-10) and the HTTP proxy port (21) report 0; this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/network.cpp) and is not measured on hardware -- [sceNetCtlCheckCallbackForNpToolkit](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlCheckCallbackForLibIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlConnectIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlConnectWithRetryIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlConnectConfIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlDisconnectIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetScanInfoBssidIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetScanInfoByBssidIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -24,11 +19,6 @@ - [sceNetCtlGetResultV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `H5yARg37U5g`) - `(int eventType, int* errorCode)` comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/NetCtl.cs#L246-L251), whose source is unknown; shadPS4 only stubs it. Callbacks registered with `sceNetCtlRegisterCallbackV6` receive no event, so event types 1-3 write 0 to `errorCode`; what a console writes before the first event is unmeasured. A null `errorCode` answers `INVALID_ADDR`, as `sceNetCtlGetResult` does, and other event types throw, as their error code on a console is unknown - [sceNetCtlGetInfoV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetNatInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlSetStunWithPaddingFlagIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlUnsetStunWithPaddingFlagIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlIsBandwidthManagementEnabledIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlDisableBandwidthManagementIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlEnableBandwidthManagementIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetBandwidthInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetNetEvConfigInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -45,15 +35,10 @@ - [sceNetCtlApRpStartWithRetry](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStartConf](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStop](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApCpStop](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApAppInitWpaKey](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApAppInitWpaKeyForQa](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRestart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweCheckCallbackIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweStartInternetConnectionTestIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweFinishInternetConnectionTestIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweStartInternetConnectionTestBandwidthTestIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweSetInternetConnectionTestResultIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetWifiInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature From 5c9322b1e01f04693074105616a27972149d45c2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:06:47 +0300 Subject: [PATCH 187/523] fix(net): recover synchronization and event callback contracts --- core/libs/prx/libSceNet/Export.cpp | 31 ++++++++++++++++++++---------- docs/dev/debt/libSceNet.md | 10 ---------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 7cddc02c41..11e606de4a 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -46,6 +46,17 @@ // Guest socket descriptors retain PS5 semantics while their transport is backed by host sockets. +struct NetSyncObject { + void* mutex; + void* condition; + std::uint32_t signals; + std::uint32_t reserved; +}; + +static_assert(sizeof(NetSyncObject) == 24); + +using NetEventCallback = std::uint64_t (APS5_VABI*)(std::uint32_t interfaceId, std::uint64_t events, void* userData, void* waitData); + struct NetIpv6Address { std::uint8_t bytes[16]; }; @@ -2128,22 +2139,22 @@ int APS5_VABI sceNetDbgInit(void) { return 0; } -int APS5_VABI sceNetEventCallbackCreate(void) { +int APS5_VABI sceNetEventCallbackCreate(std::uint32_t interfaceId, NetEventCallback callback, void* userData, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetEventCallbackDestroy(void) { +int APS5_VABI sceNetEventCallbackDestroy(std::uint32_t interfaceId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetEventCallbackGetError(void) { +int APS5_VABI sceNetEventCallbackGetError(std::uint32_t interfaceId, std::int32_t* error, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetEventCallbackWaitCB(void) { +int APS5_VABI sceNetEventCallbackWaitCB(std::int32_t timeout, void* waitData, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2202,7 +2213,7 @@ int APS5_VABI sceNetGetIfnameNumList(void) { return 0; } -int APS5_VABI sceNetGetNameToIndex(void) { +int APS5_VABI sceNetGetNameToIndex(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2298,27 +2309,27 @@ int APS5_VABI sceNetSocketInternal(const char* name, int domain, int type, int p return 0; } -int APS5_VABI sceNetSyncCreate(void) { +int APS5_VABI sceNetSyncCreate(NetSyncObject* sync, std::uint32_t signals, const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetSyncDestroy(void) { +int APS5_VABI sceNetSyncDestroy(NetSyncObject* sync) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetSyncGet(void) { +NetSyncObject* APS5_VABI sceNetSyncGet(std::uint32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetSyncSignal(void) { +int APS5_VABI sceNetSyncSignal(NetSyncObject* sync, std::uint32_t signals) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetSyncWait(void) { +int APS5_VABI sceNetSyncWait(NetSyncObject* sync, std::uint32_t* timeout) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 93f860806d..8e063352ab 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -27,10 +27,6 @@ - [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetControl](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDbgInit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEventCallbackCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEventCallbackDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEventCallbackGetError](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEventCallbackWaitCB](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetFreeAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetArpInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetAppMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -40,7 +36,6 @@ - [sceNetGetIfListOnce](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetIfName](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t index)` signature and the table of ten interface names (`lo0`, `eth0`, `eth1`, `dbg0`, `wlan0`, `wlan1`, `gbe0`, `bt0`, `phone0`, `pppoe0`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); an index past the table sets `EINVAL` and returns an empty string. Whether the console has more names, or returns null, is unverified on hardware - [sceNetGetIfnameNumList](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetNameToIndex](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t* output)` signature and the `EINVAL` failure for a null output follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the value comes from the host's `std::random_device`, as `kern.rng_pseudo` in libkernel does. Unverified on hardware - [sceNetGetRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetSystemTime](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unverified on PS5 hardware: the `void(uint64_t*)` signature, process-relative microseconds and ignored null output follow [shadPS4 net.cpp](https://github.com/shadps4-emu/shadPS4/blob/d615b3887ba29025a59242e1a26f5b68bb176158/src/core/libraries/net/net.cpp#L1654-L1659). The clock is the same as `sceKernelGetProcessTime`, including `APS5_TIME_SCALE` @@ -50,11 +45,6 @@ - [sceNetMemoryAllocate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int64_t size, int flags)` signature, flag 2 as zero fill and the null result for a size of 0 or less follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp). The block comes from the guest heap, and an allocation failure returns null without setting an errno. Any other flag bit throws, since its meaning is not known. Unverified on hardware - [sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(void* memory)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the block returns to the guest heap and a null pointer is ignored. Unverified on hardware - [sceNetSetDnsInfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSyncCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSyncDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSyncGet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSyncSignal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSyncWait](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 3812ecbc08fb71a3272de23c3326167895e1c2fa Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:06:48 +0300 Subject: [PATCH 188/523] fix(json): recover scalar boolean conversion semantics --- core/libs/prx/libSceJson2/Export.cpp | 14 +++++++++++--- core/libs/tests/GuestJson.cpp | 27 +++++++++++++++++++++++++++ docs/dev/debt/libSceJson2.md | 2 +- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 9ca8b9d125..93e460c734 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -1057,9 +1057,17 @@ int APS5_VABI _ZN3sce4Json5Value22setTypeMismatchHandlerEPFRKS1_NS0_9ValueTypeEP bool APS5_VABI _ZNK3sce4Json5ValuecvbEv(const Value* self) { const Node& n = NodeOf(*self); - if (n.type == TypeNull) return false; - if (n.type == TypeBoolean) return n.boolean; - return true; + switch (n.type) { + case TypeNull: return false; + case TypeBoolean: return n.boolean; + case TypeInteger: return n.integer != 0; + case TypeUInteger: return n.uinteger != 0; + case TypeReal: return n.real != 0.0; + case TypeString: return n.string.text && !n.string.text->empty(); + case TypeArray: + case TypeObject: return true; + default: throw std::runtime_error("sce::Json::Value: invalid value type"); + } } void APS5_VABI _ZN3sce4Json6Object4PairC1ERKNS0_6StringERKNS0_5ValueE(Pair* self, const String* key, const Value* value) { diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index c29cbc39b3..1645862b04 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -98,6 +98,7 @@ void APS5_VABI _ZN3sce4Json5ValueC1ERKNS0_6ObjectE(Value*, const Object*); void APS5_VABI _ZN3sce4Json5ValueD1Ev(Value*); void APS5_VABI _ZN3sce4Json5Value5clearEv(Value*); int APS5_VABI _ZN3sce4Json5Value3setEl(Value*, std::int64_t); +int APS5_VABI _ZN3sce4Json5Value3setEm(Value*, std::uint64_t); int APS5_VABI _ZN3sce4Json5Value3setEPKc(Value*, const char*); std::int32_t APS5_VABI _ZNK3sce4Json5Value7getTypeEv(const Value*); const bool* APS5_VABI _ZNK3sce4Json5Value10getBooleanEv(const Value*); @@ -1190,6 +1191,32 @@ static void ValueOperatorBool() { Require(_ZNK3sce4Json5ValuecvbEv(&boolTrue)); _ZN3sce4Json5ValueD1Ev(&boolTrue); + Value scalar{}; + _ZN3sce4Json5ValueC1Ev(&scalar); + for (std::int64_t value : {std::int64_t{0}, std::int64_t{1}, std::int64_t{-1}}) { + Require(_ZN3sce4Json5Value3setEl(&scalar, value) == 0); + Require(_ZNK3sce4Json5ValuecvbEv(&scalar) == (value != 0)); + } + for (std::uint64_t value : {std::uint64_t{0}, std::uint64_t{1}, std::uint64_t{0xffffffffffffffff}}) { + Require(_ZN3sce4Json5Value3setEm(&scalar, value) == 0); + Require(_ZNK3sce4Json5ValuecvbEv(&scalar) == (value != 0)); + } + for (double value : {0.0, -0.0, 1.0, -1.0, std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}) { + Require(_ZN3sce4Json5Value3setEd(&scalar, value) == 0); + Require(_ZNK3sce4Json5ValuecvbEv(&scalar) == (value != 0.0)); + } + for (const char* text : {"", "0", "false", " "}) { + Require(_ZN3sce4Json5Value3setEPKc(&scalar, text) == 0); + Require(_ZNK3sce4Json5ValuecvbEv(&scalar) == (*text != 0)); + } + _ZN3sce4Json5ValueD1Ev(&scalar); + for (std::int32_t type : {TypeArray, TypeObject}) { + Value container{}; + _ZN3sce4Json5ValueC1ENS0_9ValueTypeE(&container, type); + Require(_ZNK3sce4Json5ValuecvbEv(&container)); + _ZN3sce4Json5ValueD1Ev(&container); + } + Value cleared{}; _ZN3sce4Json5ValueC1Eb(&cleared, true); Require(_ZNK3sce4Json5ValuecvbEv(&cleared)); diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 6db5c83e5b..082a4f641b 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -9,5 +9,5 @@ - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown - [sce::Json::String size, capacity, resize, compare and operator==(const String&), append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified -- [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - member of `Value` returning `bool` as a conversion operator, with no cited source in KytyPS5, shadPS4, SharpProspero, or disassembly: evaluates to `false` for `TypeNull` and boolean `false`, and `true` for boolean `true`. For all other value types it currently returns `true`; whether the console converts them, throws, or checks container emptiness is unverified +- [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - callback dispatch before conversion is not implemented. The system library invokes the applicable type mismatch handler before converting a non-boolean value, with SDK-dependent global callback handling. - [sce::Json::Object::insert](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - new keys are appended in insertion order; the system library uses key order. Duplicate insertion preserves the existing value. From 4a7d2251d30562f99c8adf445080476db4617336 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:11:37 +0300 Subject: [PATCH 189/523] fix(ime): recover candidate keyboard and panel signatures --- core/libs/prx/libSceIme/Export.cpp | 55 ++++++++++++++++++++---------- docs/dev/debt/libSceIme.md | 18 ---------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/core/libs/prx/libSceIme/Export.cpp b/core/libs/prx/libSceIme/Export.cpp index 6eaf4cbbe8..671a4833b0 100644 --- a/core/libs/prx/libSceIme/Export.cpp +++ b/core/libs/prx/libSceIme/Export.cpp @@ -15,6 +15,25 @@ #include "prx/libSceIme/include/ImeFilterText.hpp" #include "prx/libSceIme/include/ImeInput.hpp" +struct ImeVshContext; + +struct ImeVshCaretGeometry { + std::int32_t x; + std::int32_t y; + std::uint32_t height; + std::uint32_t index; +}; + +struct ImeVshTextGeometry { + std::int32_t x; + std::int32_t y; + std::uint32_t width; + std::uint32_t height; +}; + +static_assert(sizeof(ImeVshCaretGeometry) == 16); +static_assert(sizeof(ImeVshTextGeometry) == 16); + namespace { constexpr int ErrorBusy = static_cast(0x80bc0001u); @@ -586,7 +605,7 @@ int APS5_VABI sceImeVshOpen(void) { return 0; } -int APS5_VABI sceImeKeyboardUpdate(void) { +int APS5_VABI sceImeKeyboardUpdate(EventHandler handler) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -601,7 +620,7 @@ int APS5_VABI sceImeVshUpdateContext(void) { return 0; } -int APS5_VABI sceImeVshClearPreedit(void) { +int APS5_VABI sceImeVshClearPreedit(ImeVshContext* context) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -611,47 +630,47 @@ int APS5_VABI sceImeVshNotifySetText(void) { return 0; } -int APS5_VABI sceImeVshSetCaretGeometry(void) { +int APS5_VABI sceImeVshSetCaretGeometry(ImeVshContext* context, const ImeVshCaretGeometry* geometry) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshConfirmPreedit(void) { +int APS5_VABI sceImeVshConfirmPreedit(ImeVshContext* context) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshSetCaretIndexInPreedit(void) { +int APS5_VABI sceImeVshSetCaretIndexInPreedit(ImeVshContext* context, std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshSetPreeditGeometry(void) { +int APS5_VABI sceImeVshSetPreeditGeometry(ImeVshContext* context, const ImeVshTextGeometry* geometry) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshSetSelectGeometry(void) { +int APS5_VABI sceImeVshSetSelectGeometry(ImeVshContext* context, const ImeVshTextGeometry* geometry) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshClose(void) { +int APS5_VABI sceImeVshClose(ImeVshContext* context) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshGetPanelPositionAndForm(void) { +int APS5_VABI sceImeVshGetPanelPositionAndForm(ImeVshContext* context, PositionAndForm* form) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeSetCandidateIndex(void) { +int APS5_VABI sceImeSetCandidateIndex(std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeConfirmCandidate(void) { +int APS5_VABI sceImeConfirmCandidate(std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -661,12 +680,12 @@ int APS5_VABI sceImeDisableController(void) { return 0; } -int APS5_VABI sceImeSetVoiceDictationEnabled(void) { +int APS5_VABI sceImeSetVoiceDictationEnabled(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeSetEditEventWithRemotePlayEnabled(void) { +int APS5_VABI sceImeSetEditEventWithRemotePlayEnabled(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -680,7 +699,7 @@ int APS5_VABI sceImeVshInformConfirmdString2(void) { return 0; } -int APS5_VABI sceImeVshSetSelectionText(void) { +int APS5_VABI sceImeVshSetSelectionText(ImeVshContext* context, const char16_t* text, std::uint32_t length) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -695,7 +714,7 @@ int APS5_VABI sceImeCheckRemoteEventParam(void) { return 0; } -int APS5_VABI sceImeKeyboardOpenInternal(void) { +int APS5_VABI sceImeKeyboardOpenInternal(std::int32_t userId, EventHandler handler, const KeyboardParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -745,7 +764,7 @@ int APS5_VABI sceImeVshCheckAutoReConvertingPreeditText(void) { return 0; } -int APS5_VABI sceImeVshSetPanelPosition(void) { +int APS5_VABI sceImeVshSetPanelPosition(ImeVshContext* context, float x, float y, std::int32_t horizontalAlignment, std::int32_t verticalAlignment) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -785,12 +804,12 @@ int APS5_VABI sceImeDicReplaceWord(void) { return 0; } -int APS5_VABI sceImeDicDeleteLearnDics(void) { +int APS5_VABI sceImeDicDeleteLearnDics(std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeDicDeleteUserDics(void) { +int APS5_VABI sceImeDicDeleteUserDics(std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceIme.md b/docs/dev/debt/libSceIme.md index 465e86b085..2d0e987979 100644 --- a/docs/dev/debt/libSceIme.md +++ b/docs/dev/debt/libSceIme.md @@ -11,29 +11,14 @@ - [sceImeFilterText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the behaviour follows the disassembly of `libSceIme.sprx` from firmware 13.60 and is differential-tested against that function in an emulator, not measured on a console. [ImeFilterCodepages.hpp](../../../core/libs/prx/libSceIme/include/ImeFilterCodepages.hpp) holds the two codepage checks (helpers at 0x4850 and 0x48f0), rebuilt from Unicode's public [CP932](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT) and [CP936](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT) tables plus the Latin-1, CJK Extension A and a few other units the firmware also accepts (listed in the PR). The parameter layout beyond `type` is inferred: `mask` is the allowed-class bitmask, and `version` and `variant` derive it when it is 0. [sceImeCheckFilterText](../../../core/libs/prx/libSceIme/Export.cpp) throws, as its contract is unknown - [sceImeOpen, sceImeUpdate, sceImeSetText, sceImeSetCaret, sceImeClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the event ids (Open 0, UpdateText 1, UpdateCaret 2, PressClose 4, PressEnter 5), the caret directions (left 1, right 2), the error codes and the text event payload follow the PS4 reimplementation in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/b72489d8c24e63fc56a29fe2c90120ab768a210d/src/core/libraries/ime/ime.cpp); none is measured on a PS5. Events are queued and delivered by `sceImeUpdate` only when it is passed the handler given to `sceImeOpen`, with that call's `arg`. A text event points `str` at a copy of the text kept in `work`, and `inputTextBuffer` holds the same text; both are terminated, `caret_index` is the caret after the edit and the one edit area has the edit position and the change in UTF-16 units (0 for Enter and Close). An edit sends one UpdateText; a caret move sends one UpdateCaret per UTF-16 unit, as shadPS4 does. Enter inserts a line break with the multiline option and sends PressEnter otherwise; Escape sends PressClose. Typed text is cut to `maxTextLength` without splitting a surrogate pair, line breaks are dropped without the multiline option, and the BasicLatin, URL, mail and number types do not filter characters. `sceImeOpen` checks, in shadPS4's order, the user, type, enter label, input method, option bits (the `sceImeGetPanelSize` mask), `maxTextLength` (1 to 2048), the text buffer, the position (below 1920x1080, or 3840x2160 with the over-2K option), the alignments, the extended priority and reserved bytes, `work` (non-null, 4-byte aligned), the handler, the reserved bytes and the initial text (no unpaired surrogate, no line break without the multiline option); shadPS4's SDK-dependent option, language and extended-option masks and its work-overlap check are not applied, and only the text copy is written to `work`. `sceImeSetText` keeps the caret unless the text became shorter, `sceImeSetCaret` rejects an index past the text with `0x80bc0030`, and neither sends an event. `sceImeSetTextGeometry` accepts any geometry while a panel is open, since no panel is drawn - [sceImeVshOpen](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeKeyboardUpdate](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshUpdate](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshUpdateContext](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshClearPreedit](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifySetText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetCaretGeometry](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshConfirmPreedit](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetCaretIndexInPreedit](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetPreeditGeometry](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetSelectGeometry](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshGetPanelPositionAndForm](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeSetCandidateIndex](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeConfirmCandidate](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeDisableController](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeSetVoiceDictationEnabled](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeSetEditEventWithRemotePlayEnabled](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeForTestFunction](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshInformConfirmdString2](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetSelectionText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeCheckUpdateTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeCheckRemoteEventParam](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeKeyboardOpenInternal](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshSetParam](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshUpdateContext2](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshSendTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature @@ -43,7 +28,6 @@ - [sceImeVshNotifyDeleteText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyUpdateCaretText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshCheckAutoReConvertingPreeditText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetPanelPosition](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyPreeditMetaInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigSet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigGet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature @@ -51,8 +35,6 @@ - [sceImeDicAddWord](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeDicDeleteWord](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeDicReplaceWord](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDicDeleteLearnDics](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDicDeleteUserDics](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [libSceIme pending exports](../../../core/libs/prx/libSceIme/Export.cpp) - 2 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceIme.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`. From cf0544a93bf52ae407fb278d80b87c03a11a52cd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:11:37 +0300 Subject: [PATCH 190/523] docs(json): narrow the remaining initialization debt --- docs/dev/debt/libSceJson2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 082a4f641b..12d1655e14 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -4,7 +4,7 @@ - [sce::Json::Parser::parse with a read callback](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the callback's contract is taken from PPSA04540's: it returns 0 with the next character, and at the end of its data writes `'\0'` and returns -1. Characters are read until the callback returns non-zero or gives `'\0'`, and the text read is then parsed as by the `(text, size)` overload, so trailing characters after the value are rejected as there. Whether the console stops reading at the end of the top-level value, and what it returns when the callback fails before the end, are unknown - [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer or zero size returns `JsonErrorParse` (0x80920101) without modifying the destination Value; whether the console distinguishes a null pointer from a syntax error with a separate parameter error code is unknown. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown -- [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - `InitParameterRtti` and RTTI allocation remain unverified. `InitParameterRtti2::setAllocatorRtti` writes the allocation parameters and user data where `setAllocator` writes the allocator, as both references do; whether the console also marks the object as using RTTI allocation is unknown. `InitParameterRtti`'s constructor writes the allocation parameters, user data and file buffer size to the first 24 bytes, an order inferred from its mangled arguments. The values of `SpecialFloatFormatType` and what they select are unknown, so `setSpecialFloatFormatType` stores any value and nothing reads it: `Initializer::initialize(InitParameter2)` ignores every field as `initialize(InitParameter)` does, serializing a non-finite real throws whatever the type, and whether the type lets the parser accept NaN or infinity is unknown. The setters return nothing; whether the console's return `this` is unknown +- [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - RTTI allocator adaptation and the `InitParameterRtti` layout remain unverified. `setAllocatorRtti` stores the raw allocation-parameter pointer as the allocator; `initialize` ignores allocator, buffer-size and special-float settings. Special-float enum meanings and parser support for non-finite numbers remain unresolved; serialization of non-finite values throws. Setter return types remain unverified. - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown From 9e87228eafb5c31966958ec0946283a7d86d59af Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:14:38 +0300 Subject: [PATCH 191/523] docs(debt): close verified hmd and pad parameterless signatures --- docs/dev/debt/libSceHmd.md | 6 ------ docs/dev/debt/libScePad.md | 2 -- 2 files changed, 8 deletions(-) diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 2e04c6b1e6..6305a8d945 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -23,7 +23,6 @@ - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionClearUserEventEnd](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportReadDataSize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -51,7 +50,6 @@ - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStopLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportReadData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -73,7 +71,6 @@ - [sceHmdInternalDfuSetMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetForcedCrash](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuCheckPartialUpdateAvailable](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSeparateModeClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetHmuPowerControlForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -93,7 +90,6 @@ - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDeviceConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionUnsetDisplayBuffers](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuOpticalParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVrTracker](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -102,7 +98,6 @@ - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuSend](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionClearUserEventStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMapSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetCrashDumpInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuReset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -115,7 +110,6 @@ - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalizeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetPortStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStop](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetFadeState](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index c52607d8ee..5a085d20d1 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -115,8 +115,6 @@ - [scePadIsMoveReproductionModel](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadAstonForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetAstonModel](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadMbusInit](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadMbusTerm](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadConnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadDisconnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadDisconnectDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From 5f1d9bafa2872407c119e20c07293e78ef7de49e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:16:40 +0300 Subject: [PATCH 192/523] fix(pad): recover controller settings signatures --- core/libs/prx/libScePad/Export.cpp | 28 ++++++++++++++-------------- docs/dev/debt/libScePad.md | 14 -------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 362519358f..ef58f94386 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -385,17 +385,17 @@ int APS5_VABI scePadSetUserColor(void) { return 0; } -int APS5_VABI scePadDisableVibration(void) { +int APS5_VABI scePadDisableVibration(std::int32_t handle, bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetVibrationStrength(void) { +int APS5_VABI scePadSetVibrationStrength(std::int32_t handle, std::uint32_t strength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetLightBarBaseBrightness(void) { +int APS5_VABI scePadSetLightBarBaseBrightness(std::int32_t handle, std::uint32_t brightness) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -415,37 +415,37 @@ int APS5_VABI scePadGetControllerIdleCount(void) { return 0; } -int APS5_VABI scePadSetPowerSavingState(void) { +int APS5_VABI scePadSetPowerSavingState(std::int32_t handle, std::uint8_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetAutoPowerOffCount(void) { +int APS5_VABI scePadSetAutoPowerOffCount(std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadEnableUsbConnection(void) { +int APS5_VABI scePadEnableUsbConnection(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadEnableAutoDetect(void) { +int APS5_VABI scePadEnableAutoDetect(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadEnablePlayPauseEmulation(void) { +int APS5_VABI scePadEnablePlayPauseEmulation(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadDisableTrackingLedBatterySaver(void) { +int APS5_VABI scePadDisableTrackingLedBatterySaver(bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetLoginUserNumber(void) { +int APS5_VABI scePadSetLoginUserNumber(std::int32_t number) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -510,12 +510,12 @@ int APS5_VABI scePadSwitchConnection(void) { return 0; } -int APS5_VABI scePadDisableTriggerEffect(void) { +int APS5_VABI scePadDisableTriggerEffect(std::int32_t handle, bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetTriggerEffectStrength(void) { +int APS5_VABI scePadSetTriggerEffectStrength(std::int32_t handle, std::uint32_t strength) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -525,12 +525,12 @@ int APS5_VABI scePadGetTriggerEffectInternalState(void) { return 0; } -int APS5_VABI scePadSetUserNumber(void) { +int APS5_VABI scePadSetUserNumber(std::int32_t handle, std::int32_t number) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadDisableTransportButtons(void) { +int APS5_VABI scePadDisableTransportButtons(bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 5a085d20d1..c6403b4866 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -22,19 +22,9 @@ - [scePadDeviceOpen](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDataInternal](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetUserColor](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableVibration](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetVibrationStrength](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLightBarBaseBrightness](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLightBarBlinking](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetIdleCount](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetControllerIdleCount](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetPowerSavingState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetAutoPowerOffCount](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnableUsbConnection](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnableAutoDetect](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnablePlayPauseEmulation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableTrackingLedBatterySaver](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLoginUserNumber](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetCapability](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadShareOutputData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadShareOutputDataWithFunctionType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature @@ -47,11 +37,7 @@ - [scePadSetForceIntercepted](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadIsLightBarBaseBrightnessControllable](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSwitchConnection](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableTriggerEffect](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetTriggerEffectStrength](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTriggerEffectInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetUserNumber](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableTransportButtons](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetEnd](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From adb500a3c1f3e5ec92f5498fa3abfb05fbeffb2a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:17:02 +0300 Subject: [PATCH 193/523] fix(http): recover the cookie statistics layout --- core/libs/prx/libSceHttp/Export.cpp | 14 +++++++++++++- docs/dev/debt/libSceHttp.md | 1 - 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index b923302dac..092e6d3fb5 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -31,6 +31,18 @@ struct RequestStatusRegistration { }; static std::mutex g_statusMutex; static std::map g_statusCallbacks; +struct HttpCookieStats { + std::size_t usedBytes; + std::uint32_t cookieCount; + std::uint32_t reserved; + std::size_t peakUsedBytes; + std::uint32_t peakCookieCount; + std::uint32_t evictedCookieCount; +}; + +static_assert(sizeof(HttpCookieStats) == 32); +static_assert(offsetof(HttpCookieStats, peakUsedBytes) == 16); + struct HttpMemoryPoolStats { std::size_t poolSize; std::size_t peakUsedSize; @@ -779,7 +791,7 @@ int APS5_VABI sceHttpSetCookieMaxNumPerDomain(int contextId, std::uint32_t count return 0; } -int APS5_VABI sceHttpGetCookieStats(void) { +int APS5_VABI sceHttpGetCookieStats(int id, HttpCookieStats* stats) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index d34df8365e..8163e0c50f 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -32,7 +32,6 @@ - [sceHttpSetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetCookieSendCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetCookieStats](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature From 891d790b6ab756851ede833210d51e31a1db01d3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:17:39 +0300 Subject: [PATCH 194/523] fix(pad): recover idle counters and device query signatures --- core/libs/prx/libScePad/Export.cpp | 24 ++++++++++++------------ docs/dev/debt/libScePad.md | 12 ------------ 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index ef58f94386..9ad0c2c049 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -356,11 +356,11 @@ int APS5_VABI scePadGetExtensionUnitInfo(void) { return 0; } -int APS5_VABI scePadEnableExtensionPort(void) { +int APS5_VABI scePadEnableExtensionPort(bool enabled) { return 0; } -int APS5_VABI scePadEnableSpecificDeviceClass(void) { +int APS5_VABI scePadEnableSpecificDeviceClass(std::int32_t deviceClass) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -380,7 +380,7 @@ int APS5_VABI scePadGetDataInternal(void) { return 0; } -int APS5_VABI scePadSetUserColor(void) { +int APS5_VABI scePadSetUserColor(std::int32_t handle, std::uint32_t color) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -405,12 +405,12 @@ int APS5_VABI scePadSetLightBarBlinking(void) { return 0; } -int APS5_VABI scePadGetIdleCount(void) { +std::uint64_t APS5_VABI scePadGetIdleCount(std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetControllerIdleCount(void) { +int APS5_VABI scePadGetControllerIdleCount(std::int32_t handle, std::uint64_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -465,7 +465,7 @@ int APS5_VABI scePadShareOutputDataWithFunctionType(void) { return 0; } -int APS5_VABI scePadSetVibrationForce(void) { +int APS5_VABI scePadSetVibrationForce(std::int32_t handle, const PadVibrationParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -475,12 +475,12 @@ int APS5_VABI scePadGetVersionInfo(void) { return 0; } -int APS5_VABI scePadGetControllerType(void) { +int APS5_VABI scePadGetControllerType(std::int32_t handle, std::int32_t* type) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadDisableDataConversion(void) { +int APS5_VABI scePadDisableDataConversion(std::int32_t handle, bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -490,22 +490,22 @@ int APS5_VABI scePadSetButtonRemappingInfo(void) { return 0; } -int APS5_VABI scePadSetProcessPrivilegeOfButtonRemapping(void) { +int APS5_VABI scePadSetProcessPrivilegeOfButtonRemapping(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetForceIntercepted(void) { +int APS5_VABI scePadSetForceIntercepted(bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadIsLightBarBaseBrightnessControllable(void) { +int APS5_VABI scePadIsLightBarBaseBrightnessControllable(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSwitchConnection(void) { +int APS5_VABI scePadSwitchConnection(std::int32_t handle, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index c6403b4866..a1fe12fd15 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -16,27 +16,15 @@ - [scePadReadStateExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetExtensionReport](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetExtensionUnitInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnableExtensionPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnableSpecificDeviceClass](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadOpenExt2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadDeviceOpen](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDataInternal](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetUserColor](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLightBarBlinking](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetIdleCount](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetControllerIdleCount](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetCapability](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadShareOutputData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadShareOutputDataWithFunctionType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetVibrationForce](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetVersionInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetControllerType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableDataConversion](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetButtonRemappingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetProcessPrivilegeOfButtonRemapping](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetForceIntercepted](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsLightBarBaseBrightnessControllable](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSwitchConnection](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTriggerEffectInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From 482c0e92d4db4312dd6d51b2965d6f3c872dbd44 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:18:48 +0300 Subject: [PATCH 195/523] fix(pad): recover profile and copilot control signatures --- core/libs/prx/libScePad/Export.cpp | 26 +++++++++++++------------- docs/dev/debt/libScePad.md | 13 ------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 9ad0c2c049..3332bcc0f5 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -680,57 +680,57 @@ int APS5_VABI scePadResetLeonardo(void) { return 0; } -int APS5_VABI scePadSetProfileEditMode(void) { +int APS5_VABI scePadSetProfileEditMode(std::int32_t handle, bool enabled, std::uint32_t profile) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetProfileEditMode(void) { +int APS5_VABI scePadGetProfileEditMode(std::int32_t handle, std::int32_t* mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetProfileEditModeLeonardo(void) { +int APS5_VABI scePadGetProfileEditModeLeonardo(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetSelectButtonCustomizeMode(void) { +int APS5_VABI scePadSetSelectButtonCustomizeMode(std::int32_t handle, bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetSelectButtonCustomizeMode(void) { +int APS5_VABI scePadGetSelectButtonCustomizeMode(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetToggleAllReleaseLeonardo(void) { +int APS5_VABI scePadSetToggleAllReleaseLeonardo(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadEnableControllerIndicator(void) { +int APS5_VABI scePadEnableControllerIndicator(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadEnableProfileIndicator(void) { +int APS5_VABI scePadEnableProfileIndicator(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetProfileIndicatorAnimation(void) { +int APS5_VABI scePadSetProfileIndicatorAnimation(std::int32_t handle, std::uint8_t animation) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadIsCoPilot(void) { +int APS5_VABI scePadIsCoPilot(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetIsCoPilot(void) { +int APS5_VABI scePadSetIsCoPilot(std::int32_t handle, bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -745,12 +745,12 @@ int APS5_VABI scePadGetTelemetryInfo(void) { return 0; } -int APS5_VABI scePadGetActiveTime(void) { +int APS5_VABI scePadGetActiveTime(std::int32_t handle, std::int32_t type, std::uint64_t* activeTime) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetDeviceId(void) { +int APS5_VABI scePadGetDeviceId(std::int32_t handle, std::int32_t* deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index a1fe12fd15..8bb42751b0 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -55,21 +55,8 @@ - [scePadGetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadResetLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetProfileEditMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetProfileEditMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetProfileEditModeLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetSelectButtonCustomizeMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetSelectButtonCustomizeMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetToggleAllReleaseLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnableControllerIndicator](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnableProfileIndicator](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetProfileIndicatorAnimation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsCoPilot](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetIsCoPilot](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRegisterCollaboInputMgrProcess](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetActiveTime](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetDeviceId](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadResetLightBarAll](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadResetLightBarAllByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From b878641d266a798248df58c08d9e0762219c37d4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:19:41 +0300 Subject: [PATCH 196/523] fix(pad): recover tracker and connection query signatures --- core/libs/prx/libScePad/Export.cpp | 32 +++++++++++++++++++----------- docs/dev/debt/libScePad.md | 13 ------------ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 3332bcc0f5..237a434d01 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -27,6 +27,14 @@ bool ValidPort(int userId, int type, int index) { } +struct PadMotionSensorPosition { + float x; + float y; + float z; +}; + +static_assert(sizeof(PadMotionSensorPosition) == 12); + extern "C" { int APS5_VABI scePadClose_nid_postfix(int handle) { @@ -765,7 +773,7 @@ int APS5_VABI scePadResetLightBarAll(void) { return 0; } -int APS5_VABI scePadResetLightBarAllByPortType(void) { +int APS5_VABI scePadResetLightBarAllByPortType(std::uint32_t portType) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -785,47 +793,47 @@ int APS5_VABI scePadReadBlasterForTracker(void) { return 0; } -int APS5_VABI scePadResetOrientationForTracker(void) { +int APS5_VABI scePadResetOrientationForTracker(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetMotionTimerUnit(void) { +int APS5_VABI scePadGetMotionTimerUnit(std::int32_t handle, float* unit) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetVrTrackingMode(void) { +int APS5_VABI scePadSetVrTrackingMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetMotionSensorPosition(void) { +int APS5_VABI scePadGetMotionSensorPosition(std::int32_t handle, PadMotionSensorPosition* position) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetSphereRadius(void) { +int APS5_VABI scePadGetSphereRadius(std::int32_t handle, float* radius) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadIsValidHandle(void) { +bool APS5_VABI scePadIsValidHandle(std::int32_t handle, std::int32_t portType) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadIsDS4Connected(void) { +bool APS5_VABI scePadIsDS4Connected(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadIsBlasterConnected(void) { +bool APS5_VABI scePadIsBlasterConnected(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadIsMoveConnected(void) { +bool APS5_VABI scePadIsMoveConnected(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -835,7 +843,7 @@ int APS5_VABI scePadGetBluetoothAddress(void) { return 0; } -int APS5_VABI scePadIsMoveReproductionModel(void) { +int APS5_VABI scePadIsMoveReproductionModel(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -845,7 +853,7 @@ int APS5_VABI scePadReadAstonForTracker(void) { return 0; } -int APS5_VABI scePadGetAstonModel(void) { +int APS5_VABI scePadGetAstonModel(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 8bb42751b0..d39050b7cb 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -58,24 +58,11 @@ - [scePadRegisterCollaboInputMgrProcess](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadResetLightBarAll](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadResetLightBarAllByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLightBarForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadBlasterForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadResetOrientationForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetMotionTimerUnit](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetVrTrackingMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetMotionSensorPosition](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetSphereRadius](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsValidHandle](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsDS4Connected](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsBlasterConnected](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsMoveConnected](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetBluetoothAddress](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadIsMoveReproductionModel](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadAstonForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetAstonModel](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadConnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadDisconnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadDisconnectDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From ae33ab4bb1b2be17e4e4c2f73410a3fb842a9897 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:20:31 +0300 Subject: [PATCH 197/523] fix(pad): recover feature report and compatibility signatures --- core/libs/prx/libScePad/Export.cpp | 20 ++++++++++---------- docs/dev/debt/libScePad.md | 12 ------------ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 237a434d01..12193b5057 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -873,12 +873,12 @@ int APS5_VABI scePadConnectPort(void) { return 0; } -int APS5_VABI scePadDisconnectPort(void) { +int APS5_VABI scePadDisconnectPort(std::int32_t userId, std::int32_t portType) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadDisconnectDevice(void) { +int APS5_VABI scePadDisconnectDevice(std::int32_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -903,7 +903,7 @@ int APS5_VABI scePadVirtualDeviceAddDevice(void) { return 0; } -int APS5_VABI scePadVirtualDeviceDeleteDevice(void) { +int APS5_VABI scePadVirtualDeviceDeleteDevice(std::int32_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -928,17 +928,17 @@ int APS5_VABI scePadVirtualDeviceSetTriggerEffectSetting(void) { return 0; } -int APS5_VABI scePadVirtualDeviceDisableButtonRemapping(void) { +int APS5_VABI scePadVirtualDeviceDisableButtonRemapping(bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetShareButtonMaskForRemotePlay(void) { +int APS5_VABI scePadSetShareButtonMaskForRemotePlay(std::uint8_t mask) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetPS4BcVibrationMode(void) { +int APS5_VABI scePadSetPS4BcVibrationMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -948,7 +948,7 @@ int APS5_VABI scePadSetVirtualDeviceComponentInformation(void) { return 0; } -int APS5_VABI scePadEnablePs4CompatibleMode(void) { +int APS5_VABI scePadEnablePs4CompatibleMode(std::int32_t handle, bool enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -983,17 +983,17 @@ int APS5_VABI scePadRemoteControlInsertDataForAston(void) { return 0; } -int APS5_VABI scePadGetFeatureReport2(void) { +int APS5_VABI scePadGetFeatureReport2(std::int32_t handle, std::uint8_t reportId, void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetFeatureReport2(void) { +int APS5_VABI scePadSetFeatureReport2(std::int32_t handle, std::uint8_t reportId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetMuteLed(void) { +int APS5_VABI scePadSetMuteLed(std::int32_t handle, std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index d39050b7cb..9cadbed787 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -64,31 +64,19 @@ - [scePadGetBluetoothAddress](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadAstonForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadConnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisconnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisconnectDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDeviceInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProcessFocus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVertualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceDeleteDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceInsertTriggerInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceGetRemoteSetting](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceSetTriggerEffectSetting](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceDisableButtonRemapping](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetShareButtonMaskForRemotePlay](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetPS4BcVibrationMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetVirtualDeviceComponentInformation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadEnablePs4CompatibleMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetLicenseControllerInformation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadHistory](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadStartRecording](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadStopRecording](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertDataForAston](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetFeatureReport2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetFeatureReport2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetMuteLed](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadBondUpdateInit](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadBondUpdateIsReady](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadBondUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From 7c741db5efc918a0a74ee3ef69ba1524721dfabb Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:24:24 +0300 Subject: [PATCH 198/523] fix(pad): recover controller update status and data signatures --- core/libs/prx/libScePad/Export.cpp | 28 ++++++++++++++++++---------- docs/dev/debt/libScePad.md | 14 -------------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 12193b5057..7a4390c091 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -27,6 +27,14 @@ bool ValidPort(int userId, int type, int index) { } +struct PadUpdateData { + std::size_t size; + std::uint8_t data[32768]; +}; + +static_assert(sizeof(PadUpdateData) == 32776); +static_assert(offsetof(PadUpdateData, data) == 8); + struct PadMotionSensorPosition { float x; float y; @@ -1003,7 +1011,7 @@ int APS5_VABI scePadBondUpdateInit(void) { return 0; } -int APS5_VABI scePadBondUpdateIsReady(void) { +int APS5_VABI scePadBondUpdateIsReady(std::int32_t deviceId, std::int32_t* ready) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1013,22 +1021,22 @@ int APS5_VABI scePadBondUpdateStart(void) { return 0; } -int APS5_VABI scePadBondUpdateGetStatus(void) { +int APS5_VABI scePadBondUpdateGetStatus(std::int32_t deviceId, std::int32_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadBondUpdateSendData(void) { +int APS5_VABI scePadBondUpdateSendData(std::int32_t deviceId, const PadUpdateData* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadBondUpdateResetBond(void) { +int APS5_VABI scePadBondUpdateResetBond(std::int32_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadBondUpdateGetReconnectStatus(void) { +int APS5_VABI scePadBondUpdateGetReconnectStatus(std::int32_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1048,7 +1056,7 @@ int APS5_VABI scePadControllerUpdateIsUpdateNeeded2(void) { return 0; } -int APS5_VABI scePadControllerUpdateIsReady(void) { +int APS5_VABI scePadControllerUpdateIsReady(std::int32_t deviceId, std::int32_t* ready) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1058,22 +1066,22 @@ int APS5_VABI scePadControllerUpdateStart(void) { return 0; } -int APS5_VABI scePadControllerUpdateGetStatus(void) { +int APS5_VABI scePadControllerUpdateGetStatus(std::int32_t deviceId, std::int32_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadControllerUpdateSendData(void) { +int APS5_VABI scePadControllerUpdateSendData(std::int32_t deviceId, const PadUpdateData* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadControllerUpdateResetController(void) { +int APS5_VABI scePadControllerUpdateResetController(std::int32_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadControllerUpdateGetReconnectStatus(void) { +int APS5_VABI scePadControllerUpdateGetReconnectStatus(std::int32_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 9cadbed787..7040a5d940 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -77,23 +77,9 @@ - [scePadReadHistory](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertDataForAston](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateInit](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateIsReady](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadBondUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateGetStatus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateSendData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateResetBond](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateGetReconnectStatus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateTerm](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateInit](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadControllerUpdateIsUpdateNeeded2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateIsReady](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadControllerUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateGetStatus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateSendData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateResetController](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateGetReconnectStatus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateTerm](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature ### Functional From fa38f4e2afdb5345e31ed22e691fbac6f8abf937 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:25:55 +0300 Subject: [PATCH 199/523] fix(pad): recover profile selection and infrared status signatures --- core/libs/prx/libScePad/Export.cpp | 16 ++++++++-------- docs/dev/debt/libScePad.md | 9 --------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 7a4390c091..f8565dd031 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -561,12 +561,12 @@ int APS5_VABI scePadSetLotusIrCodeSetData(void) { return 0; } -int APS5_VABI scePadSetLotusIrCodeSetEnd(void) { +int APS5_VABI scePadSetLotusIrCodeSetEnd(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetLotusIrCodeSetResult(void) { +int APS5_VABI scePadGetLotusIrCodeSetResult(std::int32_t handle, std::int32_t* result, std::uint8_t* deviceResult) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -596,7 +596,7 @@ int APS5_VABI scePadUiReadStateForTuning(void) { return 0; } -int APS5_VABI scePadSetStickTriggerTuningMode(void) { +int APS5_VABI scePadSetStickTriggerTuningMode(std::int32_t handle, std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -636,17 +636,17 @@ int APS5_VABI scePadGetActiveProfileInfo(void) { return 0; } -int APS5_VABI scePadDisableActiveProfileChange(void) { +int APS5_VABI scePadDisableActiveProfileChange(std::int32_t handle, bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadDisableActiveProfileChangeByDeviceId(void) { +int APS5_VABI scePadDisableActiveProfileChangeByDeviceId(std::int32_t deviceId, bool disabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetActiveProfileSlot(void) { +int APS5_VABI scePadSetActiveProfileSlot(std::int32_t deviceId, std::uint32_t slot) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -671,12 +671,12 @@ int APS5_VABI scePadParseUniqueDataLeonardo(void) { return 0; } -int APS5_VABI scePadGetControllerTypeLeonardo(void) { +int APS5_VABI scePadGetControllerTypeLeonardo(std::int32_t deviceId, std::int32_t* controllerType) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetControllerTypeLeonardo(void) { +int APS5_VABI scePadSetControllerTypeLeonardo(std::int32_t deviceId, std::int32_t controllerType) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 7040a5d940..661d3608d2 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -28,14 +28,11 @@ - [scePadGetTriggerEffectInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusIrCodeSetData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLotusIrCodeSetEnd](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetLotusIrCodeSetResult](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusEnableIrKey](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLotusDisableIrKey](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetUsbDetectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadUiReadState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadUiReadStateForTuning](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetStickTriggerTuningMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetStickTuningParam](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetTriggerTuningParam](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetProfileHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature @@ -43,19 +40,13 @@ - [scePadResetHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetProfileInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetActiveProfileInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableActiveProfileChange](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDisableActiveProfileChangeByDeviceId](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetActiveProfileSlot](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetSavedSettingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetSavedSettingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVrControllerGetPhysicalUsbConnectionStatus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadParseUniqueDataLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetControllerTypeLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetControllerTypeLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadResetLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadRegisterCollaboInputMgrProcess](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLightBarForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From 4e067caa65605740a961908cd2e8a3377ab62a03 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:26:54 +0300 Subject: [PATCH 200/523] fix(pad): recover infrared transfer signatures --- core/libs/prx/libScePad/Export.cpp | 8 ++++---- docs/dev/debt/libScePad.md | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index f8565dd031..3411748f9d 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -551,12 +551,12 @@ int APS5_VABI scePadDisableTransportButtons(bool disabled) { return 0; } -int APS5_VABI scePadSetLotusIrCodeSetStart(void) { +int APS5_VABI scePadSetLotusIrCodeSetStart(std::int32_t handle, std::uint8_t codeSet, std::uint16_t code, std::size_t dataSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetLotusIrCodeSetData(void) { +int APS5_VABI scePadSetLotusIrCodeSetData(std::int32_t handle, std::uint8_t packetIndex, const void* data, std::size_t dataSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -571,12 +571,12 @@ int APS5_VABI scePadGetLotusIrCodeSetResult(std::int32_t handle, std::int32_t* r return 0; } -int APS5_VABI scePadSetLotusEnableIrKey(void) { +int APS5_VABI scePadSetLotusEnableIrKey(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetLotusDisableIrKey(void) { +int APS5_VABI scePadSetLotusDisableIrKey(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 661d3608d2..3f570f61dd 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -26,10 +26,6 @@ - [scePadGetVersionInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetButtonRemappingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTriggerEffectInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLotusIrCodeSetStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLotusIrCodeSetData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLotusEnableIrKey](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLotusDisableIrKey](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetUsbDetectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadUiReadState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadUiReadStateForTuning](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From d25257b90becd72f545d22a0694bdca6f201a798 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:28:04 +0300 Subject: [PATCH 201/523] fix(pad): recover capability and tracker color signatures --- core/libs/prx/libScePad/Export.cpp | 6 +++--- docs/dev/debt/libScePad.md | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 3411748f9d..61298f177b 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -466,7 +466,7 @@ int APS5_VABI scePadSetLoginUserNumber(std::int32_t number) { return 0; } -int APS5_VABI scePadGetCapability(void) { +int APS5_VABI scePadGetCapability(std::int32_t deviceId, std::uint32_t* capability) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -786,7 +786,7 @@ int APS5_VABI scePadResetLightBarAllByPortType(std::uint32_t portType) { return 0; } -int APS5_VABI scePadSetLightBarForTracker(void) { +int APS5_VABI scePadSetLightBarForTracker(std::int32_t handle, const PadLightBarParam* color) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -846,7 +846,7 @@ bool APS5_VABI scePadIsMoveConnected(std::int32_t handle) { return 0; } -int APS5_VABI scePadGetBluetoothAddress(void) { +int APS5_VABI scePadGetBluetoothAddress(std::int32_t handle, std::uint64_t* address) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 3f570f61dd..c4912cb4fd 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -20,7 +20,6 @@ - [scePadDeviceOpen](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDataInternal](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLightBarBlinking](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetCapability](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadShareOutputData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadShareOutputDataWithFunctionType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetVersionInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature @@ -45,10 +44,8 @@ - [scePadResetLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLightBarForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadBlasterForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetBluetoothAddress](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadAstonForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadConnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDeviceInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From a1818c1ed0aec0419f9ade16a93e97bd32d38925 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:30:22 +0300 Subject: [PATCH 202/523] fix(json): preserve system object key ordering --- core/libs/prx/libSceJson2/Export.cpp | 38 ++++++++++++-------- core/libs/tests/GuestJson.cpp | 52 +++++++++++++++++++++++++--- docs/dev/debt/libSceJson2.md | 1 - 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 93e460c734..e4d7e67624 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -333,6 +333,10 @@ void Serialize(std::string& out, const Node& n) { throw std::runtime_error("sce::Json: invalid value type"); } +std::list::iterator ObjectPosition(const Object& object, const std::string& key) { + return std::find_if(object.items->begin(), object.items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key.c_str()) >= 0; }); +} + class Parser { public: Parser(const char* text, std::size_t size, const Handlers& handlers) : _text(text), _end(text + size), _handlers(handlers) {} @@ -502,11 +506,10 @@ private: if (_text >= _end || *_text != ':') return false; ++_text; skip(); - Pair pair{}; + auto& pair = *out.object.items->emplace(ObjectPosition(out.object, key)); Construct(pair.key, key); Construct(pair.value); pair.value.node->handlers = _handlers; - out.object.items->push_back(pair); if (!value(*pair.value.node)) return false; skip(); if (_text < _end && *_text == ',') { ++_text; continue; } @@ -532,13 +535,18 @@ void* const JsonMemAllocatorVtable[] = { }; Value& ObjectEntry(Object& object, const std::string& key) { - for (auto& pair : *object.items) - if (*pair.key.text == key) return pair.value; + auto position = ObjectPosition(object, key); + if (position != object.items->end() && std::strcmp(position->key.text->c_str(), key.c_str()) == 0) return position->value; Pair pair{}; Construct(pair.key, key); - Construct(pair.value); - object.items->push_back(pair); - return object.items->back().value; + try { + Construct(pair.value); + return object.items->insert(position, pair)->value; + } catch (...) { + Destroy(pair.value); + Destroy(pair.key); + throw; + } } } @@ -718,7 +726,7 @@ ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iteratorppEv(ObjectIterator* self) Pair* APS5_VABI _ZNK3sce4Json6Object8iteratordeEv(const ObjectIterator* self) { return &*self->it; } Pair* APS5_VABI _ZNK3sce4Json6Object8iteratorptEv(const ObjectIterator* self) { return &*self->it; } ObjectIterator* APS5_VABI _ZNK3sce4Json6Object4findERKNS0_6StringE(ObjectIterator* result, const Object* self, const String* key) { - result->it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return *pair.key.text == *key->text; }); + result->it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key->text->c_str()) == 0; }); return result; } bool APS5_VABI _ZNK3sce4Json6Object8iteratorneERKS2_(const ObjectIterator* self, const ObjectIterator* other) { return self->it != other->it; } @@ -732,7 +740,7 @@ bool APS5_VABI _ZNK3sce4Json6Object5emptyEv(const Object* self) { return self->i std::size_t APS5_VABI _ZNK3sce4Json6Object4sizeEv(const Object* self) { return self->items->size(); } void APS5_VABI _ZN3sce4Json6Object5eraseERKNS0_6StringE(Object* self, const String* key) { if (key == nullptr || key->text == nullptr) throw std::invalid_argument("sce::Json::Object::erase: null key"); - auto it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return *pair.key.text == *key->text; }); + auto it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key->text->c_str()) == 0; }); if (it == self->items->end()) return; Destroy(it->key); Destroy(it->value); @@ -857,7 +865,7 @@ const Value* APS5_VABI _ZNK3sce4Json5ValueixEPKc(const Value* self, const char* const Node& n = NodeOf(*self); if (key != nullptr && n.type == TypeObject) for (const auto& pair : *n.object.items) - if (*pair.key.text == key) return &pair.value; + if (std::strcmp(pair.key.text->c_str(), key) == 0) return &pair.value; return &MissingElement(self); } const Value* APS5_VABI _ZNK3sce4Json5ValueixEm(const Value* self, std::size_t index) { @@ -877,7 +885,7 @@ const Value* APS5_VABI _ZNK3sce4Json5Value8getValueERKNS0_6StringE(const Value* const Node& n = NodeOf(*self); if (key != nullptr && n.type == TypeObject) for (const auto& pair : *n.object.items) - if (*pair.key.text == *key->text) return &pair.value; + if (std::strcmp(pair.key.text->c_str(), key->text->c_str()) == 0) return &pair.value; return &MissingElement(self); } Value* APS5_VABI _ZN3sce4Json5Value10referValueEm(Value* self, std::size_t index) { @@ -891,7 +899,7 @@ Value* APS5_VABI _ZN3sce4Json5Value10referValueERKNS0_6StringE(Value* self, cons Node& n = NodeOf(*self); if (key == nullptr || n.type != TypeObject) return nullptr; for (auto& pair : *n.object.items) - if (*pair.key.text == *key->text) return &pair.value; + if (std::strcmp(pair.key.text->c_str(), key->text->c_str()) == 0) return &pair.value; return nullptr; } Array* APS5_VABI _ZN3sce4Json5Value10referArrayEv(Value* self) { @@ -1089,8 +1097,8 @@ void APS5_VABI _ZN3sce4Json6Object4PairD1Ev(Pair* self) { } ObjectIterator* APS5_VABI _ZN3sce4Json6Object6insertERKNS1_4PairE(ObjectIterator* result, Object* self, const Pair* pair) { - auto it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& p) { return *p.key.text == *pair->key.text; }); - if (it != self->items->end()) { + auto it = ObjectPosition(*self, *pair->key.text); + if (it != self->items->end() && std::strcmp(it->key.text->c_str(), pair->key.text->c_str()) == 0) { result->it = it; return result; } @@ -1099,7 +1107,7 @@ ObjectIterator* APS5_VABI _ZN3sce4Json6Object6insertERKNS1_4PairE(ObjectIterator Construct(copy.key, *pair->key.text); try { CopyInto(copy.value, pair->value); - result->it = self->items->insert(self->items->end(), copy); + result->it = self->items->insert(it, copy); return result; } catch (...) { Destroy(copy.value); diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index 1645862b04..f18fbcdec8 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -232,8 +232,8 @@ static void ParseAndRoundTrip() { Require(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5ValueixEm(_ZNK3sce4Json5ValueixEm(&list, 2), 0)) == 3); const std::string serialized = Serialize(root); - Require(serialized == "{\"name\":\"x\xc3\xa9\\n\",\"count\":3,\"big\":18446744073709551615,\"neg\":-5," - "\"pi\":1.5,\"ok\":true,\"none\":null,\"list\":[1,\"two\",[3]]}"); + Require(serialized == "{\"big\":18446744073709551615,\"count\":3,\"list\":[1,\"two\",[3]]," + "\"name\":\"x\xc3\xa9\\n\",\"neg\":-5,\"none\":null,\"ok\":true,\"pi\":1.5}"); Value reparsed{}; _ZN3sce4Json5ValueC1Ev(&reparsed); Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&reparsed, serialized.c_str(), serialized.size()) == 0); @@ -571,6 +571,11 @@ static void ObjectInsert() { Iterator otherInserted{}; Require(_ZN3sce4Json6Object6insertERKNS1_4PairE(&otherInserted, &object, &otherPair) == &otherInserted); Require(_ZNK3sce4Json6Object4sizeEv(&object) == 2); + Iterator first{}; + _ZNK3sce4Json6Object5beginEv(&first, &object); + Require(_ZNK3sce4Json6Object8iteratoreqERKS2_(&first, &otherInserted)); + Require(*_ZNK3sce4Json5Value10getIntegerEv(&_ZNK3sce4Json6Object8iteratordeEv(&inserted)->value) == 100); + _ZN3sce4Json6Object8iteratorD1Ev(&first); _ZN3sce4Json6Object8iteratorD1Ev(&otherInserted); _ZN3sce4Json6Object4PairD1Ev(&otherPair); @@ -584,6 +589,37 @@ static void ObjectInsert() { _ZN3sce4Json6ObjectD1Ev(&object); } +static void ObjectKeyOrder() { + Object object{}; + _ZN3sce4Json6ObjectC1Ev(&object); + for (const char* name : {"z", "\xc3\xa9", "a", "A", ""}) { + String key{}; + _ZN3sce4Json6StringC1EPKc(&key, name); + _ZN3sce4Json6ObjectixERKNS0_6StringE(&object, &key); + _ZN3sce4Json6StringD1Ev(&key); + } + Iterator member{}; + _ZNK3sce4Json6Object5beginEv(&member, &object); + for (const char* expected : {"", "A", "a", "z", "\xc3\xa9"}) { + Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(&_ZNK3sce4Json6Object8iteratordeEv(&member)->key), expected) == 0); + _ZN3sce4Json6Object8iteratorppEv(&member); + } + _ZN3sce4Json6Object8iteratorD1Ev(&member); + String key{}; + _ZN3sce4Json6StringC1EPKc(&key, "a"); + Value* existing = _ZN3sce4Json6ObjectixERKNS0_6StringE(&object, &key); + _ZN3sce4Json6String6appendEPKcm(&key, "\0suffix", 7); + Require(_ZN3sce4Json6ObjectixERKNS0_6StringE(&object, &key) == existing); + _ZNK3sce4Json6Object4findERKNS0_6StringE(&member, &object, &key); + Require(&_ZNK3sce4Json6Object8iteratordeEv(&member)->value == existing); + _ZN3sce4Json6Object8iteratorD1Ev(&member); + Require(_ZNK3sce4Json6Object4sizeEv(&object) == 5); + _ZN3sce4Json6Object5eraseERKNS0_6StringE(&object, &key); + Require(_ZNK3sce4Json6Object4sizeEv(&object) == 4); + _ZN3sce4Json6StringD1Ev(&key); + _ZN3sce4Json6ObjectD1Ev(&object); +} + static void StringMethods() { Require(_ZN3sce4Json6String4nposE == static_cast(-1)); const auto text = [](const String& string) { return std::string(_ZNK3sce4Json6String5c_strEv(&string), _ZNK3sce4Json6String6lengthEv(&string)); }; @@ -689,11 +725,11 @@ static void ObjectOperations() { _ZN3sce4Json6Object4PairC1ERKNS0_6StringERKNS0_5ValueE(&p2, &k2, &v2); _ZN3sce4Json6Object4PairC1ERKNS0_6StringERKNS0_5ValueE(&p3, &k3, &v3); Iterator ins{}; - _ZN3sce4Json6Object6insertERKNS1_4PairE(&ins, &object, &p1); + _ZN3sce4Json6Object6insertERKNS1_4PairE(&ins, &object, &p3); _ZN3sce4Json6Object8iteratorD1Ev(&ins); _ZN3sce4Json6Object6insertERKNS1_4PairE(&ins, &object, &p2); _ZN3sce4Json6Object8iteratorD1Ev(&ins); - _ZN3sce4Json6Object6insertERKNS1_4PairE(&ins, &object, &p3); + _ZN3sce4Json6Object6insertERKNS1_4PairE(&ins, &object, &p1); _ZN3sce4Json6Object8iteratorD1Ev(&ins); Iterator begin{}; @@ -779,6 +815,13 @@ static void ValueAccess() { const char text[] = "{\"list\":[10,20,30],\"name\":\"value\",\"flag\":true}"; Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, text, sizeof(text) - 1) == 0); Require(_ZNK3sce4Json6Object4sizeEv(_ZNK3sce4Json5Value9getObjectEv(&root)) == 3); + Iterator member{}; + _ZNK3sce4Json6Object5beginEv(&member, _ZNK3sce4Json5Value9getObjectEv(&root)); + for (const char* expected : {"flag", "list", "name"}) { + Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(&_ZNK3sce4Json6Object8iteratordeEv(&member)->key), expected) == 0); + _ZN3sce4Json6Object8iteratorppEv(&member); + } + _ZN3sce4Json6Object8iteratorD1Ev(&member); String key{}; _ZN3sce4Json6StringC1EPKc(&key, "list"); @@ -1232,6 +1275,7 @@ int main() { ObjectFind(); ObjectPair(); ObjectInsert(); + ObjectKeyOrder(); ArrayIteratorCopies(); ObjectIteratorCopies(); AllocatorNotifyError(); diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 12d1655e14..86b6444e91 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -10,4 +10,3 @@ - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown - [sce::Json::String size, capacity, resize, compare and operator==(const String&), append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - callback dispatch before conversion is not implemented. The system library invokes the applicable type mismatch handler before converting a non-boolean value, with SDK-dependent global callback handling. -- [sce::Json::Object::insert](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - new keys are appended in insertion order; the system library uses key order. Duplicate insertion preserves the existing value. From 787318c3d86c514b480fbaca607849038fe24e3e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:32:15 +0300 Subject: [PATCH 203/523] fix(pad): recover vr vibration and update query signatures --- core/libs/prx/libScePad/Export.cpp | 8 ++++---- docs/dev/debt/libScePad.md | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 61298f177b..c97a1e3c23 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -53,12 +53,12 @@ int APS5_VABI scePadClose_nid_postfix(int handle) { return PAD_OK; } -int APS5_VABI scePadVrControllerSetVibration() { +int APS5_VABI scePadVrControllerSetVibration(std::int32_t handle, std::uint8_t intensity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVrControllerSetVibrationMode() { +int APS5_VABI scePadVrControllerSetVibrationMode(std::int32_t handle, std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -876,7 +876,7 @@ int APS5_VABI scePadMbusTerm(void) { return 0; } -int APS5_VABI scePadConnectPort(void) { +int APS5_VABI scePadConnectPort(std::int32_t userId, std::int32_t portType) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1051,7 +1051,7 @@ int APS5_VABI scePadControllerUpdateInit(void) { return 0; } -int APS5_VABI scePadControllerUpdateIsUpdateNeeded2(void) { +int APS5_VABI scePadControllerUpdateIsUpdateNeeded2(std::int32_t deviceId, const std::uint32_t* version, std::uint16_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index c4912cb4fd..738e21a092 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -6,7 +6,7 @@ - [scePadSetAngularVelocityDeadbandState](../../../core/libs/prx/libScePad/src/PadState.cpp) (libScePad) - with the deadband on, each angular velocity axis whose magnitude is below 0.017453292 rad/s (1 degree per second) reads as 0, before the orientation is integrated; it starts off. Threshold, per-axis test and default follow [duaLib](https://github.com/WujekFoliarz/duaLib/blob/03bad1bea2a36561b520846776f2a07ced6773e0/src/source/duaLib.cpp#L44), which reimplements the PC `libScePad.dll`; the console's threshold is not measured, and the deadband is kept per library rather than per handle since one pad handle exists - [scePadOpen and scePadOpenExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - special ports (type 2) return `DEVICE_NOT_CONNECTED` (0x80920007) because the host has no special controller, and `scePadGetHandle` has no handle for them; whether the console opens a handle and reports the device as disconnected later is unknown. scePadOpenExt handles only special ports. PPSA01317 passes a 16-byte param that names the device by vendor and product id (`b7 0e 04 0e ...` for 0x0eb7:0x0e04) and skips the user on any negative result. Other port types throw - [scePadVrControllerReadState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `iA-DdobUen8`) - the name hashes to the imported NID; the `(int handle, void* state)` signature follows [PS5VR](https://github.com/theghostonline/PS5VR/blob/0e408909ce7a39891a2e06eee3438d07a09e11e3/app/src/app_vr.c#L326-L369) (PPSA99177), which passes a PS VR2 Sense controller handle from `scePadOpen` type 3 or 4 and a 0x68-byte state it reads as buttons at 0, stick bytes at 4 and a connected byte at 0x40. The title calls it only after `sceHmd2Initialize` succeeds, which it does not here, so every call throws; the state layout is otherwise unknown -- PPSA07411 PS VR2 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [scePadVrControllerSetVibration, scePadVrControllerSetVibrationMode and scePadVrControllerReadCombinedState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) and [sceVrTracker2ResetLocalCoordinate](../../../core/libs/prx/libSceVrTracker2/Export.cpp) (libSceVrTracker2) +- PPSA07411 PS VR2 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [scePadVrControllerReadCombinedState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) and [sceVrTracker2ResetLocalCoordinate](../../../core/libs/prx/libSceVrTracker2/Export.cpp) (libSceVrTracker2) - [scePadSetVibrationMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - modes 1 to 3 and 5 are accepted and stored. On a PS5 (firmware 13.60) the call returns `0x80920005` before `scePadInit`, then rejects a mode of 0 or below with `INVALID_ARG` before it checks the handle, and returns `INVALID_HANDLE` for a bad handle with any higher mode; whether an open handle accepts modes above 3 was not measured, since the payload could not open a pad; 5 is accepted because PPSA25286 passes it on its open handle at start-up; the meanings of modes 3 and 5 are unknown (PPSA21480 calls it with 3 right after `scePadOpen` and aborts on failure; [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Pad/Pad.cs) names only 1 and 2). The stored mode has no other effect - [scePadIsRemoteController](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false - [scePadSetAngularVelocityBiasCorrectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled @@ -47,7 +47,6 @@ - [scePadReadForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadBlasterForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadAstonForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadConnectPort](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDeviceInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProcessFocus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVertualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature @@ -62,7 +61,6 @@ - [scePadRemoteControlInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertDataForAston](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadBondUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateIsUpdateNeeded2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadControllerUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature ### Functional From 1d9ba432df0e80f873e187cbdeae48ff6107ca93 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:34:15 +0300 Subject: [PATCH 204/523] fix(net): recover diagnostic buffer and pool signatures --- core/libs/prx/libSceNet/Export.cpp | 20 ++++++++++---------- docs/dev/debt/libSceNet.md | 11 ----------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 11e606de4a..a6c3965f80 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2056,7 +2056,7 @@ int APS5_VABI sceNetSetDnsInfo(void) { return 0; } -int APS5_VABI sceNetShowIfconfigWithMemory(void) { +int APS5_VABI sceNetShowIfconfigWithMemory(std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2066,7 +2066,7 @@ int APS5_VABI sceNetShowPolicy(void) { return 0; } -int APS5_VABI sceNetShowPolicyWithMemory(void) { +int APS5_VABI sceNetShowPolicyWithMemory(std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2075,7 +2075,7 @@ int APS5_VABI sceNetShowRoute(void) { return 0; } -int APS5_VABI sceNetShowRouteWithMemory(void) { +int APS5_VABI sceNetShowRouteWithMemory(std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2114,7 +2114,7 @@ int APS5_VABI sceNetShowRoute6(void) { return 0; } -int APS5_VABI sceNetShowRoute6WithMemory(void) { +int APS5_VABI sceNetShowRoute6WithMemory(std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2359,27 +2359,27 @@ int APS5_VABI sceNetUsleep(int microseconds) { return sceKernelUsleep_nid_postfix(static_cast(microseconds)); } -int APS5_VABI sceNetShowIfconfigForBuffer(void) { +int APS5_VABI sceNetShowIfconfigForBuffer(char* buffer, std::int32_t bufferSize, std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetShowRouteForBuffer(void) { +int APS5_VABI sceNetShowRouteForBuffer(char* buffer, std::int32_t bufferSize, std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetShowNetstatForBuffer(void) { +int APS5_VABI sceNetShowNetstatForBuffer(char* buffer, std::int32_t bufferSize, std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetShowNetstatEx(void) { +int APS5_VABI sceNetShowNetstatEx(std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetShowNetstatExForBuffer(void) { +int APS5_VABI sceNetShowNetstatExForBuffer(char* buffer, std::int32_t bufferSize, std::int32_t poolId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2404,7 +2404,7 @@ int APS5_VABI sceNetSetDns6InfoToKernel(void) { return 0; } -int APS5_VABI sceNetShowRoute6ForBuffer(void) { +int APS5_VABI sceNetShowRoute6ForBuffer(char* buffer, std::int32_t bufferSize, std::int32_t poolId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 8e063352ab..88add20018 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -12,17 +12,12 @@ - [sceNetGetStatisticsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetInterfaceStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowIfconfigWithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowPolicy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowPolicyWithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowRouteWithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnectAbort](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnectCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnect](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnectDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetSockInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowRoute6WithMemory](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetControl](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -49,16 +44,10 @@ - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetUsleep](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int microseconds)` signature and the `EINVAL` failure for a negative count follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); other counts sleep through libkernel's `sceKernelUsleep`, a cancellation point, and return 0. Whether the console takes the count as signed, and what it returns, is unverified on hardware -- [sceNetShowIfconfigForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowRouteForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowNetstatForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowNetstatEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowNetstatExForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetNtopWithScopeId](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonWithScopeId](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDns6InfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetShowRoute6ForBuffer](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetIfLinkLocalAddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigFlushRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 9a09fc0a78948157f68291ae994a7885b6fa2172 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:35:11 +0300 Subject: [PATCH 205/523] fix(net): restore dns cache argument validation --- core/libs/prx/libSceNet/Export.cpp | 5 ++++- core/libs/tests/GuestSceNet.cpp | 11 +++++++---- docs/dev/debt/libSceNet.md | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index a6c3965f80..1719fc4787 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -100,6 +100,7 @@ constexpr int NET_EMSGSIZE = 40; constexpr int NET_ETIMEDOUT = 60; constexpr int NET_ECONNREFUSED = 61; constexpr int NET_EHOSTUNREACH = 65; +constexpr int NET_ENOTINIT = 200; constexpr int NET_ERROR_BASE = static_cast(0x80410100u); constexpr int NET_ERROR_RESOLVER_ENODNS = static_cast(0x804101E1u); constexpr int NET_RESOLVER_ABORT_FLAG_NTOA_PRESERVATION = 0x1; @@ -2037,7 +2038,9 @@ extern const std::uint8_t in6addr_any_nid_postfix[16] = {}; extern const std::uint8_t in6addr_loopback_nid_postfix[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; int APS5_VABI sceNetClearDnsCache(int flags) { - if (flags != 0) throw std::runtime_error("sceNetClearDnsCache: flags " + std::to_string(flags) + " are not supported"); + std::lock_guard lock(g_mutex); + if (!g_net_inited) return fail(NET_ENOTINIT); + if (flags != 0) return fail(NET_EINVAL); return 0; } diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 3f00cfe6d4..52b2c027b0 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -1439,6 +1439,10 @@ int main() { Require(in6addr_any_nid_postfix[i] == 0); Require(in6addr_loopback_nid_postfix[i] == (i == 15 ? 1 : 0)); } + for (const int flags : {0, 1, -1}) { + Require(sceNetClearDnsCache(flags) == static_cast(0x804101C8)); + Require(*sceNetErrnoLoc() == 200); + } Require(sceNetInit_nid_postfix() == 0); CheckEpollWritable(); CheckSocketHostErrors(); @@ -1823,10 +1827,9 @@ int main() { ipv4.fill(0xA5); Require(sceNetResolverStartNtoa(resolver, "localhost", ipv4.data(), 5000000, 1, 0) == 0 && ipv4[0] == 127); Require(sceNetResolverGetError(resolver, &resolver_error) == 0 && resolver_error == 0); - for (const int flags : {0x1, -1}) { - bool threw = false; - try { sceNetClearDnsCache(flags); } catch (const std::runtime_error&) { threw = true; } - Require(threw); + for (const int flags : {1, 2, 0x10000, -1}) { + Require(sceNetClearDnsCache(flags) == static_cast(0x80410116)); + Require(*sceNetErrnoLoc() == 22); } Require(sceNetResolverDestroy(resolver) == 0); resolver_error = -1; diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 88add20018..6d37567f4c 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -8,7 +8,7 @@ - [sceNetEtherStrton](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - its signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L737-L913) and its [Net bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs#L19), not measured on a console; the throwing declaration only resolves the import. - [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - the signatures `(int id, stats*)`, the layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) and the error codes were measured on a PS5 (firmware 13.60). The pool size is the one given at init or pool creation. Http2 and Net allocate nothing from these pools. Ssl counts its certificate backend allocations and returned data against its pool; C++ bookkeeping is outside that budget. All three initially report zero use, where the console already reports 0x15C0 bytes for Ssl, 0x1640 for Http2 and 4 for a Net pool. Ssl's allocation sizes and peak usage do not reproduce the console allocator -- [sceNetClearDnsCache](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) returns 0 without doing anything: every `sceNetResolverStart*` lookup goes to the host resolver, so the library keeps no DNS cache to drop, and the host's own cache is left alone. The `(int flags)` signature and its meaning come from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L928-L930); nonzero flags throw because their meaning is unknown +- [sceNetClearDnsCache](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the library has no DNS cache to clear because lookups use the host resolver. The host resolver cache is left unchanged. - [sceNetGetStatisticsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetInterfaceStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetSetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 1ef8e7beab4359f6b932ada4330352af1a4fbbde Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:36:07 +0300 Subject: [PATCH 206/523] fix(net): recover ipv4 and ipv6 dns parameter layouts --- core/libs/prx/libSceNet/Export.cpp | 25 +++++++++++++++++++------ docs/dev/debt/libSceNet.md | 5 ----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 1719fc4787..93e8f30629 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -63,6 +63,19 @@ struct NetIpv6Address { static_assert(sizeof(NetIpv6Address) == 16); +struct NetDnsInfo { + std::uint32_t primaryAddress; + std::uint32_t secondaryAddress; +}; + +struct NetDns6Info { + NetIpv6Address primaryAddress; + NetIpv6Address secondaryAddress; +}; + +static_assert(sizeof(NetDnsInfo) == 8); +static_assert(sizeof(NetDns6Info) == 32); + struct NetIpv6InterfaceAddress { NetIpv6Address address; std::uint8_t prefixLength; @@ -2054,7 +2067,7 @@ int APS5_VABI sceNetGetInterfaceStats(void) { return 0; } -int APS5_VABI sceNetSetDnsInfo(void) { +int APS5_VABI sceNetSetDnsInfo(const NetDnsInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2108,7 +2121,7 @@ int APS5_VABI sceNetGetSockInfo6(void) { return 0; } -int APS5_VABI sceNetSetDns6Info(void) { +int APS5_VABI sceNetSetDns6Info(const NetDns6Info* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2177,7 +2190,7 @@ int APS5_VABI sceNetGetAppMemInfo(void) { return 0; } -int APS5_VABI sceNetGetDnsInfo(std::uint32_t* info, int flags) { +int APS5_VABI sceNetGetDnsInfo(NetDnsInfo* info, std::uint32_t flags) { if (flags != 0) throw std::runtime_error("sceNetGetDnsInfo: flags " + std::to_string(flags) + " are not supported"); if (!info) throw std::runtime_error("sceNetGetDnsInfo: a null info is not supported"); std::array servers{}; @@ -2297,7 +2310,7 @@ void APS5_VABI sceNetMemoryFree(void* memory) { GuestHeap::GuestHeapFree_nid_postfix(memory); } -int APS5_VABI sceNetSetDnsInfoToKernel(void) { +int APS5_VABI sceNetSetDnsInfoToKernel(const NetDnsInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2397,12 +2410,12 @@ int APS5_VABI sceNetInetPtonWithScopeId(void) { return 0; } -int APS5_VABI sceNetGetDns6Info(void) { +int APS5_VABI sceNetGetDns6Info(NetDns6Info* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetSetDns6InfoToKernel(void) { +int APS5_VABI sceNetSetDns6InfoToKernel(const NetDns6Info* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 6d37567f4c..9c27c860fb 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -11,13 +11,11 @@ - [sceNetClearDnsCache](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the library has no DNS cache to clear because lookups use the host resolver. The host resolver cache is left unchanged. - [sceNetGetStatisticsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetInterfaceStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnectAbort](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnectCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnect](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetResolverConnectDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetSockInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetControl](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -39,15 +37,12 @@ - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. - [sceNetMemoryAllocate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int64_t size, int flags)` signature, flag 2 as zero fill and the null result for a size of 0 or less follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp). The block comes from the guest heap, and an allocation failure returns null without setting an errno. Any other flag bit throws, since its meaning is not known. Unverified on hardware - [sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(void* memory)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the block returns to the guest heap and a null pointer is ignored. Unverified on hardware -- [sceNetSetDnsInfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetUsleep](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int microseconds)` signature and the `EINVAL` failure for a negative count follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); other counts sleep through libkernel's `sceKernelUsleep`, a cancellation point, and return 0. Whether the console takes the count as signed, and what it returns, is unverified on hardware - [sceNetInetNtopWithScopeId](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonWithScopeId](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetDns6Info](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetSetDns6InfoToKernel](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigSetIfLinkLocalAddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigFlushRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 0e9839e53c0b85f84b04c8a5b783896be219fdd9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:37:24 +0300 Subject: [PATCH 207/523] fix(net): recover packet dump and control signatures --- core/libs/prx/libSceNet/Export.cpp | 14 +++++++------- docs/dev/debt/libSceNet.md | 10 ---------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 93e8f30629..cbc9df76fd 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2145,7 +2145,7 @@ int APS5_VABI sceNetAllocateAllRouteInfo6(void) { return 0; } -int APS5_VABI sceNetControl(void) { +int APS5_VABI sceNetControl(std::uint32_t interfaceId, std::int32_t interfaceIndex, std::uint32_t command, void* data, std::uint32_t dataSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2855,7 +2855,7 @@ int APS5_VABI sceNetBandwidthControlGetDataTraffic(void) { return 0; } -int APS5_VABI sceNetConfigSetDefaultScope(void) { +int APS5_VABI sceNetConfigSetDefaultScope(std::uint32_t interfaceId, const char* interfaceName, std::int32_t scopeId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2875,7 +2875,7 @@ int APS5_VABI sceNetConfigRoutingShowRoutingConfig(void) { return 0; } -int APS5_VABI sceNetConfigRoutingShowtCtlVar(void) { +int APS5_VABI sceNetConfigRoutingShowtCtlVar(const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2885,12 +2885,12 @@ int APS5_VABI sceNetGetStatisticsInfoInternal(void) { return 0; } -int APS5_VABI sceNetDumpAbort(void) { +int APS5_VABI sceNetDumpAbort(std::int32_t dumpId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetDumpCreate(void) { +int APS5_VABI sceNetDumpCreate(const char* name, std::int32_t bufferSize, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2900,12 +2900,12 @@ int APS5_VABI sceNetDumpSetFilterParam(void) { return 0; } -int APS5_VABI sceNetDumpDestroy(void) { +int APS5_VABI sceNetDumpDestroy(std::int32_t dumpId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetDumpRead(void) { +int APS5_VABI sceNetDumpRead(std::int32_t dumpId, void* buffer, std::int32_t bufferSize, std::uint32_t* flags) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 9c27c860fb..ca82d1ac9b 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -18,7 +18,6 @@ - [sceNetGetSockInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetControl](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDbgInit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetFreeAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetArpInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -56,7 +55,6 @@ - [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6GetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetInfoDumpStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocClearWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -82,21 +80,13 @@ - [sceNetConfigWlanSetDeviceConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigEtherPostPlugInOutEvent](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetPppoeStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetBandwidthControlSetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetBandwidthControlGetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetBandwidthControlGetDataTraffic](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetDefaultScope](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigRoutingStop](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigRoutingShowtCtlVar](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetStatisticsInfoInternal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDumpAbort](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDumpCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDumpSetFilterParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDumpDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDumpRead](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetEmulationDebugSettingsSet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetEmulationDebugSettingsGet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetEmulationSet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 726359fadda131e02518571b255f8dc9bb48f90b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:39:07 +0300 Subject: [PATCH 208/523] fix(ime): recover text notification signatures --- core/libs/prx/libSceIme/Export.cpp | 12 ++++++------ docs/dev/debt/libSceIme.md | 7 ------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceIme/Export.cpp b/core/libs/prx/libSceIme/Export.cpp index 671a4833b0..3d8735dc88 100644 --- a/core/libs/prx/libSceIme/Export.cpp +++ b/core/libs/prx/libSceIme/Export.cpp @@ -610,7 +610,7 @@ int APS5_VABI sceImeKeyboardUpdate(EventHandler handler) { return 0; } -int APS5_VABI sceImeVshUpdate(void) { +int APS5_VABI sceImeVshUpdate(ImeVshContext* context) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -694,7 +694,7 @@ int APS5_VABI sceImeForTestFunction(void) { return ErrorInternal; } -int APS5_VABI sceImeVshInformConfirmdString2(void) { +int APS5_VABI sceImeVshInformConfirmdString2(ImeVshContext* context, const char16_t* text, std::int32_t firstParameter, std::int32_t secondParameter, std::int32_t thirdParameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -734,7 +734,7 @@ int APS5_VABI sceImeVshSendTextInfo(void) { return 0; } -int APS5_VABI sceImeVshInformConfirmdString(void) { +int APS5_VABI sceImeVshInformConfirmdString(ImeVshContext* context, const char16_t* text, std::int32_t parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -749,17 +749,17 @@ int APS5_VABI sceImeVshNotifyCurrentTextInfoForReconvertText(void) { return 0; } -int APS5_VABI sceImeVshNotifyDeleteText(void) { +int APS5_VABI sceImeVshNotifyDeleteText(ImeVshContext* context, const char16_t* text, std::int32_t length, std::int32_t parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshNotifyUpdateCaretText(void) { +int APS5_VABI sceImeVshNotifyUpdateCaretText(ImeVshContext* context, const char16_t* text, std::int32_t length, std::int32_t parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshCheckAutoReConvertingPreeditText(void) { +int APS5_VABI sceImeVshCheckAutoReConvertingPreeditText(ImeVshContext* context, const char16_t* text) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceIme.md b/docs/dev/debt/libSceIme.md index 2d0e987979..16d8f84e8e 100644 --- a/docs/dev/debt/libSceIme.md +++ b/docs/dev/debt/libSceIme.md @@ -11,23 +11,16 @@ - [sceImeFilterText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the behaviour follows the disassembly of `libSceIme.sprx` from firmware 13.60 and is differential-tested against that function in an emulator, not measured on a console. [ImeFilterCodepages.hpp](../../../core/libs/prx/libSceIme/include/ImeFilterCodepages.hpp) holds the two codepage checks (helpers at 0x4850 and 0x48f0), rebuilt from Unicode's public [CP932](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT) and [CP936](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT) tables plus the Latin-1, CJK Extension A and a few other units the firmware also accepts (listed in the PR). The parameter layout beyond `type` is inferred: `mask` is the allowed-class bitmask, and `version` and `variant` derive it when it is 0. [sceImeCheckFilterText](../../../core/libs/prx/libSceIme/Export.cpp) throws, as its contract is unknown - [sceImeOpen, sceImeUpdate, sceImeSetText, sceImeSetCaret, sceImeClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the event ids (Open 0, UpdateText 1, UpdateCaret 2, PressClose 4, PressEnter 5), the caret directions (left 1, right 2), the error codes and the text event payload follow the PS4 reimplementation in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/b72489d8c24e63fc56a29fe2c90120ab768a210d/src/core/libraries/ime/ime.cpp); none is measured on a PS5. Events are queued and delivered by `sceImeUpdate` only when it is passed the handler given to `sceImeOpen`, with that call's `arg`. A text event points `str` at a copy of the text kept in `work`, and `inputTextBuffer` holds the same text; both are terminated, `caret_index` is the caret after the edit and the one edit area has the edit position and the change in UTF-16 units (0 for Enter and Close). An edit sends one UpdateText; a caret move sends one UpdateCaret per UTF-16 unit, as shadPS4 does. Enter inserts a line break with the multiline option and sends PressEnter otherwise; Escape sends PressClose. Typed text is cut to `maxTextLength` without splitting a surrogate pair, line breaks are dropped without the multiline option, and the BasicLatin, URL, mail and number types do not filter characters. `sceImeOpen` checks, in shadPS4's order, the user, type, enter label, input method, option bits (the `sceImeGetPanelSize` mask), `maxTextLength` (1 to 2048), the text buffer, the position (below 1920x1080, or 3840x2160 with the over-2K option), the alignments, the extended priority and reserved bytes, `work` (non-null, 4-byte aligned), the handler, the reserved bytes and the initial text (no unpaired surrogate, no line break without the multiline option); shadPS4's SDK-dependent option, language and extended-option masks and its work-overlap check are not applied, and only the text copy is written to `work`. `sceImeSetText` keeps the caret unless the text became shorter, `sceImeSetCaret` rejects an index past the text with `0x80bc0030`, and neither sends an event. `sceImeSetTextGeometry` accepts any geometry while a panel is open, since no panel is drawn - [sceImeVshOpen](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshUpdate](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshUpdateContext](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifySetText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDisableController](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeForTestFunction](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshInformConfirmdString2](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeCheckUpdateTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeCheckRemoteEventParam](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshSetParam](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshUpdateContext2](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshSendTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshInformConfirmdString](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyCurrentTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyCurrentTextInfoForReconvertText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshNotifyDeleteText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshNotifyUpdateCaretText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshCheckAutoReConvertingPreeditText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyPreeditMetaInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigSet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigGet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature From 763dcfe88da354a4ec63917ebd6d22d42dc053e1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:40:02 +0300 Subject: [PATCH 209/523] fix(hmd): recover led and device status signatures --- core/libs/prx/libSceHmd/Export.cpp | 18 +++++++++--------- docs/dev/debt/libSceHmd.md | 12 ------------ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 493e492595..82ed0eedc9 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -186,7 +186,7 @@ int APS5_VABI sceHmdInternalAnotherScreenSendVideo(void) { return 0; } -int APS5_VABI sceHmdInternalGetRequiredPUPVersion(void) { +int APS5_VABI sceHmdInternalGetRequiredPUPVersion(std::uint32_t* firstVersion, std::uint32_t* secondVersion) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -250,7 +250,7 @@ int APS5_VABI sceHmdInternalSetVRMode(std::int32_t enabled) { return 0; } -int APS5_VABI sceHmdInternalCrashReportReadDataSize(void) { +int APS5_VABI sceHmdInternalCrashReportReadDataSize(std::uint32_t* dataSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -287,7 +287,7 @@ int APS5_VABI sceHmdInternalSetDebugSocialScreenMode(std::uint8_t mode) { return 0; } -int APS5_VABI sceHmdInternalGetHmuPowerStatusForDebug(void) { +int APS5_VABI sceHmdInternalGetHmuPowerStatusForDebug(std::uint8_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -335,7 +335,7 @@ int APS5_VABI sceHmdReprojectionStopCapture(void) { return 0; } -int APS5_VABI sceHmdInternalIsCommonDlgMiniAppVr2d(void) { +bool APS5_VABI sceHmdInternalIsCommonDlgMiniAppVr2d(void) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -359,12 +359,12 @@ int APS5_VABI sceHmdInternalGetDebugMode(std::uint8_t* mode) { return 0; } -int APS5_VABI sceHmdInternalIsMiniAppVr2d(void) { +bool APS5_VABI sceHmdInternalIsMiniAppVr2d(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalIsCommonDlgVr2d(void) { +bool APS5_VABI sceHmdInternalIsCommonDlgVr2d(void) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -374,7 +374,7 @@ int APS5_VABI sceHmdReprojectionSetOutputMinColor(float red, float green, float return 0; } -int APS5_VABI sceHmdInternalSetPortConnection(void) { +int APS5_VABI sceHmdInternalSetPortConnection(std::uint64_t connection) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -384,7 +384,7 @@ int APS5_VABI sceHmdReprojectionDebugGetLastInfo(void) { return 0; } -int APS5_VABI sceHmdInternalSetM2LedOn(void) { +int APS5_VABI sceHmdInternalSetM2LedOn(std::int32_t handle, std::uint32_t ledMask, std::uint32_t brightness) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -405,7 +405,7 @@ int APS5_VABI sceHmdInternalSetIpdSettingEnableForSystemService(std::uint8_t ena return 0; } -int APS5_VABI sceHmdInternalSetLedOn(void) { +int APS5_VABI sceHmdInternalSetLedOn(std::int32_t handle, std::uint32_t ledMask) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 6305a8d945..7e1c4ee2d7 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -11,41 +11,29 @@ - [sceHmdInternalSocialScreenSetOutput](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetEyeStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionUnsetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalRegisterEventCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspect](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuGetStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetRequiredPUPVersion](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalResetInertialSensor](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCrashReportReadDataSize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetPuPositionParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitializeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetHmuPowerStatusForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetDisplayBuffers](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCreateSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspectDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStopCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalIsCommonDlgMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetSensorCalibrationData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalIsMiniAppVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalIsCommonDlgVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetPortConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetM2LedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetLedOn](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioSendData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 47f6af24a46cc0de340645d2984addbfe6f17056 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:42:05 +0300 Subject: [PATCH 210/523] fix(json): restore the parser error code --- core/libs/prx/libSceJson2/Export.cpp | 5 +++-- core/libs/tests/GuestJson.cpp | 12 +++++++++--- docs/dev/debt/libSceJson2.md | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index e4d7e67624..2d1ea2907f 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -25,7 +25,7 @@ enum ValueType : std::int32_t { TypeObject = 7, }; -constexpr int JsonErrorParse = static_cast(0x80920101); +constexpr int JsonErrorParse = static_cast(0x80848101); struct Node; struct Value { Node* node; }; @@ -945,7 +945,8 @@ int APS5_VABI _ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(Value* out, Pars } int APS5_VABI _ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(Value* out, const char* text, std::size_t size) { - if (text == nullptr || size == 0) return JsonErrorParse; + if (size == 0) return JsonErrorParse; + if (text == nullptr) throw std::invalid_argument("sce::Json::Parser::parse: null text"); Node parsed{}; parsed.handlers = NodeOf(*out).handlers; Parser parser(text, size, parsed.handlers); diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index f18fbcdec8..1c6ca64872 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -158,7 +158,7 @@ static void Check(bool value, int line) { #define Require(value) Check((value), __LINE__) enum : std::int32_t { TypeNull, TypeBoolean, TypeInteger, TypeUInteger, TypeReal, TypeString, TypeArray, TypeObject }; -constexpr int JsonErrorParse = static_cast(0x80920101); +constexpr int JsonErrorParse = static_cast(0x80848101); static Value fallback{}; static int callbackCalls = 0; @@ -241,11 +241,17 @@ static void ParseAndRoundTrip() { _ZN3sce4Json5ValueD1Ev(&reparsed); for (const char* invalid : {"{\"a\":}", "[1,]", "\"open", "tru", "{} x", "01"}) { - Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, invalid, std::strlen(invalid)) < 0); + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, invalid, std::strlen(invalid)) == JsonErrorParse); Require(_ZNK3sce4Json5Value7getTypeEv(&root) == TypeObject && _ZNK3sce4Json5Value5countEv(&root) == 8); } Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, nullptr, 0) == JsonErrorParse); - Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, nullptr, 10) == JsonErrorParse); + bool nullTextRejected = false; + try { + _ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, nullptr, 10); + } catch (const std::invalid_argument&) { + nullTextRejected = true; + } + Require(nullTextRejected); Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, "", 0) == JsonErrorParse); Require(_ZNK3sce4Json5Value7getTypeEv(&root) == TypeObject && _ZNK3sce4Json5Value5countEv(&root) == 8); for (const char* overflow : {"1e309", "[-1e309]", "{\"a\":[1,1e400]}"}) { diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 86b6444e91..b290b68e93 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -3,7 +3,7 @@ ### Unknown function info - [sce::Json::Parser::parse with a read callback](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the callback's contract is taken from PPSA04540's: it returns 0 with the next character, and at the end of its data writes `'\0'` and returns -1. Characters are read until the callback returns non-zero or gives `'\0'`, and the text read is then parsed as by the `(text, size)` overload, so trailing characters after the value are rejected as there. Whether the console stops reading at the end of the top-level value, and what it returns when the callback fails before the end, are unknown -- [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer or zero size returns `JsonErrorParse` (0x80920101) without modifying the destination Value; whether the console distinguishes a null pointer from a syntax error with a separate parameter error code is unknown. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown +- [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown - [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - RTTI allocator adaptation and the `InitParameterRtti` layout remain unverified. `setAllocatorRtti` stores the raw allocation-parameter pointer as the allocator; `initialize` ignores allocator, buffer-size and special-float settings. Special-float enum meanings and parser support for non-finite numbers remain unresolved; serialization of non-finite values throws. Setter return types remain unverified. - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types From 17a1c4d291631706b5575f0e7a558a9d5ff31bec Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:43:48 +0300 Subject: [PATCH 211/523] fix(json): restore bytewise string comparison --- core/libs/prx/libSceJson2/Export.cpp | 16 ++++++++++++---- core/libs/tests/GuestJson.cpp | 17 +++++++++++++++++ docs/dev/debt/libSceJson2.md | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 2d1ea2907f..4a6b1247b7 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -333,6 +333,14 @@ void Serialize(std::string& out, const Node& n) { throw std::runtime_error("sce::Json: invalid value type"); } +int CompareText(const char* left, const char* right) { + while (*left != '\0' && *left == *right) { + ++left; + ++right; + } + return static_cast(*left) - static_cast(*right); +} + std::list::iterator ObjectPosition(const Object& object, const std::string& key) { return std::find_if(object.items->begin(), object.items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key.c_str()) >= 0; }); } @@ -617,8 +625,8 @@ bool APS5_VABI _ZNK3sce4Json6String5emptyEv(const String* self) { return self->t std::size_t APS5_VABI _ZNK3sce4Json6String6lengthEv(const String* self) { return self->text->size(); } std::size_t APS5_VABI _ZNK3sce4Json6String4sizeEv(const String* self) { return self->text->size(); } std::size_t APS5_VABI _ZNK3sce4Json6String8capacityEv(const String* self) { return self->text->capacity(); } -bool APS5_VABI _ZNK3sce4Json6StringeqEPKc(const String* self, const char* text) { return text != nullptr && *self->text == text; } -bool APS5_VABI _ZNK3sce4Json6StringeqERKS1_(const String* self, const String* other) { return other != nullptr && other->text != nullptr && *self->text == *other->text; } +bool APS5_VABI _ZNK3sce4Json6StringeqEPKc(const String* self, const char* text) { return text != nullptr && CompareText(self->text->c_str(), text) == 0; } +bool APS5_VABI _ZNK3sce4Json6StringeqERKS1_(const String* self, const String* other) { return other != nullptr && other->text != nullptr && CompareText(self->text->c_str(), other->text->c_str()) == 0; } void APS5_VABI _ZN3sce4Json6String5clearEv(String* self) { self->text->clear(); } void APS5_VABI _ZN3sce4Json6String6resizeEm(String* self, std::size_t count) { self->text->resize(count); } void APS5_VABI _ZN3sce4Json6String7reserveEm(String* self, std::size_t capacity) { self->text->reserve(capacity); } @@ -653,11 +661,11 @@ std::size_t APS5_VABI _ZNK3sce4Json6String4findEcm(const String* self, char char std::size_t APS5_VABI _ZNK3sce4Json6String5rfindEcm(const String* self, char character, std::size_t position) { return self->text->rfind(character, position); } int APS5_VABI _ZNK3sce4Json6String7compareEPKc(const String* self, const char* text) { if (text == nullptr) throw std::invalid_argument("sce::Json::String::compare: null text"); - return self->text->compare(text); + return CompareText(self->text->c_str(), text); } int APS5_VABI _ZNK3sce4Json6String7compareERKS1_(const String* self, const String* other) { if (other == nullptr || other->text == nullptr) throw std::invalid_argument("sce::Json::String::compare: null other"); - return self->text->compare(*other->text); + return CompareText(self->text->c_str(), other->text->c_str()); } void APS5_VABI _ZN3sce4Json5ArrayC1Ev(Array* self) { Construct(*self); } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index 1c6ca64872..f8c8c0f121 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -626,6 +626,22 @@ static void ObjectKeyOrder() { _ZN3sce4Json6ObjectD1Ev(&object); } +static void StringComparison() { + String left{}, right{}; + _ZN3sce4Json6StringC1EPKc(&left, "a"); + _ZN3sce4Json6StringC1EPKc(&right, "a"); + _ZN3sce4Json6String6appendEPKcm(&left, "\0suffix", 7); + Require(_ZNK3sce4Json6StringeqERKS1_(&left, &right)); + Require(_ZNK3sce4Json6StringeqEPKc(&left, "a")); + Require(_ZNK3sce4Json6String7compareERKS1_(&left, &right) == 0); + Require(_ZNK3sce4Json6String7compareEPKc(&left, "a") == 0); + Require(_ZNK3sce4Json6String7compareEPKc(&left, "z") == -25); + Require(_ZNK3sce4Json6String7compareEPKc(&left, "") == 97); + Require(_ZNK3sce4Json6String7compareEPKc(&left, "\xff") == -158); + _ZN3sce4Json6StringD1Ev(&right); + _ZN3sce4Json6StringD1Ev(&left); +} + static void StringMethods() { Require(_ZN3sce4Json6String4nposE == static_cast(-1)); const auto text = [](const String& string) { return std::string(_ZNK3sce4Json6String5c_strEv(&string), _ZNK3sce4Json6String6lengthEv(&string)); }; @@ -1286,6 +1302,7 @@ int main() { ObjectIteratorCopies(); AllocatorNotifyError(); StringMethods(); + StringComparison(); ValueSwap(); ArrayIteratorAdvance(); ObjectOperations(); diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index b290b68e93..eba56c389a 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -8,5 +8,5 @@ - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown -- [sce::Json::String size, capacity, resize, compare and operator==(const String&), append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified +- [sce::Json::String size, capacity, resize, append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - callback dispatch before conversion is not implemented. The system library invokes the applicable type mismatch handler before converting a non-boolean value, with SDK-dependent global callback handling. From f487c3699c13f253e54f8669533ca79a6fffaf0b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:45:18 +0300 Subject: [PATCH 212/523] fix(json): replace duplicate members while parsing --- core/libs/prx/libSceJson2/Export.cpp | 38 ++++++++++++++-------------- core/libs/tests/GuestJson.cpp | 12 +++++++++ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 4a6b1247b7..6e6a475608 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -345,6 +345,21 @@ std::list::iterator ObjectPosition(const Object& object, const std::string return std::find_if(object.items->begin(), object.items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key.c_str()) >= 0; }); } +Value& ObjectEntry(Object& object, const std::string& key) { + auto position = ObjectPosition(object, key); + if (position != object.items->end() && std::strcmp(position->key.text->c_str(), key.c_str()) == 0) return position->value; + Pair pair{}; + Construct(pair.key, key); + try { + Construct(pair.value); + return object.items->insert(position, pair)->value; + } catch (...) { + Destroy(pair.value); + Destroy(pair.key); + throw; + } +} + class Parser { public: Parser(const char* text, std::size_t size, const Handlers& handlers) : _text(text), _end(text + size), _handlers(handlers) {} @@ -514,11 +529,10 @@ private: if (_text >= _end || *_text != ':') return false; ++_text; skip(); - auto& pair = *out.object.items->emplace(ObjectPosition(out.object, key)); - Construct(pair.key, key); - Construct(pair.value); - pair.value.node->handlers = _handlers; - if (!value(*pair.value.node)) return false; + Value& entry = ObjectEntry(out.object, key); + Clear(*entry.node); + entry.node->handlers = _handlers; + if (!value(*entry.node)) return false; skip(); if (_text < _end && *_text == ',') { ++_text; continue; } if (_text < _end && *_text == '}') { ++_text; --_depth; return true; } @@ -542,20 +556,6 @@ void* const JsonMemAllocatorVtable[] = { reinterpret_cast(&JsonMemAllocatorPureVirtual), reinterpret_cast(&JsonMemAllocatorPureVirtual), }; -Value& ObjectEntry(Object& object, const std::string& key) { - auto position = ObjectPosition(object, key); - if (position != object.items->end() && std::strcmp(position->key.text->c_str(), key.c_str()) == 0) return position->value; - Pair pair{}; - Construct(pair.key, key); - try { - Construct(pair.value); - return object.items->insert(position, pair)->value; - } catch (...) { - Destroy(pair.value); - Destroy(pair.key); - throw; - } -} } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index f8c8c0f121..fac453fa0e 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -626,6 +626,17 @@ static void ObjectKeyOrder() { _ZN3sce4Json6ObjectD1Ev(&object); } +static void ParseDuplicateKeys() { + Value root{}; + _ZN3sce4Json5ValueC1Ev(&root); + for (const char* text : {R"({"a":1,"a":2})", R"({"a":[1,2],"a":2})", R"({"a":{"old":true},"a":2})", R"({"a":1,"a\u0000suffix":2})"}) { + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, text, std::strlen(text)) == 0); + Require(_ZNK3sce4Json5Value5countEv(&root) == 1); + Require(Serialize(root) == R"({"a":2})"); + } + _ZN3sce4Json5ValueD1Ev(&root); +} + static void StringComparison() { String left{}, right{}; _ZN3sce4Json6StringC1EPKc(&left, "a"); @@ -1303,6 +1314,7 @@ int main() { AllocatorNotifyError(); StringMethods(); StringComparison(); + ParseDuplicateKeys(); ValueSwap(); ArrayIteratorAdvance(); ObjectOperations(); From 03e47f84fb671cac565c1f87c2b6842afaa69deb Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:47:16 +0300 Subject: [PATCH 213/523] fix(json): restore incremental parser callback semantics --- core/libs/prx/libSceJson2/Export.cpp | 182 +++++++++++++++------------ core/libs/tests/GuestJson.cpp | 27 +++- docs/dev/debt/libSceJson2.md | 3 +- 3 files changed, 126 insertions(+), 86 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 6e6a475608..b14ad5ccd7 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -360,30 +360,57 @@ Value& ObjectEntry(Object& object, const std::string& key) { } } +using ParserReadCallback = int (APS5_VABI*)(char&, void*); + class Parser { public: - Parser(const char* text, std::size_t size, const Handlers& handlers) : _text(text), _end(text + size), _handlers(handlers) {} + Parser(const char* text, std::size_t size, const Handlers& handlers) : text(text), end(text + size), handlers(handlers) {} + Parser(ParserReadCallback read, void* context, const Handlers& handlers) : read(read), context(context), handlers(handlers) {} bool Parse(Node& out) { skip(); - if (!value(out)) return false; - skip(); - return _text == _end || *_text == '\0'; + return value(out); } + int Error() const { return readError < 0 ? readError : JsonErrorParse; } + private: static constexpr std::size_t MaxNestingDepth = 512; - const char* _text; - const char* _end; - Handlers _handlers; - std::size_t _depth = 0; + const char* text = nullptr; + const char* end = nullptr; + ParserReadCallback read = nullptr; + void* context = nullptr; + Handlers handlers; + std::size_t depth = 0; + int current = -2; + int readError = 0; - void skip() { while (_text < _end && (*_text == ' ' || *_text == '\t' || *_text == '\n' || *_text == '\r')) ++_text; } + int peek() { + if (current != -2) return current; + if (read != nullptr) { + char character = 0; + readError = read(character, context); + current = readError < 0 ? -1 : static_cast(character); + } else if (text == end) { + readError = JsonErrorParse; + current = -1; + } else { + current = static_cast(*text++); + } + return current; + } + int take() { + const int character = peek(); + if (character >= 0) current = -2; + return character; + } + void skip() { while (peek() == ' ' || peek() == '\t' || peek() == '\n' || peek() == '\r') take(); } bool literal(const char* word) { - const auto length = std::strlen(word); - if (static_cast(_end - _text) < length || std::memcmp(_text, word, length) != 0) return false; - _text += length; + for (; *word != '\0'; ++word) { + if (peek() != static_cast(*word)) return false; + take(); + } return true; } static void appendUtf8(std::string& out, std::uint32_t code) { @@ -393,10 +420,9 @@ private: else { out += static_cast(0xf0 | (code >> 18)); out += static_cast(0x80 | ((code >> 12) & 0x3f)); out += static_cast(0x80 | ((code >> 6) & 0x3f)); out += static_cast(0x80 | (code & 0x3f)); } } bool hex4(std::uint32_t& code) { - if (_end - _text < 4) return false; code = 0; for (int i = 0; i < 4; ++i) { - const char c = *_text++; + const int c = take(); code <<= 4; if (c >= '0' && c <= '9') code |= static_cast(c - '0'); else if (c >= 'a' && c <= 'f') code |= static_cast(c - 'a' + 10); @@ -406,14 +432,14 @@ private: return true; } bool string(std::string& out) { - if (_text >= _end || *_text != '"') return false; - ++_text; - while (_text < _end && *_text != '"') { - const char c = *_text++; + if (peek() < 0 || peek() != '"') return false; + take(); + while (peek() >= 0 && peek() != '"') { + const int c = take(); if (static_cast(c) < 0x20) return false; if (c != '\\') { out += c; continue; } - if (_text >= _end) return false; - switch (*_text++) { + if (peek() < 0) return false; + switch (take()) { case '"': out += '"'; break; case '\\': out += '\\'; break; case '/': out += '/'; break; @@ -436,31 +462,30 @@ private: default: return false; } } - if (_text >= _end) return false; - ++_text; + if (peek() < 0) return false; + take(); return true; } bool number(Node& out) { - const char* start = _text; - if (_text < _end && *_text == '-') ++_text; - if (_text >= _end || *_text < '0' || *_text > '9') return false; - if (*_text == '0') ++_text; - else while (_text < _end && *_text >= '0' && *_text <= '9') ++_text; + std::string token; + if (peek() >= 0 && peek() == '-') token.push_back(static_cast(take())); + if (peek() < 0 || peek() < '0' || peek() > '9') return false; + while (peek() >= '0' && peek() <= '9') token.push_back(static_cast(take())); bool integral = true; - if (_text < _end && *_text == '.') { + if (peek() >= 0 && peek() == '.') { integral = false; - ++_text; - if (_text >= _end || *_text < '0' || *_text > '9') return false; - while (_text < _end && *_text >= '0' && *_text <= '9') ++_text; + token.push_back(static_cast(take())); + if (peek() < 0 || peek() < '0' || peek() > '9') return false; + while (peek() >= 0 && peek() >= '0' && peek() <= '9') token.push_back(static_cast(take())); } - if (_text < _end && (*_text == 'e' || *_text == 'E')) { + if (peek() >= 0 && (peek() == 'e' || peek() == 'E')) { integral = false; - ++_text; - if (_text < _end && (*_text == '+' || *_text == '-')) ++_text; - if (_text >= _end || *_text < '0' || *_text > '9') return false; - while (_text < _end && *_text >= '0' && *_text <= '9') ++_text; + token.push_back(static_cast(take())); + if (peek() >= 0 && (peek() == '+' || peek() == '-')) token.push_back(static_cast(take())); + if (peek() < 0 || peek() < '0' || peek() > '9') return false; + while (peek() >= 0 && peek() >= '0' && peek() <= '9') token.push_back(static_cast(take())); } - const std::string text(start, _text); + const std::string& text = token; if (integral) { errno = 0; if (text[0] == '-') { @@ -482,8 +507,8 @@ private: return true; } bool value(Node& out) { - if (_text >= _end) return false; - switch (*_text) { + if (peek() < 0) return false; + switch (peek()) { case 'n': if (!literal("null")) return false; SetType(out, TypeNull); return true; case 't': if (!literal("true")) return false; SetType(out, TypeBoolean); out.boolean = true; return true; case 'f': if (!literal("false")) return false; SetType(out, TypeBoolean); return true; @@ -495,47 +520,47 @@ private: return true; } case '[': { - if (_depth == MaxNestingDepth) return false; - ++_text; - ++_depth; + if (depth == MaxNestingDepth) return false; + take(); + ++depth; SetType(out, TypeArray); skip(); - if (_text < _end && *_text == ']') { ++_text; --_depth; return true; } + if (peek() >= 0 && peek() == ']') { take(); --depth; return true; } for (;;) { Value item{}; Construct(item); - item.node->handlers = _handlers; + item.node->handlers = handlers; out.array.items->push_back(item); skip(); if (!value(*item.node)) return false; skip(); - if (_text < _end && *_text == ',') { ++_text; continue; } - if (_text < _end && *_text == ']') { ++_text; --_depth; return true; } + if (peek() >= 0 && peek() == ',') { take(); continue; } + if (peek() >= 0 && peek() == ']') { take(); --depth; return true; } return false; } } case '{': { - if (_depth == MaxNestingDepth) return false; - ++_text; - ++_depth; + if (depth == MaxNestingDepth) return false; + take(); + ++depth; SetType(out, TypeObject); skip(); - if (_text < _end && *_text == '}') { ++_text; --_depth; return true; } + if (peek() >= 0 && peek() == '}') { take(); --depth; return true; } for (;;) { skip(); std::string key; if (!string(key)) return false; skip(); - if (_text >= _end || *_text != ':') return false; - ++_text; + if (peek() < 0 || peek() != ':') return false; + take(); skip(); Value& entry = ObjectEntry(out.object, key); Clear(*entry.node); - entry.node->handlers = _handlers; + entry.node->handlers = handlers; if (!value(*entry.node)) return false; skip(); - if (_text < _end && *_text == ',') { ++_text; continue; } - if (_text < _end && *_text == '}') { ++_text; --_depth; return true; } + if (peek() >= 0 && peek() == ',') { take(); continue; } + if (peek() >= 0 && peek() == '}') { take(); --depth; return true; } return false; } } @@ -545,6 +570,25 @@ private: } }; +int ParseValue(Value* out, Parser& parser) { + Node parsed{}; + parsed.handlers = NodeOf(*out).handlers; + bool parsedOk = false; + try { + parsedOk = parser.Parse(parsed); + } catch (...) { + Clear(parsed); + throw; + } + if (!parsedOk) { + Clear(parsed); + return parser.Error(); + } + Clear(NodeOf(*out)); + *out->node = parsed; + return 0; +} + [[noreturn]] void APS5_VABI JsonMemAllocatorPureVirtual() { throw std::runtime_error("sce::Json::MemAllocator: pure virtual function called"); } @@ -940,38 +984,18 @@ int APS5_VABI _ZNK3sce4Json5Value8toStringERNS0_6StringE(const Value* self, Stri return 0; } -using ParserReadCallback = int (APS5_VABI*)(char&, void*); - -int APS5_VABI _ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(Value* out, const char* text, std::size_t size); int APS5_VABI _ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(Value* out, ParserReadCallback read, void* context) { if (read == nullptr) throw std::invalid_argument("sce::Json::Parser::parse: null read callback"); - std::string text; - char ch = 0; - while (read(ch, context) == 0 && ch != '\0') text.push_back(ch); - return _ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(out, text.data(), text.size()); + Parser parser(read, context, NodeOf(*out).handlers); + return ParseValue(out, parser); } int APS5_VABI _ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(Value* out, const char* text, std::size_t size) { if (size == 0) return JsonErrorParse; if (text == nullptr) throw std::invalid_argument("sce::Json::Parser::parse: null text"); - Node parsed{}; - parsed.handlers = NodeOf(*out).handlers; - Parser parser(text, size, parsed.handlers); - bool parsedOk = false; - try { - parsedOk = parser.Parse(parsed); - } catch (...) { - Clear(parsed); - throw; - } - if (!parsedOk) { - Clear(parsed); - return JsonErrorParse; - } - Clear(NodeOf(*out)); - *out->node = parsed; - return 0; + Parser parser(text, size, NodeOf(*out).handlers); + return ParseValue(out, parser); } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index fac453fa0e..9193c726c7 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -240,7 +240,7 @@ static void ParseAndRoundTrip() { Require(Serialize(reparsed) == serialized); _ZN3sce4Json5ValueD1Ev(&reparsed); - for (const char* invalid : {"{\"a\":}", "[1,]", "\"open", "tru", "{} x", "01"}) { + for (const char* invalid : {"{\"a\":}", "[1,]", "\"open", "tru"}) { Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, invalid, std::strlen(invalid)) == JsonErrorParse); Require(_ZNK3sce4Json5Value7getTypeEv(&root) == TypeObject && _ZNK3sce4Json5Value5countEv(&root) == 8); } @@ -1038,6 +1038,8 @@ struct ReadState { const char* text; std::size_t position; int calls; + int successStatus = 0; + int failureStatus = -1; }; static int APS5_VABI ReadNext(char& ch, void* context) { @@ -1045,10 +1047,10 @@ static int APS5_VABI ReadNext(char& ch, void* context) { ++state->calls; if (state->text[state->position] == '\0') { ch = '\0'; - return -1; + return state->failureStatus; } ch = state->text[state->position++]; - return 0; + return state->successStatus; } static void StreamParse() { @@ -1056,12 +1058,27 @@ static void StreamParse() { _ZN3sce4Json5ValueC1Ev(&root); ReadState valid{"{\"list\": [1, 2.5, true]}", 0, 0}; Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &valid) == 0); - Require(valid.calls == static_cast(std::strlen(valid.text)) + 1); + Require(valid.calls == static_cast(std::strlen(valid.text))); Require(_ZNK3sce4Json5Value7getTypeEv(&root) == TypeObject); ReadState invalid{"{\"list\": [1, 2.5", 0, 0}; Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &invalid) < 0); ReadState trailing{"[1] 2", 0, 0}; - Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &trailing) < 0); + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &trailing) == 0); + Require(trailing.position == 3); + Require(Serialize(root) == "[1]"); + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &trailing) == 0); + Require(Serialize(root) == "2"); + ReadState positive{"true false", 0, 0, 7}; + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &positive) == 0); + Require(positive.position == 4 && positive.calls == 4); + Require(Serialize(root) == "true"); + ReadState failed{"tr", 0, 0, 0, -123456}; + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPFiRcPvES5_(&root, ReadNext, &failed) == -123456); + Require(failed.calls == 3); + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, "{} trailing", 11) == 0); + Require(Serialize(root) == "{}"); + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&root, "01", 2) == 0); + Require(Serialize(root) == "1"); _ZN3sce4Json5ValueD1Ev(&root); } diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index eba56c389a..897a310ef2 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -2,8 +2,7 @@ ### Unknown function info -- [sce::Json::Parser::parse with a read callback](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the callback's contract is taken from PPSA04540's: it returns 0 with the next character, and at the end of its data writes `'\0'` and returns -1. Characters are read until the callback returns non-zero or gives `'\0'`, and the text read is then parsed as by the `(text, size)` overload, so trailing characters after the value are rejected as there. Whether the console stops reading at the end of the top-level value, and what it returns when the callback fails before the end, are unknown -- [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown +- [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown. Positive integer tokens use the signed type when they fit; the system parser uses the unsigned type. Syntax validation for number tokens and destination state after a failed parse still differ from the system parser - [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - RTTI allocator adaptation and the `InitParameterRtti` layout remain unverified. `setAllocatorRtti` stores the raw allocation-parameter pointer as the allocator; `initialize` ignores allocator, buffer-size and special-float settings. Special-float enum meanings and parser support for non-finite numbers remain unresolved; serialization of non-finite values throws. Setter return types remain unverified. - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types From 10c25e1dac30144bc56ee4fec61155931e0531b4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:51:07 +0300 Subject: [PATCH 214/523] fix(http2): recover websocket callbacks and async signatures --- core/libs/prx/libSceHttp2/Export.cpp | 13 +++++++++---- docs/dev/debt/libSceHttp2.md | 1 - 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceHttp2/Export.cpp b/core/libs/prx/libSceHttp2/Export.cpp index 3d681f4ede..bc1682e4fe 100644 --- a/core/libs/prx/libSceHttp2/Export.cpp +++ b/core/libs/prx/libSceHttp2/Export.cpp @@ -101,6 +101,11 @@ static int CompleteAsync(const char* function, int req_id, const Http2AsyncOptio return 0; } +using Http2WebSocketTextCallback = void (APS5_VABI*)(std::int32_t requestId, const char* text, std::uint32_t flags, void* userData); +using Http2WebSocketDataCallback = void (APS5_VABI*)(std::int32_t requestId, const void* data, std::size_t dataSize, std::uint32_t flags, void* userData); +using Http2WebSocketCloseCallback = void (APS5_VABI*)(std::int32_t requestId, const std::uint16_t* status, const char* reason, std::uint32_t flags, void* userData); +using Http2WebSocketErrorCallback = void (APS5_VABI*)(std::int32_t requestId, std::int32_t error, void* userData); + extern "C" { int APS5_VABI sceHttp2AddRequestHeader(int id, const char* name, const char* value, uint32_t mode) { @@ -381,22 +386,22 @@ int APS5_VABI sceHttp2SetResolveRetry(int id, int32_t retry) { return 0; } -int APS5_VABI sceHttp2WebSocketCreateRequest() { +int APS5_VABI sceHttp2WebSocketCreateRequest(std::int32_t templateId, const char* url, Http2WebSocketTextCallback textCallback, Http2WebSocketDataCallback dataCallback, Http2WebSocketCloseCallback closeCallback, Http2WebSocketErrorCallback errorCallback, void* receiveBuffer, std::size_t receiveBufferSize, void* userData, const void* option) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttp2WebSocketCloseAsync() { +int APS5_VABI sceHttp2WebSocketCloseAsync(std::int32_t requestId, const std::uint16_t* status, const char* reason, std::uint8_t flags, const Http2AsyncOption* asyncOption, const void* option) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttp2WebSocketSendTextMessageAsync() { +int APS5_VABI sceHttp2WebSocketSendTextMessageAsync(std::int32_t requestId, const char* text, std::uint8_t flags, const Http2AsyncOption* asyncOption, const void* option) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttp2WebSocketSendDataMessageAsync() { +int APS5_VABI sceHttp2WebSocketSendDataMessageAsync(std::int32_t requestId, const void* data, std::size_t dataSize, std::uint8_t flags, const Http2AsyncOption* asyncOption, const void* option) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp2.md b/docs/dev/debt/libSceHttp2.md index 2fa52cfbc5..f3db1ce9df 100644 --- a/docs/dev/debt/libSceHttp2.md +++ b/docs/dev/debt/libSceHttp2.md @@ -10,7 +10,6 @@ - [sceHttp2WebSocketSetPingTimeout](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2, NID `-MU9L6S3Fj4`) - imported by PPSA27836 01.000.001, whose loader stopped on the missing export; not called during its start-up. The parameters and the console errors are unknown. Every call throws. - [sceHttp2DeleteCookieBox](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA35943's Unity player. The `(int cookieBoxId)` signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http2.cs#L215-L217). A box `sceHttp2CreateCookieBox` returned is discarded; an unknown or already deleted id returns the library's invalid-id error (0x817B1100), as [prosper](https://github.com/mattias800/prosper/blob/b1bec57186a0/prosper/src/hle/net/hle_http2.cpp#L512-L523) does; not measured on a console - [sceHttp2SetResolveRetry](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signature `(int id, int32_t retry)` from libSceHttp's `sceHttpSetResolveRetry`; the value is accepted and ignored like the other Http2 settings, since no request reaches the network -- [sceHttp2WebSocketCreateRequest, sceHttp2WebSocketCloseAsync, sceHttp2WebSocketSendTextMessageAsync, sceHttp2WebSocketSendDataMessageAsync](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA16064 and not called during its start-up; unknown signatures - [libSceHttp2 cookie boxes](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - `sceHttp2CreateCookieBox` is taken to take the library context id first, as in the [prosper](https://github.com/mattias800/prosper) reimplementation; further arguments are unknown and ignored. `sceHttp2SetRequestNoContentLength` is assumed to take only the request id. Cookies are never stored, so `sceHttp2CookieFlush` has nothing to discard From 7525726c14dd102490ae0b1936af9870a599d910 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:55:27 +0300 Subject: [PATCH 215/523] fix(netctl): recover access point retry signatures --- core/libs/prx/libSceNetCtl/Export.cpp | 6 +++--- docs/dev/debt/libSceNetCtl.md | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index 1819d066c5..6c6663967f 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -778,12 +778,12 @@ int APS5_VABI sceNetCtlApCpStart(void) { return 0; } -int APS5_VABI sceNetCtlApAppStartWithRetryPid(void) { +int APS5_VABI sceNetCtlApAppStartWithRetryPid(std::int32_t processId, const char* passcode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlApAppStartWithRetry(void) { +int APS5_VABI sceNetCtlApAppStartWithRetry(const char* passcode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -810,7 +810,7 @@ int APS5_VABI sceNetCtlApAppInitWpaKey(void) { return 0; } -int APS5_VABI sceNetCtlApAppInitWpaKeyForQa(void) { +int APS5_VABI sceNetCtlApAppInitWpaKeyForQa(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 4f3c132d59..3f5f367476 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -29,14 +29,9 @@ - [sceNetCtlApRpGetState](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApCpStart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApAppStartWithRetryPid](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApAppStartWithRetry](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStartWithRetry](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStartConf](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStop](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApAppInitWpaKey](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApAppInitWpaKeyForQa](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRestart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweSetInternetConnectionTestResultIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature From 0b9b261a7aa1e9f6520bbb239d1634c69fc8d100 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:56:23 +0300 Subject: [PATCH 216/523] fix(hmd): recover screen transfer and eye status signatures --- core/libs/prx/libSceHmd/Export.cpp | 15 +++++++++++---- docs/dev/debt/libSceHmd.md | 6 ------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 82ed0eedc9..0a5e7009e1 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -27,6 +27,13 @@ struct HmdEyeOffset { float z; }; +struct HmdScreenData { + const void* data; + std::int32_t size; +}; + +static_assert(sizeof(HmdScreenData) == 16); +static_assert(offsetof(HmdScreenData, size) == 8); static_assert(sizeof(HmdEyeDistances) == 8); static_assert(sizeof(HmdEyeOffset) == 12); @@ -108,7 +115,7 @@ int APS5_VABI sceHmdInternalGetVirtualDisplayDepth(std::uint8_t* depth) { return 0; } -int APS5_VABI sceHmdInternalGetEyeStatus(void) { +int APS5_VABI sceHmdInternalGetEyeStatus(std::int32_t handle, HmdEyeOffset* left, HmdEyeOffset* right) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -181,7 +188,7 @@ int APS5_VABI sceHmdInternalDfuGetStatus(void) { return 0; } -int APS5_VABI sceHmdInternalAnotherScreenSendVideo(void) { +int APS5_VABI sceHmdInternalAnotherScreenSendVideo(const HmdScreenData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -260,7 +267,7 @@ int APS5_VABI sceHmdInternalGetVr2dData(void) { return 0; } -int APS5_VABI sceHmdInternalAnotherScreenSendAudio(void) { +int APS5_VABI sceHmdInternalAnotherScreenSendAudio(const HmdScreenData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -840,7 +847,7 @@ int APS5_VABI sceHmdInternal3dAudioOpen(void) { return 0; } -int APS5_VABI sceHmdInternalAnotherScreenGetFadeState(void) { +int APS5_VABI sceHmdInternalAnotherScreenGetFadeState(std::int32_t* state) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 7e1c4ee2d7..40c6b40792 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -7,21 +7,16 @@ ### Unknown function info - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count -- [sceHmdInternalEventInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetOutput](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetEyeStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalRegisterEventCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspect](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuGetStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetPuPositionParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitializeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -100,6 +95,5 @@ - [sceHmdInternalSetPortStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenGetFadeState](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 09d851b3155826d82c54ef3157cf7f4288400830 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:57:23 +0300 Subject: [PATCH 217/523] fix(hmd): recover device update transfer signatures --- core/libs/prx/libSceHmd/Export.cpp | 18 ++++++++++++++---- docs/dev/debt/libSceHmd.md | 8 -------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 0a5e7009e1..cd2eaa264e 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -27,6 +27,16 @@ struct HmdEyeOffset { float z; }; +struct HmdDfuData { + const void* data; + std::uint32_t size; + std::uint32_t mode; +}; + +static_assert(sizeof(HmdDfuData) == 16); +static_assert(offsetof(HmdDfuData, size) == 8); +static_assert(offsetof(HmdDfuData, mode) == 12); + struct HmdScreenData { const void* data; std::int32_t size; @@ -546,7 +556,7 @@ int APS5_VABI sceHmdDistortionInitialize(void) { return 0; } -int APS5_VABI sceHmdInternalDfuSetMode(void) { +int APS5_VABI sceHmdInternalDfuSetMode(std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -556,7 +566,7 @@ int APS5_VABI sceHmdInternalSetForcedCrash(void) { return 0; } -int APS5_VABI sceHmdInternalDfuCheckPartialUpdateAvailable(void) { +int APS5_VABI sceHmdInternalDfuCheckPartialUpdateAvailable(bool* available) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -646,7 +656,7 @@ int APS5_VABI sceHmdDistortionGet2dVrCommand(void) { return 0; } -int APS5_VABI sceHmdInternalDfuSendSize(void) { +int APS5_VABI sceHmdInternalDfuSendSize(std::int32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -730,7 +740,7 @@ int APS5_VABI sceHmdGetDistortionParams(void) { return 0; } -int APS5_VABI sceHmdInternalDfuSend(void) { +int APS5_VABI sceHmdInternalDfuSend(const HmdDfuData* data) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 40c6b40792..e41acf1fcd 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -37,7 +37,6 @@ - [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetHmuPowerControl](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalBindDeviceWithUserId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuSerialNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -49,16 +48,12 @@ - [sceHmdInternalIsGameVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuSetMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetForcedCrash](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuCheckPartialUpdateAvailable](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetHmuPowerControlForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVsh](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDebugMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportCancel](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -68,7 +63,6 @@ - [sceHmdInternalSeparateModeOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetModeId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuSendSize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -80,10 +74,8 @@ - [sceHmdInternalCrashReportOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuSend](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMapSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetCrashDumpInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuReset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetCount](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 4e3f7647e8ff456b65ec414480758d9566e57098 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:57:56 +0300 Subject: [PATCH 218/523] fix(hmd): recover device control and capability signatures --- core/libs/prx/libSceHmd/Export.cpp | 22 +++++++++++----------- docs/dev/debt/libSceHmd.md | 11 ----------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index cd2eaa264e..c7cd7529b0 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -481,7 +481,7 @@ int APS5_VABI sceHmdReprojectionQueryGarlicBuffAlign(void) { return 0x100; } -int APS5_VABI sceHmdInternalSetHmuPowerControl(void) { +int APS5_VABI sceHmdInternalSetHmuPowerControl(std::uint8_t control) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -501,7 +501,7 @@ int APS5_VABI sceHmdDistortionGetWideNearCorrectionCommand(void) { return 0; } -int APS5_VABI sceHmdInternalCheckS3dPassModeAvailable(void) { +int APS5_VABI sceHmdInternalCheckS3dPassModeAvailable(bool* available) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -521,7 +521,7 @@ int APS5_VABI sceHmdReprojectionStartLiveCapture(void) { return 0; } -int APS5_VABI sceHmdInternalIsGameVr2d(void) { +bool APS5_VABI sceHmdInternalIsGameVr2d(void) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -561,7 +561,7 @@ int APS5_VABI sceHmdInternalDfuSetMode(std::uint32_t mode) { return 0; } -int APS5_VABI sceHmdInternalSetForcedCrash(void) { +int APS5_VABI sceHmdInternalSetForcedCrash(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -586,7 +586,7 @@ int APS5_VABI sceHmdInternalCrashReportClose(void) { return 0; } -int APS5_VABI sceHmdInternalSetHmuPowerControlForDebug(void) { +int APS5_VABI sceHmdInternalSetHmuPowerControlForDebug(std::uint8_t control) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -611,7 +611,7 @@ int APS5_VABI sceHmdInternalDfuOpen(void) { return 0; } -int APS5_VABI sceHmdInternalSetDebugMode(void) { +int APS5_VABI sceHmdInternalSetDebugMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -626,17 +626,17 @@ int APS5_VABI sceHmdInternalSeparateModeGetVideoStatus(void) { return 0; } -int APS5_VABI sceHmdInternalCheckDeviceModelMk3(void) { +int APS5_VABI sceHmdInternalCheckDeviceModelMk3(bool* isMk3) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdSetVr2dEnable(void) { +int APS5_VABI sceHmdSetVr2dEnable(std::int32_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalSetSidetone(void) { +int APS5_VABI sceHmdInternalSetSidetone(std::uint8_t level) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -676,7 +676,7 @@ int APS5_VABI sceHmdGetDistortionCorrectionCommand(void) { return 0; } -int APS5_VABI sceHmdInternalSetDeviceConnection(void) { +int APS5_VABI sceHmdInternalSetDeviceConnection(std::uint64_t connection) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -837,7 +837,7 @@ int APS5_VABI sceHmdReprojectionFinalizeCapture(void) { return 0; } -int APS5_VABI sceHmdInternalSetPortStatus(void) { +int APS5_VABI sceHmdInternalSetPortStatus(std::uint64_t status) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index e41acf1fcd..6e2bc07f80 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -37,36 +37,26 @@ - [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetHmuPowerControl](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalBindDeviceWithUserId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuSerialNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetWideNearCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCheckS3dPassModeAvailable](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalIsGameVr2d](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetForcedCrash](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetHmuPowerControlForDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVsh](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDebugMode](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportCancel](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCheckDeviceModelMk3](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdSetVr2dEnable](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetSidetone](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMmapGetModeId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDeviceConnection](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuOpticalParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalResetLedForVrTracker](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -84,7 +74,6 @@ - [sceHmdInternalSeparateModeSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalizeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetPortStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 027ec5453ca0c8bf577b900d3b23e140d78e1754 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:58:38 +0300 Subject: [PATCH 219/523] fix(hmd): recover crash report and separate screen signatures --- core/libs/prx/libSceHmd/Export.cpp | 8 ++++---- docs/dev/debt/libSceHmd.md | 8 -------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index c7cd7529b0..8d24c62b73 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -452,7 +452,7 @@ int APS5_VABI sceHmdInternalAnotherScreenClose(void) { return 0; } -int APS5_VABI sceHmdInternalCrashReportReadData(void) { +int APS5_VABI sceHmdInternalCrashReportReadData(void* buffer, std::uint32_t* bytesRead, std::uint32_t bufferSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -696,7 +696,7 @@ int APS5_VABI sceHmdInternalGetHmuOpticalParam(void) { return 0; } -int APS5_VABI sceHmdInternalSeparateModeSendVideo(void) { +int APS5_VABI sceHmdInternalSeparateModeSendVideo(const HmdScreenData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -812,12 +812,12 @@ int APS5_VABI sceHmdInternalSetBrightness(std::uint8_t brightness) { return 0; } -int APS5_VABI sceHmdInternalSocialScreenGetFadeState(void) { +int APS5_VABI sceHmdInternalSocialScreenGetFadeState(std::int32_t* state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalSeparateModeSendAudio(void) { +int APS5_VABI sceHmdInternalSeparateModeSendAudio(const HmdScreenData* data) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 6e2bc07f80..44528ec2d5 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -33,7 +33,6 @@ - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStopLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCrashReportReadData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -46,9 +45,7 @@ - [sceHmdReprojectionFinalize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCrashReportClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalResetLedForVsh](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportCancel](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -58,10 +55,7 @@ - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetHmuOpticalParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSeparateModeSendVideo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalResetLedForVrTracker](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCrashReportOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMapSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -70,8 +64,6 @@ - [sceHmdInternalMmapGetCount](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternal3dAudioClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSocialScreenGetFadeState](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSeparateModeSendAudio](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalizeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From bea25c3189e92d260cc0a8d835671d4743b887db Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:59:35 +0300 Subject: [PATCH 220/523] fix(hmd): recover audio port and event callback signatures --- core/libs/prx/libSceHmd/Export.cpp | 24 ++++++++++++++++++------ docs/dev/debt/libSceHmd.md | 7 ------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 8d24c62b73..eb46e0aeef 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -27,6 +27,18 @@ struct HmdEyeOffset { float z; }; +struct HmdMirroringAspect { + std::uint16_t reserved0; + std::int16_t aspect; + std::uint16_t reserved1; + std::uint16_t reserved2; +}; + +static_assert(sizeof(HmdMirroringAspect) == 8); +static_assert(offsetof(HmdMirroringAspect, aspect) == 2); + +using HmdEventCallback = void (APS5_VABI *)(std::uint32_t events); + struct HmdDfuData { const void* data; std::uint32_t size; @@ -154,7 +166,7 @@ int APS5_VABI sceHmdDistortionGetWorkMemoryAlign(void) { return 0x400; } -int APS5_VABI sceHmdInternalRegisterEventCallback(void) { +int APS5_VABI sceHmdInternalRegisterEventCallback(HmdEventCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -188,7 +200,7 @@ int APS5_VABI sceHmdInternalSetIPD(std::int32_t mode, HmdEyeDistances distances) return 0; } -int APS5_VABI sceHmdInternalMirroringModeSetAspect(void) { +int APS5_VABI sceHmdInternalMirroringModeSetAspect(const HmdMirroringAspect* aspect) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -427,7 +439,7 @@ int APS5_VABI sceHmdInternalSetLedOn(std::int32_t handle, std::uint32_t ledMask) return 0; } -int APS5_VABI sceHmdInternal3dAudioSendData(void) { +int APS5_VABI sceHmdInternal3dAudioSendData(std::int32_t port, const void* data, std::int32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -646,7 +658,7 @@ int APS5_VABI sceHmdInternalSeparateModeOpen(void) { return 0; } -int APS5_VABI sceHmdInternalMmapGetModeId(void) { +int APS5_VABI sceHmdInternalMmapGetModeId(std::uint32_t* modeId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -802,7 +814,7 @@ int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std:: return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } -int APS5_VABI sceHmdInternal3dAudioClose(void) { +int APS5_VABI sceHmdInternal3dAudioClose(std::int32_t port) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -852,7 +864,7 @@ int APS5_VABI sceHmdInternalAnotherScreenGetVideoStatus(void) { return 0; } -int APS5_VABI sceHmdInternal3dAudioOpen(void) { +int APS5_VABI sceHmdInternal3dAudioOpen(std::int32_t port) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 44528ec2d5..f632e1a788 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -8,9 +8,7 @@ - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count - [sceHmdInternalSocialScreenSetOutput](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalRegisterEventCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalMirroringModeSetAspect](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuGetStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -29,7 +27,6 @@ - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternal3dAudioSendData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStopLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -49,7 +46,6 @@ - [sceHmdInternalCrashReportCancel](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalMmapGetModeId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -61,12 +57,9 @@ - [sceHmdInternalMapSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetCrashDumpInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalMmapGetCount](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternal3dAudioClose](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionFinalizeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternal3dAudioOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 9773ed9724892b23e6b6eb030b7afd32ee97b1f6 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 12:59:51 +0300 Subject: [PATCH 221/523] docs(hmd): close verified capture cleanup signatures --- docs/dev/debt/libSceHmd.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index f632e1a788..937a332046 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -29,7 +29,6 @@ - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStopLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -39,7 +38,6 @@ - [sceHmdInternalAnotherScreenGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionFinalize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature @@ -54,12 +52,10 @@ - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalMapSharedMemory](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetCrashDumpInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionFinalizeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 60e09c873db55a5046bff5c3a8efa120c51d20dd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:01:45 +0300 Subject: [PATCH 222/523] fix(net): recover scoped address and interface control signatures --- core/libs/prx/libSceNet/Export.cpp | 16 ++++++++-------- docs/dev/debt/libSceNet.md | 8 -------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index cbc9df76fd..80d5b41db6 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2400,12 +2400,12 @@ int APS5_VABI sceNetShowNetstatExForBuffer(char* buffer, std::int32_t bufferSize return 0; } -int APS5_VABI sceNetInetNtopWithScopeId(void) { +const char* APS5_VABI sceNetInetNtopWithScopeId(std::int32_t family, const void* address, char* text, std::uint32_t size, std::uint32_t scopeId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetInetPtonWithScopeId(void) { +int APS5_VABI sceNetInetPtonWithScopeId(std::int32_t family, const char* text, void* address, std::uint32_t* scopeId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2465,7 +2465,7 @@ int APS5_VABI sceNetConfigSetIfaddr(int interfaceId, const std::uint32_t* addres return 0; } -int APS5_VABI sceNetConfigSetIfLinkLocalAddr6(void) { +int APS5_VABI sceNetConfigSetIfLinkLocalAddr6(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2485,12 +2485,12 @@ int APS5_VABI sceNetConfigCleanUpAllInterfaces(void) { return 0; } -int APS5_VABI sceNetConfigFlushRoute(void) { +int APS5_VABI sceNetConfigFlushRoute(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigFlushRoute6(void) { +int APS5_VABI sceNetConfigFlushRoute6(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2690,7 +2690,7 @@ int APS5_VABI sceNetDuplicateIpStop(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigWlanAdhocClearWakeOnWlan(void) { +int APS5_VABI sceNetConfigWlanAdhocClearWakeOnWlan(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2715,7 +2715,7 @@ int APS5_VABI sceNetConfigWlanAdhocLeave(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigWlanAdhocPspEmuClearWakeOnWlan(void) { +int APS5_VABI sceNetConfigWlanAdhocPspEmuClearWakeOnWlan(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2820,7 +2820,7 @@ int APS5_VABI sceNetConfigEtherGetLinkMode(std::uint32_t interfaceId, int* mode) return 0; } -int APS5_VABI sceNetConfigEtherPostPlugInOutEvent(void) { +int APS5_VABI sceNetConfigEtherPostPlugInOutEvent(std::uint32_t interfaceId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index ca82d1ac9b..a4c93d98ec 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -40,12 +40,7 @@ - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetUsleep](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int microseconds)` signature and the `EINVAL` failure for a negative count follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); other counts sleep through libkernel's `sceKernelUsleep`, a cancellation point, and return 0. Whether the console takes the count as signed, and what it returns, is unverified on hardware -- [sceNetInetNtopWithScopeId](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetInetPtonWithScopeId](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigSetIfLinkLocalAddr6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigFlushRoute](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigFlushRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpGetAutoipInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -57,11 +52,9 @@ - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocClearWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocGetWakeOnWlanInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocPspEmuClearWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocPspEmuGetWakeOnWlanInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocPspEmuSetWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -78,7 +71,6 @@ - [sceNetConfigWlanInfraScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanScan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanSetDeviceConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigEtherPostPlugInOutEvent](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetBandwidthControlSetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetBandwidthControlGetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 85b192580a6211336c970853849cc4dd50c1244d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:03:06 +0300 Subject: [PATCH 223/523] fix(net): recover thread join and interface list signatures --- core/libs/prx/libSceNet/Export.cpp | 4 ++-- docs/dev/debt/libSceNet.md | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 80d5b41db6..5214a1b339 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2224,7 +2224,7 @@ const char* APS5_VABI sceNetGetIfName(std::uint32_t index) { return names[index]; } -int APS5_VABI sceNetGetIfnameNumList(void) { +int APS5_VABI sceNetGetIfnameNumList(std::uint32_t* interfaceIds, std::int32_t capacity, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2365,7 +2365,7 @@ int APS5_VABI sceNetThreadExit(void) { return 0; } -int APS5_VABI sceNetThreadJoin(void) { +int APS5_VABI sceNetThreadJoin(void* thread) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index a4c93d98ec..6d3285577b 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -27,7 +27,6 @@ - [sceNetGetIfListEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetIfListOnce](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetIfName](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t index)` signature and the table of ten interface names (`lo0`, `eth0`, `eth1`, `dbg0`, `wlan0`, `wlan1`, `gbe0`, `bt0`, `phone0`, `pppoe0`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); an index past the table sets `EINVAL` and returns an empty string. Whether the console has more names, or returns null, is unverified on hardware -- [sceNetGetIfnameNumList](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t* output)` signature and the `EINVAL` failure for a null output follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the value comes from the host's `std::random_device`, as `kern.rng_pseudo` in libkernel does. Unverified on hardware - [sceNetGetRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetSystemTime](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unverified on PS5 hardware: the `void(uint64_t*)` signature, process-relative microseconds and ignored null output follow [shadPS4 net.cpp](https://github.com/shadps4-emu/shadPS4/blob/d615b3887ba29025a59242e1a26f5b68bb176158/src/core/libraries/net/net.cpp#L1654-L1659). The clock is the same as `sceKernelGetProcessTime`, including `APS5_TIME_SCALE` @@ -38,7 +37,6 @@ - [sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(void* memory)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the block returns to the guest heap and a null pointer is ignored. Unverified on hardware - [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetThreadJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetUsleep](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int microseconds)` signature and the `EINVAL` failure for a negative count follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); other counts sleep through libkernel's `sceKernelUsleep`, a cancellation point, and return 0. Whether the console takes the count as signed, and what it returns, is unverified on hardware - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 843185a354542e187b46aabf1335316767a3f729 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:05:18 +0300 Subject: [PATCH 224/523] fix(json): restore integer token types and bit interpretation --- core/libs/prx/libSceJson2/Export.cpp | 11 ++++++----- core/libs/tests/GuestJson.cpp | 27 +++++++++++++++++++++++++-- docs/dev/debt/libSceJson2.md | 2 +- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index b14ad5ccd7..2593044dad 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -494,8 +495,8 @@ private: } else { const unsigned long long value = std::strtoull(text.c_str(), nullptr, 10); if (errno == 0) { - if (value <= static_cast(INT64_MAX)) { out.type = TypeInteger; out.integer = static_cast(value); } - else { out.type = TypeUInteger; out.uinteger = value; } + out.type = TypeUInteger; + out.uinteger = value; return true; } } @@ -882,15 +883,15 @@ ValueType APS5_VABI _ZNK3sce4Json5Value7getTypeEv(const Value* self) { return No const bool* APS5_VABI _ZNK3sce4Json5Value10getBooleanEv(const Value* self) { return &Typed(self, TypeBoolean).boolean; } const std::int64_t* APS5_VABI _ZNK3sce4Json5Value10getIntegerEv(const Value* self) { Node& n = const_cast(NodeOf(*self)); - if (n.type == TypeUInteger && n.uinteger <= static_cast(INT64_MAX)) { - n.integer = static_cast(n.uinteger); + if (n.type == TypeUInteger) { + n.integer = std::bit_cast(n.uinteger); return &n.integer; } return &Typed(self, TypeInteger).integer; } const std::uint64_t* APS5_VABI _ZNK3sce4Json5Value11getUIntegerEv(const Value* self) { Node& n = const_cast(NodeOf(*self)); - if (n.type == TypeInteger && n.integer >= 0) { + if (n.type == TypeInteger) { n.uinteger = static_cast(n.integer); return &n.uinteger; } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index 9193c726c7..f73d4bbe14 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -197,7 +197,7 @@ static void ParseAndRoundTrip() { Require(_ZNK3sce4Json5Value7getTypeEv(&root) == TypeObject); Require(_ZNK3sce4Json5Value5countEv(&root) == 8); Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(_ZNK3sce4Json5Value9getStringEv(&Member(root, "name"))), "x\xc3\xa9\n") == 0); - Require(_ZNK3sce4Json5Value7getTypeEv(&Member(root, "count")) == TypeInteger); + Require(_ZNK3sce4Json5Value7getTypeEv(&Member(root, "count")) == TypeUInteger); Require(*_ZNK3sce4Json5Value10getIntegerEv(&Member(root, "count")) == 3); Require(*_ZNK3sce4Json5Value11getUIntegerEv(&Member(root, "count")) == 3); Require(*_ZNK3sce4Json5Value7getRealEv(&Member(root, "count")) == 3.0); @@ -228,7 +228,7 @@ static void ParseAndRoundTrip() { types.push_back(_ZNK3sce4Json5Value7getTypeEv(_ZNK3sce4Json5Array8iteratordeEv(&it))); _ZN3sce4Json5Array8iteratorD1Ev(&it); _ZN3sce4Json5Array8iteratorD1Ev(&end); - Require((types == std::vector{TypeInteger, TypeString, TypeArray})); + Require((types == std::vector{TypeUInteger, TypeString, TypeArray})); Require(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5ValueixEm(_ZNK3sce4Json5ValueixEm(&list, 2), 0)) == 3); const std::string serialized = Serialize(root); @@ -1053,6 +1053,28 @@ static int APS5_VABI ReadNext(char& ch, void* context) { return state->successStatus; } +static void IntegerRepresentation() { + Value value{}; + _ZN3sce4Json5ValueC1Ev(&value); + for (const char* text : {"0", "1", "9223372036854775807", "9223372036854775808", "18446744073709551615"}) { + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&value, text, std::strlen(text)) == 0); + Require(_ZNK3sce4Json5Value7getTypeEv(&value) == TypeUInteger); + Require(Serialize(value) == text); + } + Require(*_ZNK3sce4Json5Value10getIntegerEv(&value) == -1); + Require(_ZNK3sce4Json5Value7getTypeEv(&value) == TypeUInteger); + Require(_ZN3sce4Json5Value3setEl(&value, -1) == 0); + Require(*_ZNK3sce4Json5Value11getUIntegerEv(&value) == std::numeric_limits::max()); + Require(_ZNK3sce4Json5Value7getTypeEv(&value) == TypeInteger); + for (const char* text : {"-0", "-1", "-9223372036854775808"}) { + Require(_ZN3sce4Json6Parser5parseERNS0_5ValueEPKcm(&value, text, std::strlen(text)) == 0); + Require(_ZNK3sce4Json5Value7getTypeEv(&value) == TypeInteger); + } + Require(*_ZNK3sce4Json5Value11getUIntegerEv(&value) == (std::uint64_t{1} << 63)); + Require(_ZNK3sce4Json5Value7getTypeEv(&value) == TypeInteger); + _ZN3sce4Json5ValueD1Ev(&value); +} + static void StreamParse() { Value root{}; _ZN3sce4Json5ValueC1Ev(&root); @@ -1346,5 +1368,6 @@ int main() { InitParameters(); ValueClear(); ValueOperatorBool(); + IntegerRepresentation(); StreamParse(); } diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 897a310ef2..6aef517355 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -2,7 +2,7 @@ ### Unknown function info -- [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown. Positive integer tokens use the signed type when they fit; the system parser uses the unsigned type. Syntax validation for number tokens and destination state after a failed parse still differ from the system parser +- [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown. Syntax validation for number tokens and destination state after a failed parse still differ from the system parser - [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - RTTI allocator adaptation and the `InitParameterRtti` layout remain unverified. `setAllocatorRtti` stores the raw allocation-parameter pointer as the allocator; `initialize` ignores allocator, buffer-size and special-float settings. Special-float enum meanings and parser support for non-finite numbers remain unresolved; serialization of non-finite values throws. Setter return types remain unverified. - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types From 5cd83484897a6b02a78e16d05565b6a776f9ed8c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:08:00 +0300 Subject: [PATCH 225/523] fix(ime): recover clause conversion argument width --- core/libs/prx/libSceIme/Export.cpp | 2 +- docs/dev/debt/libSceIme.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceIme/Export.cpp b/core/libs/prx/libSceIme/Export.cpp index 3d8735dc88..5ab0ae9f52 100644 --- a/core/libs/prx/libSceIme/Export.cpp +++ b/core/libs/prx/libSceIme/Export.cpp @@ -514,7 +514,7 @@ int APS5_VABI sceImeSetCaret(const Caret* caret) { return 0; } -int APS5_VABI sceImeSetConsecutiveClauseConversionEnabled(bool enabled) { +int APS5_VABI sceImeSetConsecutiveClauseConversionEnabled(std::uint8_t enabled) { (void)enabled; NotImplemented_nid_no_patch(__func__); return 0; diff --git a/docs/dev/debt/libSceIme.md b/docs/dev/debt/libSceIme.md index 16d8f84e8e..4e6a62319d 100644 --- a/docs/dev/debt/libSceIme.md +++ b/docs/dev/debt/libSceIme.md @@ -6,8 +6,7 @@ ### Unknown function info -- [sceImeSetConsecutiveClauseConversionEnabled](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - name from its NID (`row9sfnYNx4`) in [sce_symbols](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); neither [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0ebea15c24253b60f514fc37a348dabc8d0bcb90/src/core/libraries/ime/ime.cpp) nor [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/e5f0ee7fc2fb4ce88c6abb2fa02296404e2fe2a6/src/libs/ime.cpp) implements it, so it throws. PPSA24191 calls it with 1 as its only argument before each `sceImeOpen` and opens the panel only when it returns 0; the argument type, the result while a panel is open and the error codes are unknown -- [sceImeGetPanelPositionAndForm](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the output is taken to be the `PositionAndForm` that `sceImeDialogGetPanelPositionAndForm` fills. While a panel is open it reports the type, position and alignments passed to `sceImeOpen` and the size `sceImeGetPanelSize` gives; the console's values for a panel moved by its own position adjustment are unknown. That a closed panel is reported before a null output is assumed by analogy with `sceImeSetCaret`, not confirmed on a PS5 title +- [sceImeGetPanelPositionAndForm](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the output is taken to be the `PositionAndForm` that `sceImeDialogGetPanelPositionAndForm` fills. While a panel is open it reports the type, position and alignments passed to `sceImeOpen` and the size `sceImeGetPanelSize` gives; the console's values for a panel moved by its own position adjustment are unknown. - [sceImeFilterText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the behaviour follows the disassembly of `libSceIme.sprx` from firmware 13.60 and is differential-tested against that function in an emulator, not measured on a console. [ImeFilterCodepages.hpp](../../../core/libs/prx/libSceIme/include/ImeFilterCodepages.hpp) holds the two codepage checks (helpers at 0x4850 and 0x48f0), rebuilt from Unicode's public [CP932](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT) and [CP936](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT) tables plus the Latin-1, CJK Extension A and a few other units the firmware also accepts (listed in the PR). The parameter layout beyond `type` is inferred: `mask` is the allowed-class bitmask, and `version` and `variant` derive it when it is 0. [sceImeCheckFilterText](../../../core/libs/prx/libSceIme/Export.cpp) throws, as its contract is unknown - [sceImeOpen, sceImeUpdate, sceImeSetText, sceImeSetCaret, sceImeClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the event ids (Open 0, UpdateText 1, UpdateCaret 2, PressClose 4, PressEnter 5), the caret directions (left 1, right 2), the error codes and the text event payload follow the PS4 reimplementation in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/b72489d8c24e63fc56a29fe2c90120ab768a210d/src/core/libraries/ime/ime.cpp); none is measured on a PS5. Events are queued and delivered by `sceImeUpdate` only when it is passed the handler given to `sceImeOpen`, with that call's `arg`. A text event points `str` at a copy of the text kept in `work`, and `inputTextBuffer` holds the same text; both are terminated, `caret_index` is the caret after the edit and the one edit area has the edit position and the change in UTF-16 units (0 for Enter and Close). An edit sends one UpdateText; a caret move sends one UpdateCaret per UTF-16 unit, as shadPS4 does. Enter inserts a line break with the multiline option and sends PressEnter otherwise; Escape sends PressClose. Typed text is cut to `maxTextLength` without splitting a surrogate pair, line breaks are dropped without the multiline option, and the BasicLatin, URL, mail and number types do not filter characters. `sceImeOpen` checks, in shadPS4's order, the user, type, enter label, input method, option bits (the `sceImeGetPanelSize` mask), `maxTextLength` (1 to 2048), the text buffer, the position (below 1920x1080, or 3840x2160 with the over-2K option), the alignments, the extended priority and reserved bytes, `work` (non-null, 4-byte aligned), the handler, the reserved bytes and the initial text (no unpaired surrogate, no line break without the multiline option); shadPS4's SDK-dependent option, language and extended-option masks and its work-overlap check are not applied, and only the text copy is written to `work`. `sceImeSetText` keeps the caret unless the text became shorter, `sceImeSetCaret` rejects an index past the text with `0x80bc0030`, and neither sends an event. `sceImeSetTextGeometry` accepts any geometry while a panel is open, since no panel is drawn - [sceImeVshOpen](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature From b1bcbf7995743af5bab57852df1af5d07d16100d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:10:36 +0300 Subject: [PATCH 226/523] fix(psml): recover shared memory requirements and release signatures --- core/libs/prx/libScePsml_debug/Export.cpp | 17 +++++++++-------- core/libs/tests/GuestPsml.cpp | 9 +++++---- docs/dev/debt/libScePsml_debug.md | 5 ++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libScePsml_debug/Export.cpp b/core/libs/prx/libScePsml_debug/Export.cpp index 448d0c8a60..1b8babf0fc 100644 --- a/core/libs/prx/libScePsml_debug/Export.cpp +++ b/core/libs/prx/libScePsml_debug/Export.cpp @@ -19,6 +19,7 @@ constexpr std::int32_t SCE_PSML_ERROR_NULL_OUTPUT = static_cast(0x constexpr std::uint64_t MfsrBlockBytes = 0x200000; constexpr std::uint64_t MfsrBlockCount = 4; constexpr std::uint32_t MfsrDispatchPacketDwords = 0x600; +constexpr std::uint64_t MfsrSharedBlockCount = 52; struct MfsrRequirement { std::uint64_t blockBytes; @@ -78,10 +79,10 @@ void RequireMfsr(bool condition, const char* function, const char* what) { if (!condition) throw std::runtime_error(std::string(function) + ": " + what); } -void RequireBlocks(const MfsrBlock* blocks, std::uint64_t blockCount, const char* function) { - RequireMfsr(blockCount >= MfsrBlockCount, function, "fewer memory blocks than the requirement"); +void RequireBlocks(const MfsrBlock* blocks, std::uint64_t blockCount, std::uint64_t requiredCount, const char* function) { + RequireMfsr(blockCount >= requiredCount, function, "fewer memory blocks than the requirement"); RequireMfsr(blocks != nullptr, function, "null memory block list"); - for (std::uint64_t i = 0; i < MfsrBlockCount; ++i) { + for (std::uint64_t i = 0; i < requiredCount; ++i) { RequireMfsr(blocks[i].bytes >= MfsrBlockBytes && blocks[i].address % MfsrBlockBytes == 0, function, "memory block smaller or less aligned than the requirement"); } } @@ -162,7 +163,7 @@ std::int32_t APS5_VABI scePsmlMfsrGetSharedResourcesInitRequirement(MfsrRequirem if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; RequireMfsr(requirement != nullptr && param != nullptr, __func__, "null requirement or parameter"); RequireMfsr(param->type == 0, __func__, ("shared resources type " + std::to_string(param->type) + " is not supported").c_str()); - *requirement = {MfsrBlockBytes, MfsrBlockBytes, MfsrBlockCount}; + *requirement = {MfsrBlockBytes, MfsrBlockBytes, MfsrSharedBlockCount}; return 0; } @@ -170,7 +171,7 @@ std::int32_t APS5_VABI scePsmlMfsrCreateSharedResources(void** sharedResources, if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; RequireMfsr(sharedResources != nullptr && param != nullptr, __func__, "null handle or parameter"); RequireMfsr(param->type == 0, __func__, ("shared resources type " + std::to_string(param->type) + " is not supported").c_str()); - RequireBlocks(param->blocks, param->blockCount, __func__); + RequireBlocks(param->blocks, param->blockCount, MfsrSharedBlockCount, __func__); *sharedResources = CreateMfsrObject(MfsrObjectKind::SharedResources, nullptr, __func__); return 0; } @@ -191,7 +192,7 @@ std::int32_t APS5_VABI scePsmlMfsrGetContextBufferRequirement800M3_2(MfsrRequire std::int32_t APS5_VABI scePsmlMfsrCreateContext800M3_2(void** context, const MfsrContextParam* param) { if (!g_mfsrInitialized) return SCE_PSML_ERROR_NOT_INITIALIZED; RequireMfsr(context != nullptr && param != nullptr, __func__, "null handle or parameter"); - RequireBlocks(param->blocks, param->blockCount, __func__); + RequireBlocks(param->blocks, param->blockCount, MfsrBlockCount, __func__); const auto [inputWidth, inputHeight] = MfsrInputDimensions(param); *context = CreateMfsrObject(MfsrObjectKind::Context, param->sharedResources, __func__, inputWidth, inputHeight); return 0; @@ -274,7 +275,7 @@ std::int32_t APS5_VABI scePsmlMfsrGetDispatchMfsrPacket900(void* context, void* return SCE_PSML_ERROR_NOT_INITIALIZED; } -int APS5_VABI scePsmlMfsr2ReleaseSharedResources() { +int APS5_VABI scePsmlMfsr2ReleaseSharedResources(void* sharedResources) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -289,7 +290,7 @@ int APS5_VABI scePsmlMfsr2GetContextInitRequirement() { return 0; } -int APS5_VABI scePsmlMfsr2ReleaseContext() { +int APS5_VABI scePsmlMfsr2ReleaseContext(void* context) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestPsml.cpp b/core/libs/tests/GuestPsml.cpp index f82ec1744a..882a4b5d04 100644 --- a/core/libs/tests/GuestPsml.cpp +++ b/core/libs/tests/GuestPsml.cpp @@ -5,6 +5,7 @@ #include #include #include +#include extern "C" { std::int32_t APS5_VABI scePsmlMfsrGetContextBufferRequirement1100(void* requirement, const void* param); @@ -116,8 +117,8 @@ struct ContextParam { } }; -std::array Blocks(const Requirement& requirement, std::uint64_t first) { - std::array blocks{}; +std::vector Blocks(const Requirement& requirement, std::uint64_t first) { + std::vector blocks(requirement.blockCount); for (std::size_t i = 0; i < blocks.size(); ++i) blocks[i] = {first + i * requirement.blockBytes, requirement.blockBytes}; return blocks; } @@ -153,7 +154,7 @@ void SetupAfterInit() { SharedParam sharedParam{}; Requirement shared{}; Require(scePsmlMfsrGetSharedResourcesInitRequirement(&shared, &sharedParam) == 0); - Require(shared.blockBytes == 0x200000 && shared.alignment == 0x200000 && shared.blockCount == 4); + Require(shared.blockBytes == 0x200000 && shared.alignment == 0x200000 && shared.blockCount == 52); const auto sharedBlocks = Blocks(shared, 0x10000000); sharedParam.blocks = sharedBlocks.data(); @@ -239,7 +240,7 @@ void RefusedAfterInit() { Require(scePsmlMfsrGetSharedResourcesInitRequirement(&requirement, &sharedParam) == 0); const auto blocks = Blocks(requirement, 0x10000000); sharedParam.blocks = blocks.data(); - sharedParam.blockCount = 3; + sharedParam.blockCount = 51; void* sharedResources = nullptr; Require(Throws([&] { scePsmlMfsrCreateSharedResources(&sharedResources, &sharedParam); }, "fewer memory blocks")); auto small = blocks; diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index a4cd5f3070..af02ca6976 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -2,12 +2,11 @@ ### Unknown function info -- [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - signatures from the calls in PPSA08330 (0x5724b6-0x5727e8, 0xc8697) and PPSA03016 (0x43dcf4d-0x43dd69a): a `{block bytes, alignment, block count}` requirement, a 32-byte shared-resources parameter with the type at 0 and the `{address, bytes}` block list at 8 and 16, a context parameter with the block list at 8 and 16 and the shared-resources handle at 0x38, and 8-byte handles written through the first argument and passed back by value. The descriptor pointer at 0x28 is not read. The requirement of four 2 MiB blocks, 2 MiB aligned, is what PPSA08330 v06.006 allocates regardless of the returned count; Sony's values are unknown ([KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/libPsml.cpp) derives 52 blocks for type 0; its layout differs from these calls: it writes a 0x30-byte object through the first argument, where PPSA08330 passes one of two adjacent 8-byte handle globals, and reads the shared resources at context parameter offset 8, where both titles pass the block list). Only type 0 is accepted. The error codes for null pointers, short block lists and foreign handles are unknown, so those calls throw -- [scePsmlMfsr2CreateContext, scePsmlMfsr2CreateSharedResources, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords, scePsmlMfsr2GetSharedResourcesInitRequirement, scePsmlMfsr2Init, scePsmlMfsr2ReleaseContext, scePsmlMfsr2ReleaseSharedResources](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters +- [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - signatures from the calls in PPSA08330 (0x5724b6-0x5727e8, 0xc8697) and PPSA03016 (0x43dcf4d-0x43dd69a): a `{block bytes, alignment, block count}` requirement, a 32-byte shared-resources parameter with the type at 0 and the `{address, bytes}` block list at 8 and 16, a context parameter with the block list at 8 and 16 and the shared-resources handle at 0x38, and 8-byte handles written through the first argument and passed back by value. The descriptor pointer at 0x28 is not read. The context requirement still reports four 2 MiB blocks regardless of its parameters; the system calculation depends on the context configuration. Only type 0 is accepted. The error codes for null pointers, short block lists and foreign handles are unknown, so those calls throw +- [scePsmlMfsr2CreateContext, scePsmlMfsr2CreateSharedResources, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords, scePsmlMfsr2GetSharedResourcesInitRequirement](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters - [scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_debug) - argument layouts unknown; PPSA26344 imports but does not call the first two and at `0x87EFA` passes `(rdi, rsi=&stack, rdx)` as three pointers to `GetDispatchMfsrPacket1100`; before `scePsmlMfsrInit` they return 0x8A810001 (not initialized) and after it they throw. `scePsmlMfsrCreateContext1300` and `scePsmlMfsrGetDispatchMfsrPacket900`, `1000` and `1300` take the same shapes (context slot and parameter; context, command buffer and parameter) and behave the same way. PPSA08330 and PPSA03016 pass `GetDispatchMfsrPacket900`/`1000` the context handle, a `{cursor, size in dwords}` command buffer and the dispatch parameters, and advance their cursor by the size `scePsmlMfsrGetDispatchMfsrPacketSizeInDwords` returned. Whether the 1100 and 1300 versions take these arguments, and the console's error, are unverified - [scePsmlMfsrGetDispatchMfsrPacket1000](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA25344. libScePsml and libScePsml_debug are built from the same source - [scePsmlMfsrCreateContext1300, scePsmlMfsrGetDispatchMfsrPacket1300](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA28416 next to `scePsmlMfsrGetContextBufferRequirement1100`. The names are not in the aerolib NID list; they hash to the imported NIDs `kLF7WtQ1-Dg` and `DDVUyPyYHTA` (`tools/nid_names.py`) -- [scePsmlMfsrEnableSpecularAA, scePsmlMfsrEnableQuality8K](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_specularAA and libScePsml_quality8K) - imported by PPSA01317, which fails to load without them; signatures unknown, so they throw. The names are not in the aerolib NID list; they hash to the imported NIDs `xNZM9Y5lf-M` and `lcZD9tJraAw` (`tools/nid_names.py`) ### Functional From 69095fd9107cc514be7f3aa824cbd96ca08d1ece Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:12:28 +0300 Subject: [PATCH 227/523] fix(psml): recover shared resource parameter layouts --- core/libs/prx/libScePsml_debug/Export.cpp | 20 ++++++++++++++++++-- docs/dev/debt/libScePsml_debug.md | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libScePsml_debug/Export.cpp b/core/libs/prx/libScePsml_debug/Export.cpp index 1b8babf0fc..524e34549c 100644 --- a/core/libs/prx/libScePsml_debug/Export.cpp +++ b/core/libs/prx/libScePsml_debug/Export.cpp @@ -40,6 +40,22 @@ struct MfsrSharedResourcesParam { std::uint64_t reserved2; }; +struct Mfsr2SharedResourcesParam { + std::uint32_t type; + std::uint32_t reserved0; + const MfsrBlock* blocks; + std::uint64_t blockCount; + std::uint16_t width; + std::uint16_t height; + std::uint32_t reserved1; +}; + +static_assert(sizeof(Mfsr2SharedResourcesParam) == 32); +static_assert(offsetof(Mfsr2SharedResourcesParam, blocks) == 8); +static_assert(offsetof(Mfsr2SharedResourcesParam, blockCount) == 16); +static_assert(offsetof(Mfsr2SharedResourcesParam, width) == 24); +static_assert(offsetof(Mfsr2SharedResourcesParam, reserved1) == 28); + struct MfsrContextParam { std::uint32_t reserved0[2]; const MfsrBlock* blocks; @@ -280,7 +296,7 @@ int APS5_VABI scePsmlMfsr2ReleaseSharedResources(void* sharedResources) { return 0; } -int APS5_VABI scePsmlMfsr2GetSharedResourcesInitRequirement() { +int APS5_VABI scePsmlMfsr2GetSharedResourcesInitRequirement(MfsrRequirement* requirement, const Mfsr2SharedResourcesParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -300,7 +316,7 @@ int APS5_VABI scePsmlMfsr2GetDispatchPacketsSizeInDwords() { return 0; } -int APS5_VABI scePsmlMfsr2CreateSharedResources() { +int APS5_VABI scePsmlMfsr2CreateSharedResources(void** sharedResources, const Mfsr2SharedResourcesParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index af02ca6976..e7c6c48729 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -3,7 +3,7 @@ ### Unknown function info - [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - signatures from the calls in PPSA08330 (0x5724b6-0x5727e8, 0xc8697) and PPSA03016 (0x43dcf4d-0x43dd69a): a `{block bytes, alignment, block count}` requirement, a 32-byte shared-resources parameter with the type at 0 and the `{address, bytes}` block list at 8 and 16, a context parameter with the block list at 8 and 16 and the shared-resources handle at 0x38, and 8-byte handles written through the first argument and passed back by value. The descriptor pointer at 0x28 is not read. The context requirement still reports four 2 MiB blocks regardless of its parameters; the system calculation depends on the context configuration. Only type 0 is accepted. The error codes for null pointers, short block lists and foreign handles are unknown, so those calls throw -- [scePsmlMfsr2CreateContext, scePsmlMfsr2CreateSharedResources, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords, scePsmlMfsr2GetSharedResourcesInitRequirement](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters +- [scePsmlMfsr2CreateContext, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters - [scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_debug) - argument layouts unknown; PPSA26344 imports but does not call the first two and at `0x87EFA` passes `(rdi, rsi=&stack, rdx)` as three pointers to `GetDispatchMfsrPacket1100`; before `scePsmlMfsrInit` they return 0x8A810001 (not initialized) and after it they throw. `scePsmlMfsrCreateContext1300` and `scePsmlMfsrGetDispatchMfsrPacket900`, `1000` and `1300` take the same shapes (context slot and parameter; context, command buffer and parameter) and behave the same way. PPSA08330 and PPSA03016 pass `GetDispatchMfsrPacket900`/`1000` the context handle, a `{cursor, size in dwords}` command buffer and the dispatch parameters, and advance their cursor by the size `scePsmlMfsrGetDispatchMfsrPacketSizeInDwords` returned. Whether the 1100 and 1300 versions take these arguments, and the console's error, are unverified - [scePsmlMfsrGetDispatchMfsrPacket1000](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA25344. libScePsml and libScePsml_debug are built from the same source - [scePsmlMfsrCreateContext1300, scePsmlMfsrGetDispatchMfsrPacket1300](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA28416 next to `scePsmlMfsrGetContextBufferRequirement1100`. The names are not in the aerolib NID list; they hash to the imported NIDs `kLF7WtQ1-Dg` and `DDVUyPyYHTA` (`tools/nid_names.py`) From 979fa6208639d4e977b494a082d7cabb0cc167ef Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:12:58 +0300 Subject: [PATCH 228/523] docs(psml): narrow the remaining resource setup debt --- docs/dev/debt/libScePsml_debug.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index e7c6c48729..5b0a539687 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -2,7 +2,7 @@ ### Unknown function info -- [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - signatures from the calls in PPSA08330 (0x5724b6-0x5727e8, 0xc8697) and PPSA03016 (0x43dcf4d-0x43dd69a): a `{block bytes, alignment, block count}` requirement, a 32-byte shared-resources parameter with the type at 0 and the `{address, bytes}` block list at 8 and 16, a context parameter with the block list at 8 and 16 and the shared-resources handle at 0x38, and 8-byte handles written through the first argument and passed back by value. The descriptor pointer at 0x28 is not read. The context requirement still reports four 2 MiB blocks regardless of its parameters; the system calculation depends on the context configuration. Only type 0 is accepted. The error codes for null pointers, short block lists and foreign handles are unknown, so those calls throw +- [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - only resource type 0 is supported. The context requirement reports four 2 MiB blocks regardless of its parameters, while the system calculation depends on the context configuration. The descriptor pointer at 0x28 is not read. Validation throws for null pointers, short block lists and foreign handles instead of reproducing the system error and fault behaviour. - [scePsmlMfsr2CreateContext, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters - [scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_debug) - argument layouts unknown; PPSA26344 imports but does not call the first two and at `0x87EFA` passes `(rdi, rsi=&stack, rdx)` as three pointers to `GetDispatchMfsrPacket1100`; before `scePsmlMfsrInit` they return 0x8A810001 (not initialized) and after it they throw. `scePsmlMfsrCreateContext1300` and `scePsmlMfsrGetDispatchMfsrPacket900`, `1000` and `1300` take the same shapes (context slot and parameter; context, command buffer and parameter) and behave the same way. PPSA08330 and PPSA03016 pass `GetDispatchMfsrPacket900`/`1000` the context handle, a `{cursor, size in dwords}` command buffer and the dispatch parameters, and advance their cursor by the size `scePsmlMfsrGetDispatchMfsrPacketSizeInDwords` returned. Whether the 1100 and 1300 versions take these arguments, and the console's error, are unverified - [scePsmlMfsrGetDispatchMfsrPacket1000](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA25344. libScePsml and libScePsml_debug are built from the same source From 7ede27cc9c88f6466dfd8b96ba60c819aa20507d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:16:57 +0300 Subject: [PATCH 229/523] fix(hmd): recover sensor calibration buffer signature --- core/libs/prx/libSceHmd/Export.cpp | 2 +- docs/dev/debt/libSceHmd.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index eb46e0aeef..fbdfeacca6 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -369,7 +369,7 @@ bool APS5_VABI sceHmdInternalIsCommonDlgMiniAppVr2d(void) { return 0; } -int APS5_VABI sceHmdInternalMmapGetSensorCalibrationData(void) { +int APS5_VABI sceHmdInternalMmapGetSensorCalibrationData(void* buffer, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 937a332046..73cc19fd6c 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -23,7 +23,6 @@ - [sceHmdInternalMirroringModeSetAspectDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStopCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalMmapGetSensorCalibrationData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 4cb4905c29bb461b27492fcc8cdc0a4acf682827 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:17:02 +0300 Subject: [PATCH 230/523] fix(netctl): recover connection test result signature --- core/libs/prx/libSceNetCtl/Export.cpp | 2 +- docs/dev/debt/libSceNetCtl.md | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index 6c6663967f..03167d8863 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -860,7 +860,7 @@ int APS5_VABI sceNetBweStartInternetConnectionTestBandwidthTestIpcInt(void) { return 0; } -int APS5_VABI sceNetBweSetInternetConnectionTestResultIpcInt(void) { +int APS5_VABI sceNetBweSetInternetConnectionTestResultIpcInt(std::int32_t firstResult, std::int32_t secondResult) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 3f5f367476..dbb3c48613 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -32,9 +32,7 @@ - [sceNetCtlApRpStartWithRetry](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStartConf](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStop](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRestart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetBweGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweSetInternetConnectionTestResultIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlGetWifiInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature ### Functional From 0e1ddf3bdf97c053ce10638790ed54b9cae02f94 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:17:06 +0300 Subject: [PATCH 231/523] fix(json): preserve value storage when swapping contents --- core/libs/prx/libSceJson2/Export.cpp | 11 ++++++++++- core/libs/tests/GuestJson.cpp | 8 ++++++++ core/libs/tests/JsonCopyFailure.cpp | 29 +++++++++++++++++++++++++++- docs/dev/debt/libSceJson2.md | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 2593044dad..7d0dc664c6 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -837,7 +837,16 @@ Value* APS5_VABI _ZN3sce4Json5ValueaSERKS1_(Value* self, const Value* other) { return self; } void APS5_VABI _ZN3sce4Json5Value4swapERS1_(Value* self, Value* other) { - std::swap(self->node, other->node); + Node temporary{}; + try { + AssignNode(temporary, NodeOf(*self)); + AssignNode(NodeOf(*self), NodeOf(*other)); + AssignNode(NodeOf(*other), temporary); + } catch (...) { + Clear(temporary); + throw; + } + Clear(temporary); } int APS5_VABI _ZN3sce4Json5Value3setENS0_9ValueTypeE(Value* self, ValueType type) { SetType(NodeOf(*self), type); return 0; } diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index f73d4bbe14..da59aa435e 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -714,6 +714,14 @@ static void ValueSwap() { _ZN3sce4Json5Value4swapERS1_(&v1, &v2); Require(*_ZNK3sce4Json5Value10getIntegerEv(&v2) == 123); Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(_ZNK3sce4Json5Value9getStringEv(&v1)), "hello") == 0); + Require(_ZN3sce4Json5Value3setEl(&v1, -456) == 0); + const auto* firstStorage = _ZNK3sce4Json5Value10getIntegerEv(&v1); + const auto* secondStorage = _ZNK3sce4Json5Value10getIntegerEv(&v2); + _ZN3sce4Json5Value4swapERS1_(&v1, &v2); + Require(_ZNK3sce4Json5Value10getIntegerEv(&v1) == firstStorage && *firstStorage == 123); + Require(_ZNK3sce4Json5Value10getIntegerEv(&v2) == secondStorage && *secondStorage == -456); + _ZN3sce4Json5Value4swapERS1_(&v1, &v1); + Require(*_ZNK3sce4Json5Value10getIntegerEv(&v1) == 123); _ZN3sce4Json5ValueD1Ev(&v1); _ZN3sce4Json5ValueD1Ev(&v2); } diff --git a/core/libs/tests/JsonCopyFailure.cpp b/core/libs/tests/JsonCopyFailure.cpp index 0fa28bad98..9707f85913 100644 --- a/core/libs/tests/JsonCopyFailure.cpp +++ b/core/libs/tests/JsonCopyFailure.cpp @@ -13,6 +13,7 @@ struct Object { void* items; }; struct Pair { String key; std::uint64_t reserved; Value value; }; extern "C" { +void APS5_VABI _ZN3sce4Json5Value4swapERS1_(Value*, Value*); void APS5_VABI _ZN3sce4Json5ValueC1Ev(Value*); void APS5_VABI _ZN3sce4Json5ValueC1EPKc(Value*, const char*); void APS5_VABI _ZN3sce4Json5ValueC1ERKNS0_6StringE(Value*, const String*); @@ -212,9 +213,35 @@ int main() { } Require(succeeded, "copy never succeeded"); } + activeCase = "value swap"; + bool swapSucceeded = false; + for (activeBudget = 0; activeBudget < 256 && !swapSucceeded; ++activeBudget) { + const auto before = liveAllocations; + Value first{}, second{}; + _ZN3sce4Json5ValueC1ERKS1_(&first, &objectValue); + _ZN3sce4Json5ValueC1ERKS1_(&second, &arrayValue); + allocationBudget = activeBudget; + try { + _ZN3sce4Json5Value4swapERS1_(&first, &second); + swapSucceeded = true; + } catch (const std::bad_alloc&) { + ++failures; + } + allocationBudget = -1; + if (swapSucceeded) { + CheckValue(first, arrayJson); + CheckValue(second, objectJson); + } + _ZN3sce4Json5ValueD1Ev(&first); + _ZN3sce4Json5ValueD1Ev(&second); + Require(liveAllocations == before, "swap leaked allocations"); + CheckValue(objectValue, objectJson); + CheckValue(arrayValue, arrayJson); + } + Require(swapSucceeded, "swap never succeeded"); _ZN3sce4Json6StringD1Ev(&text); _ZN3sce4Json5ValueD1Ev(&arrayValue); _ZN3sce4Json5ValueD1Ev(&objectValue); Require(liveAllocations == initial, "source cleanup leaked allocations"); - std::printf("JSON copy cleanup: %zu operations, %u allocation failures, no leaks\n", sizeof(cases) / sizeof(cases[0]), failures); + std::printf("JSON copy cleanup: %zu operations, %u allocation failures, no leaks\n", sizeof(cases) / sizeof(cases[0]) + 1, failures); } diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 6aef517355..40f236efab 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -7,5 +7,5 @@ - [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown -- [sce::Json::String size, capacity, resize, append(const char*, size_t), operator+=; Value::swap; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `Value::swap` exchanges the node pointers; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified +- [sce::Json::String size, capacity, resize, append(const char*, size_t), operator+=; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - callback dispatch before conversion is not implemented. The system library invokes the applicable type mismatch handler before converting a non-boolean value, with SDK-dependent global callback handling. From ce03b9c68a75eb356112564e113b0b2889211127 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:22:09 +0300 Subject: [PATCH 232/523] fix(json): restore array iterator lifetime and end handling --- core/libs/prx/libSceJson2/Export.cpp | 57 +++++++++++++++++++++------- core/libs/tests/GuestJson.cpp | 16 +++++++- docs/dev/debt/libSceJson2.md | 5 ++- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 7d0dc664c6..369597ce8f 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -34,7 +34,11 @@ struct String { std::string* text; }; struct Array { std::list* items; }; struct Pair; struct Object { std::list* items; }; -struct ArrayIterator { std::list::iterator it; }; +struct ArrayIteratorState { + std::list::iterator position; + std::list::iterator end; +}; +struct ArrayIterator { ArrayIteratorState* state; }; struct ObjectIterator { std::list::iterator it; }; struct Pair { String key; @@ -726,8 +730,8 @@ void APS5_VABI _ZN3sce4Json5Array9push_backERKNS0_5ValueE(Array* self, const Val throw; } } -ArrayIterator* APS5_VABI _ZNK3sce4Json5Array5beginEv(ArrayIterator* result, const Array* self) { result->it = self->items->begin(); return result; } -ArrayIterator* APS5_VABI _ZNK3sce4Json5Array3endEv(ArrayIterator* result, const Array* self) { result->it = self->items->end(); return result; } +ArrayIterator* APS5_VABI _ZNK3sce4Json5Array5beginEv(ArrayIterator* result, const Array* self) { result->state = new ArrayIteratorState{self->items->begin(), self->items->end()}; return result; } +ArrayIterator* APS5_VABI _ZNK3sce4Json5Array3endEv(ArrayIterator* result, const Array* self) { result->state = new ArrayIteratorState{self->items->end(), self->items->end()}; return result; } bool APS5_VABI _ZNK3sce4Json5Array5emptyEv(const Array* self) { return self->items->empty(); } std::size_t APS5_VABI _ZNK3sce4Json5Array4sizeEv(const Array* self) { return self->items->size(); } void APS5_VABI _ZN3sce4Json5Array5clearEv(Array* self) { @@ -742,14 +746,32 @@ const Value* APS5_VABI _ZNK3sce4Json5Array4backEv(const Array* self) { if (self->items->empty()) throw std::out_of_range("sce::Json::Array::back: empty array"); return &self->items->back(); } -void APS5_VABI _ZN3sce4Json5Array8iteratorD1Ev(ArrayIterator* self) { (void)self; } -ArrayIterator* APS5_VABI _ZN3sce4Json5Array8iteratorppEv(ArrayIterator* self) { ++self->it; return self; } -Value* APS5_VABI _ZNK3sce4Json5Array8iteratordeEv(const ArrayIterator* self) { return &*self->it; } -Value* APS5_VABI _ZNK3sce4Json5Array8iteratorptEv(const ArrayIterator* self) { return &*self->it; } -bool APS5_VABI _ZNK3sce4Json5Array8iteratorneERKS2_(const ArrayIterator* self, const ArrayIterator* other) { return self->it != other->it; } -void APS5_VABI _ZN3sce4Json5Array8iteratorC1ERKS2_(ArrayIterator* self, const ArrayIterator* other) { self->it = other->it; } +void APS5_VABI _ZN3sce4Json5Array8iteratorD1Ev(ArrayIterator* self) { delete self->state; self->state = nullptr; } +ArrayIterator* APS5_VABI _ZN3sce4Json5Array8iteratorppEv(ArrayIterator* self) { + if (self->state && self->state->position != self->state->end) ++self->state->position; + return self; +} +Value* APS5_VABI _ZNK3sce4Json5Array8iteratordeEv(const ArrayIterator* self) { + if (!self->state || self->state->position == self->state->end) throw std::out_of_range("sce::Json::Array::iterator: no current element"); + return &*self->state->position; +} +Value* APS5_VABI _ZNK3sce4Json5Array8iteratorptEv(const ArrayIterator* self) { return _ZNK3sce4Json5Array8iteratordeEv(self); } +bool APS5_VABI _ZNK3sce4Json5Array8iteratorneERKS2_(const ArrayIterator* self, const ArrayIterator* other) { + if (!self->state || !other->state) return false; + const Value* left = self->state && self->state->position != self->state->end ? &*self->state->position : nullptr; + const Value* right = other->state && other->state->position != other->state->end ? &*other->state->position : nullptr; + return left != right; +} +void APS5_VABI _ZN3sce4Json5Array8iteratorC1ERKS2_(ArrayIterator* self, const ArrayIterator* other) { + self->state = other->state ? new ArrayIteratorState(*other->state) : nullptr; +} ArrayIterator* APS5_VABI _ZN3sce4Json5Array8iterator7advanceEm(ArrayIterator* self, std::size_t count) { - std::advance(self->it, count); + if (self->state) { + while (count && self->state->position != self->state->end) { + ++self->state->position; + --count; + } + } return self; } @@ -1160,18 +1182,25 @@ ObjectIterator* APS5_VABI _ZN3sce4Json6Object6insertERKNS1_4PairE(ObjectIterator } ArrayIterator* APS5_VABI _ZN3sce4Json5Array8iteratorppEi(ArrayIterator* result, ArrayIterator* self, int) { - result->it = self->it; - ++self->it; + _ZN3sce4Json5Array8iteratorC1ERKS2_(result, self); + _ZN3sce4Json5Array8iteratorppEv(self); return result; } ArrayIterator* APS5_VABI _ZN3sce4Json5Array8iteratoraSERKS2_(ArrayIterator* self, const ArrayIterator* other) { - self->it = other->it; + if (self->state) { + if (other->state && other->state->position != other->state->end) { + *self->state = *other->state; + } else { + delete self->state; + self->state = nullptr; + } + } return self; } void APS5_VABI _ZN3sce4Json5Array8iteratorC1Ev(ArrayIterator* self) { - self->it = {}; + self->state = new ArrayIteratorState{}; } ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iteratorppEi(ObjectIterator* result, ObjectIterator* self, int) { diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index da59aa435e..3baa092693 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -400,7 +400,7 @@ static void ArrayIteratorCopies() { Iterator it{}; std::memset(&it, 0x5a, sizeof(it)); _ZN3sce4Json5Array8iteratorC1Ev(&it); - Require(it.position == nullptr); + Require(it.position != nullptr); Iterator begin{}, end{}; _ZNK3sce4Json5Array5beginEv(&begin, &array); @@ -411,6 +411,7 @@ static void ArrayIteratorCopies() { std::vector seen; Iterator previous{}; while (_ZNK3sce4Json5Array8iteratorneERKS2_(&it, &end)) { + _ZN3sce4Json5Array8iteratorD1Ev(&previous); Require(_ZN3sce4Json5Array8iteratorppEi(&previous, &it, 0) == &previous); seen.push_back(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5Array8iteratordeEv(&previous))); Require(_ZNK3sce4Json5Array8iteratorneERKS2_(&previous, &it)); @@ -418,6 +419,11 @@ static void ArrayIteratorCopies() { Require((seen == std::vector{1, 2, 3})); Require(!_ZNK3sce4Json5Array8iteratorneERKS2_(&it, &end)); Require(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5Array8iteratordeEv(&previous)) == 3); + Require(_ZN3sce4Json5Array8iteratoraSERKS2_(&it, &end) == &it); + Require(it.position == nullptr); + Require(_ZN3sce4Json5Array8iteratoraSERKS2_(&it, &begin) == &it); + Require(it.position == nullptr); + Require(!_ZNK3sce4Json5Array8iteratorneERKS2_(&it, &begin)); _ZN3sce4Json5Array8iteratorD1Ev(&previous); _ZN3sce4Json5Array8iteratorD1Ev(&it); @@ -742,6 +748,14 @@ static void ArrayIteratorAdvance() { _ZN3sce4Json5Array8iteratorC1ERKS2_(©, &begin); Require(_ZN3sce4Json5Array8iterator7advanceEm(©, 3) == ©); Require(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5Array8iteratordeEv(©)) == 30); + Iterator end{}; + _ZNK3sce4Json5Array3endEv(&end, &array); + _ZN3sce4Json5Array8iterator7advanceEm(©, std::numeric_limits::max()); + Require(!_ZNK3sce4Json5Array8iteratorneERKS2_(©, &end)); + _ZN3sce4Json5Array8iteratorppEv(©); + Require(!_ZNK3sce4Json5Array8iteratorneERKS2_(©, &end)); + Require(*_ZNK3sce4Json5Value10getIntegerEv(_ZNK3sce4Json5Array8iteratordeEv(&begin)) == 0); + _ZN3sce4Json5Array8iteratorD1Ev(&end); _ZN3sce4Json5Array8iteratorD1Ev(&begin); _ZN3sce4Json5Array8iteratorD1Ev(©); _ZN3sce4Json5ArrayD1Ev(&array); diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index 40f236efab..cadf462424 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -4,8 +4,9 @@ - [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown. Syntax validation for number tokens and destination state after a failed parse still differ from the system parser - [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - RTTI allocator adaptation and the `InitParameterRtti` layout remain unverified. `setAllocatorRtti` stores the raw allocation-parameter pointer as the allocator; `initialize` ignores allocator, buffer-size and special-float settings. Special-float enum meanings and parser support for non-finite numbers remain unresolved; serialization of non-finite values throws. Setter return types remain unverified. -- [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed iterators are singular +- [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed object iterators are singular - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown -- [sce::Json::String size, capacity, resize, append(const char*, size_t), operator+=; Array::iterator and Object::iterator copy constructor, advance and operator==](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names and the behaviour is that of the matching `std::string` or container operation, with no cited source in KytyPS5, shadPS4, SharpProspero or disassembly. `append(text, length)` throws on a null text with a non-zero length; `operator+=` returns the String; `advance` moves the iterator in place, returns it, and does not check the distance against the end. Whether the console returns the iterator from `advance` and how it bounds it is unverified +- [sce::Json::String size, capacity, resize, append(const char*, size_t), operator+=](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - storage uses `std::string`; capacity and allocation growth do not match the system 32-bit capacity and allocator settings. Appending null text with a nonzero length throws instead of dereferencing it. +- [sce::Json::Array::iterator::advance and Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the return type of `advance` remains unverified. Object iterator advancement has no end check, while the system asserts on advancing past the end. Dereferencing an empty array iterator throws instead of returning the system null-value pointer or an invalid address. Object iterators still use host iterator storage and lifetime semantics. - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - callback dispatch before conversion is not implemented. The system library invokes the applicable type mismatch handler before converting a non-boolean value, with SDK-dependent global callback handling. From b19f2227df8f0a49d2e146fea482a2608e5d5fc7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:29:41 +0300 Subject: [PATCH 233/523] fix(json): restore object iterator ownership and bounds --- core/libs/prx/libSceJson2/Export.cpp | 74 +++++++++++++++++++++------- core/libs/tests/GuestJson.cpp | 14 +++++- core/libs/tests/JsonCopyFailure.cpp | 37 +++++++++++++- docs/dev/debt/libSceJson2.md | 4 +- 4 files changed, 107 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libSceJson2/Export.cpp b/core/libs/prx/libSceJson2/Export.cpp index 369597ce8f..5b92c48d68 100644 --- a/core/libs/prx/libSceJson2/Export.cpp +++ b/core/libs/prx/libSceJson2/Export.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "SceTypes.hpp" @@ -39,7 +40,11 @@ struct ArrayIteratorState { std::list::iterator end; }; struct ArrayIterator { ArrayIteratorState* state; }; -struct ObjectIterator { std::list::iterator it; }; +struct ObjectIteratorState { + std::list* owner = nullptr; + std::list::iterator position{}; +}; +struct ObjectIterator { ObjectIteratorState* state; }; struct Pair { String key; std::uint64_t reserved; @@ -794,21 +799,44 @@ void APS5_VABI _ZN3sce4Json6Object5clearEv(Object* self) { self->items->clear(); } Value* APS5_VABI _ZN3sce4Json6ObjectixERKNS0_6StringE(Object* self, const String* key) { return &ObjectEntry(*self, *key->text); } -ObjectIterator* APS5_VABI _ZNK3sce4Json6Object5beginEv(ObjectIterator* result, const Object* self) { result->it = self->items->begin(); return result; } -ObjectIterator* APS5_VABI _ZNK3sce4Json6Object3endEv(ObjectIterator* result, const Object* self) { result->it = self->items->end(); return result; } -void APS5_VABI _ZN3sce4Json6Object8iteratorD1Ev(ObjectIterator* self) { (void)self; } -ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iteratorppEv(ObjectIterator* self) { ++self->it; return self; } -Pair* APS5_VABI _ZNK3sce4Json6Object8iteratordeEv(const ObjectIterator* self) { return &*self->it; } -Pair* APS5_VABI _ZNK3sce4Json6Object8iteratorptEv(const ObjectIterator* self) { return &*self->it; } +ObjectIterator* APS5_VABI _ZNK3sce4Json6Object5beginEv(ObjectIterator* result, const Object* self) { result->state = new ObjectIteratorState{self->items, self->items->begin()}; return result; } +ObjectIterator* APS5_VABI _ZNK3sce4Json6Object3endEv(ObjectIterator* result, const Object* self) { result->state = new ObjectIteratorState{self->items, self->items->end()}; return result; } +void APS5_VABI _ZN3sce4Json6Object8iteratorD1Ev(ObjectIterator* self) { delete self->state; self->state = nullptr; } +ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iteratorppEv(ObjectIterator* self) { + if (self->state && self->state->owner) { + if (self->state->position == self->state->owner->end()) throw std::out_of_range("sce::Json::Object::iterator: advance past the end"); + ++self->state->position; + } + return self; +} +Pair* APS5_VABI _ZNK3sce4Json6Object8iteratordeEv(const ObjectIterator* self) { + if (!self->state || !self->state->owner || self->state->position == self->state->owner->end()) throw std::out_of_range("sce::Json::Object::iterator: no current element"); + return &*self->state->position; +} +Pair* APS5_VABI _ZNK3sce4Json6Object8iteratorptEv(const ObjectIterator* self) { return _ZNK3sce4Json6Object8iteratordeEv(self); } ObjectIterator* APS5_VABI _ZNK3sce4Json6Object4findERKNS0_6StringE(ObjectIterator* result, const Object* self, const String* key) { - result->it = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key->text->c_str()) == 0; }); + const auto position = std::find_if(self->items->begin(), self->items->end(), [&](const Pair& pair) { return std::strcmp(pair.key.text->c_str(), key->text->c_str()) == 0; }); + result->state = new ObjectIteratorState{self->items, position}; return result; } -bool APS5_VABI _ZNK3sce4Json6Object8iteratorneERKS2_(const ObjectIterator* self, const ObjectIterator* other) { return self->it != other->it; } -bool APS5_VABI _ZNK3sce4Json6Object8iteratoreqERKS2_(const ObjectIterator* self, const ObjectIterator* other) { return self->it == other->it; } -void APS5_VABI _ZN3sce4Json6Object8iteratorC1ERKS2_(ObjectIterator* self, const ObjectIterator* other) { self->it = other->it; } +bool APS5_VABI _ZNK3sce4Json6Object8iteratoreqERKS2_(const ObjectIterator* self, const ObjectIterator* other) { + if (!self->state || !other->state) return false; + return self->state->owner == other->state->owner && self->state->position == other->state->position; +} +bool APS5_VABI _ZNK3sce4Json6Object8iteratorneERKS2_(const ObjectIterator* self, const ObjectIterator* other) { + if (!self->state || !other->state) return false; + return !_ZNK3sce4Json6Object8iteratoreqERKS2_(self, other); +} +void APS5_VABI _ZN3sce4Json6Object8iteratorC1ERKS2_(ObjectIterator* self, const ObjectIterator* other) { + self->state = other->state ? new ObjectIteratorState(*other->state) : nullptr; +} ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iterator7advanceEm(ObjectIterator* self, std::size_t count) { - std::advance(self->it, count); + if (self->state && self->state->owner) { + while (count) { + _ZN3sce4Json6Object8iteratorppEv(self); + --count; + } + } return self; } bool APS5_VABI _ZNK3sce4Json6Object5emptyEv(const Object* self) { return self->items->empty(); } @@ -1163,8 +1191,9 @@ void APS5_VABI _ZN3sce4Json6Object4PairD1Ev(Pair* self) { ObjectIterator* APS5_VABI _ZN3sce4Json6Object6insertERKNS1_4PairE(ObjectIterator* result, Object* self, const Pair* pair) { auto it = ObjectPosition(*self, *pair->key.text); + auto state = std::make_unique(ObjectIteratorState{self->items, it}); if (it != self->items->end() && std::strcmp(it->key.text->c_str(), pair->key.text->c_str()) == 0) { - result->it = it; + result->state = state.release(); return result; } Pair copy{}; @@ -1172,7 +1201,8 @@ ObjectIterator* APS5_VABI _ZN3sce4Json6Object6insertERKNS1_4PairE(ObjectIterator Construct(copy.key, *pair->key.text); try { CopyInto(copy.value, pair->value); - result->it = self->items->insert(it, copy); + state->position = self->items->insert(it, copy); + result->state = state.release(); return result; } catch (...) { Destroy(copy.value); @@ -1204,18 +1234,26 @@ void APS5_VABI _ZN3sce4Json5Array8iteratorC1Ev(ArrayIterator* self) { } ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iteratorppEi(ObjectIterator* result, ObjectIterator* self, int) { - result->it = self->it; - ++self->it; + auto state = self->state ? std::make_unique(*self->state) : nullptr; + _ZN3sce4Json6Object8iteratorppEv(self); + result->state = state.release(); return result; } ObjectIterator* APS5_VABI _ZN3sce4Json6Object8iteratoraSERKS2_(ObjectIterator* self, const ObjectIterator* other) { - self->it = other->it; + if (self->state) { + if (other->state && other->state->owner) { + *self->state = *other->state; + } else { + delete self->state; + self->state = nullptr; + } + } return self; } void APS5_VABI _ZN3sce4Json6Object8iteratorC1Ev(ObjectIterator* self) { - self->it = {}; + self->state = new ObjectIteratorState{}; } void APS5_VABI _ZN3sce4Json6Object4PairC1Ev(Pair* self) { diff --git a/core/libs/tests/GuestJson.cpp b/core/libs/tests/GuestJson.cpp index 3baa092693..c20ffcbd83 100644 --- a/core/libs/tests/GuestJson.cpp +++ b/core/libs/tests/GuestJson.cpp @@ -457,6 +457,7 @@ static void ObjectFind() { Value* second = _ZN3sce4Json6ObjectixERKNS0_6StringE(&object, &beta); Require(_ZN3sce4Json5Value3setEPKc(second, "b") == 0); _ZNK3sce4Json6Object3endEv(&end, &object); + _ZN3sce4Json6Object8iteratorD1Ev(&found); Iterator* returned = _ZNK3sce4Json6Object4findERKNS0_6StringE(&found, &object, &beta); Require(returned == &found); Require(_ZNK3sce4Json6Object8iteratorneERKS2_(&found, &end)); @@ -466,6 +467,7 @@ static void ObjectFind() { Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(&pair->key), "beta") == 0); _ZN3sce4Json6Object8iteratorppEv(&found); Require(!_ZNK3sce4Json6Object8iteratorneERKS2_(&found, &end)); + _ZN3sce4Json6Object8iteratorD1Ev(&found); _ZNK3sce4Json6Object4findERKNS0_6StringE(&found, &object, &gamma); Require(!_ZNK3sce4Json6Object8iteratorneERKS2_(&found, &end)); Require(_ZNK3sce4Json6Object4sizeEv(&object) == 2); @@ -512,7 +514,8 @@ static void ObjectIteratorCopies() { Iterator it{}; std::memset(&it, 0x5a, sizeof(it)); _ZN3sce4Json6Object8iteratorC1Ev(&it); - Require(it.position == nullptr); + Require(it.position != nullptr); + _ZN3sce4Json6Object8iteratorD1Ev(&it); Iterator begin{}, end{}; _ZNK3sce4Json6Object5beginEv(&begin, &object); @@ -528,6 +531,7 @@ static void ObjectIteratorCopies() { _ZN3sce4Json6Object8iteratorD1Ev(&post); Iterator assigned{}; + _ZN3sce4Json6Object8iteratorC1Ev(&assigned); Require(_ZN3sce4Json6Object8iteratoraSERKS2_(&assigned, &begin) == &assigned); Require(_ZNK3sce4Json6Object8iteratoreqERKS2_(&assigned, &begin)); Require(!_ZNK3sce4Json6Object8iteratorneERKS2_(&assigned, &begin)); @@ -793,6 +797,14 @@ static void ObjectOperations() { _ZN3sce4Json6Object8iteratorC1ERKS2_(©, &begin); Require(_ZN3sce4Json6Object8iterator7advanceEm(©, 2) == ©); Require(std::strcmp(_ZNK3sce4Json6String5c_strEv(&_ZNK3sce4Json6Object8iteratordeEv(©)->key), "gamma") == 0); + _ZN3sce4Json6Object8iterator7advanceEm(©, 1); + bool endRejected = false; + try { + _ZN3sce4Json6Object8iterator7advanceEm(©, 1); + } catch (const std::out_of_range&) { + endRejected = true; + } + Require(endRejected); _ZN3sce4Json6Object8iteratorD1Ev(&begin); _ZN3sce4Json6Object8iteratorD1Ev(©); diff --git a/core/libs/tests/JsonCopyFailure.cpp b/core/libs/tests/JsonCopyFailure.cpp index 9707f85913..6f95b142e3 100644 --- a/core/libs/tests/JsonCopyFailure.cpp +++ b/core/libs/tests/JsonCopyFailure.cpp @@ -5,14 +5,22 @@ #include #include #include +#include struct Value { void* node; }; struct String { void* text; }; struct Array { void* items; }; struct Object { void* items; }; +struct Iterator { void* state; }; struct Pair { String key; std::uint64_t reserved; Value value; }; extern "C" { +Iterator* APS5_VABI _ZNK3sce4Json5Array5beginEv(Iterator*, const Array*); +Iterator* APS5_VABI _ZNK3sce4Json6Object5beginEv(Iterator*, const Object*); +void APS5_VABI _ZN3sce4Json5Array8iteratorC1ERKS2_(Iterator*, const Iterator*); +void APS5_VABI _ZN3sce4Json6Object8iteratorC1ERKS2_(Iterator*, const Iterator*); +void APS5_VABI _ZN3sce4Json5Array8iteratorD1Ev(Iterator*); +void APS5_VABI _ZN3sce4Json6Object8iteratorD1Ev(Iterator*); void APS5_VABI _ZN3sce4Json5Value4swapERS1_(Value*, Value*); void APS5_VABI _ZN3sce4Json5ValueC1Ev(Value*); void APS5_VABI _ZN3sce4Json5ValueC1EPKc(Value*, const char*); @@ -213,6 +221,33 @@ int main() { } Require(succeeded, "copy never succeeded"); } + for (bool objectIterator : {false, true}) { + activeCase = objectIterator ? "object iterator copy" : "array iterator copy"; + const auto before = liveAllocations; + Iterator source{}; + if (objectIterator) _ZNK3sce4Json6Object5beginEv(&source, object); + else _ZNK3sce4Json5Array5beginEv(&source, array); + for (activeBudget = 0; activeBudget <= 1; ++activeBudget) { + Iterator copy{}; + bool succeeded = false; + allocationBudget = activeBudget; + try { + if (objectIterator) _ZN3sce4Json6Object8iteratorC1ERKS2_(©, &source); + else _ZN3sce4Json5Array8iteratorC1ERKS2_(©, &source); + succeeded = true; + } catch (const std::bad_alloc&) { + ++failures; + } + allocationBudget = -1; + Require(succeeded == (activeBudget == 1), "iterator allocation failure ignored"); + if (succeeded) Require(copy.state != source.state, "iterator copy shares owned storage"); + if (objectIterator) _ZN3sce4Json6Object8iteratorD1Ev(©); + else _ZN3sce4Json5Array8iteratorD1Ev(©); + } + if (objectIterator) _ZN3sce4Json6Object8iteratorD1Ev(&source); + else _ZN3sce4Json5Array8iteratorD1Ev(&source); + Require(liveAllocations == before, "iterator copy leaked allocations"); + } activeCase = "value swap"; bool swapSucceeded = false; for (activeBudget = 0; activeBudget < 256 && !swapSucceeded; ++activeBudget) { @@ -243,5 +278,5 @@ int main() { _ZN3sce4Json5ValueD1Ev(&arrayValue); _ZN3sce4Json5ValueD1Ev(&objectValue); Require(liveAllocations == initial, "source cleanup leaked allocations"); - std::printf("JSON copy cleanup: %zu operations, %u allocation failures, no leaks\n", sizeof(cases) / sizeof(cases[0]) + 1, failures); + std::printf("JSON copy cleanup: %zu operations, %u allocation failures, no leaks\n", sizeof(cases) / sizeof(cases[0]) + 3, failures); } diff --git a/docs/dev/debt/libSceJson2.md b/docs/dev/debt/libSceJson2.md index cadf462424..4868db14d0 100644 --- a/docs/dev/debt/libSceJson2.md +++ b/docs/dev/debt/libSceJson2.md @@ -4,9 +4,9 @@ - [sce::Json::Parser::parse](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a null text pointer with nonzero size throws instead of being dereferenced. A number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown. Syntax validation for number tokens and destination state after a failed parse still differ from the system parser - [sce::Json::InitParameter2, InitParameterRtti and InitParameterRtti2](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - RTTI allocator adaptation and the `InitParameterRtti` layout remain unverified. `setAllocatorRtti` stores the raw allocation-parameter pointer as the allocator; `initialize` ignores allocator, buffer-size and special-float settings. Special-float enum meanings and parser support for non-finite numbers remain unresolved; serialization of non-finite values throws. Setter return types remain unverified. -- [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. Default-constructed object iterators are singular +- [sce::Json::Object::Pair, sce::Json::Array::iterator and sce::Json::Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the signatures follow the mangled names, with `Array::iterator::operator++(int)` and `Object::iterator::operator++(int)` returning their copy through a result pointer before `this` as `begin` does, since the iterators have non-trivial destructors. `Pair(const String&, const Value&)` and default `Pair()` deep-copy or initialize into the layout `Object` iteration already hands out (key at 0, value at 0x10) and zeroes the 8 bytes between them; what the console keeps there is unknown. - [sce::Json::Value::setTypeMismatchHandler, setElementAccessFailureHandler and setSpecialFloatHandler](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - members of `Value` that return an int, inferred from a caller that registers them on its root `Value` before parsing into it, with the handler and its context, and accepts 0 or 0x80848104 as success; they return 0. The PS Vita library's `Value::setNullAccessCallback` is such a member too ([vitasdk](https://github.com/vitasdk/vita-headers/blob/15605617cbbb23d266f88af28899fdb573e2355e/include/psp2/json.h#L638)). The handler signatures follow the mangled names. A handler applies to the `Value` it is set on and to the values that `Value` holds then, and `Parser::parse` gives the values it creates the handlers of the `Value` it parses into, since the title reads the errors its handlers record after accessing parsed members; copies, `push_back` and `Object::operator[]` do not carry them, and how the console finds the handler of an accessed value is unknown. A typed getter on a non-null value of another type calls the type mismatch handler with the requested type, the value, null and the context, and returns the value the handler returns if it has the requested type, else the getter's default; passing null as the third handler argument (the second `Value` pointer), and the order of the two `Value` pointers, remain unverified against the console. The element access failure handler is not called: a failed `operator[]` or `getValue` on a value carrying it throws, as the error code the console passes, which the title records as its error, is unknown; without one the access still reaches the null-access callback. The special float handler is not called: serializing a non-finite real throws with or without it, as the `FunctionType` values and the `bool*` output are unknown, and the title's handler returns a different replacement for each of its two types - [sce::Json::MemAllocator::notifyError](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the base class's virtual does nothing. A title's allocator that does not override it calls it through its own vtable; the library never calls the title's allocator, so it never reports an allocation failure. Whether the console's base implementation logs anything is unknown - [sce::Json::String size, capacity, resize, append(const char*, size_t), operator+=](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - storage uses `std::string`; capacity and allocation growth do not match the system 32-bit capacity and allocator settings. Appending null text with a nonzero length throws instead of dereferencing it. -- [sce::Json::Array::iterator::advance and Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the return type of `advance` remains unverified. Object iterator advancement has no end check, while the system asserts on advancing past the end. Dereferencing an empty array iterator throws instead of returning the system null-value pointer or an invalid address. Object iterators still use host iterator storage and lifetime semantics. +- [sce::Json::Array::iterator::advance and Object::iterator members](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - the return type of `advance` remains unverified. Object iterator advancement past the end throws instead of asserting. Dereferencing an empty array iterator throws instead of returning the system null-value pointer or an invalid address. - [sce::Json::Value::operator bool](../../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - callback dispatch before conversion is not implemented. The system library invokes the applicable type mismatch handler before converting a non-boolean value, with SDK-dependent global callback handling. From daaa00c99140578048562642f33033030f12d7ba Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:33:40 +0300 Subject: [PATCH 234/523] fix(psml): restore mfsr2 context and dispatch signatures --- core/libs/prx/libScePsml_debug/Export.cpp | 67 ++++++++++++++++++++++- docs/dev/debt/libScePsml_debug.md | 2 +- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libScePsml_debug/Export.cpp b/core/libs/prx/libScePsml_debug/Export.cpp index 524e34549c..44021762c2 100644 --- a/core/libs/prx/libScePsml_debug/Export.cpp +++ b/core/libs/prx/libScePsml_debug/Export.cpp @@ -56,6 +56,67 @@ static_assert(offsetof(Mfsr2SharedResourcesParam, blockCount) == 16); static_assert(offsetof(Mfsr2SharedResourcesParam, width) == 24); static_assert(offsetof(Mfsr2SharedResourcesParam, reserved1) == 28); +struct Mfsr2ContextParam { + std::uint32_t profile; + std::uint32_t reserved0; + void* sharedResources; + const MfsrBlock* blocks; + std::uint64_t blockCount; + std::uint64_t reserved1; + std::uint16_t outputWidth; + std::uint16_t outputHeight; + std::uint32_t inputColorSpace; +}; + +struct MfsrCommandBuffer { + std::uint32_t* cursor; + std::uint32_t sizeInDwords; +}; + +struct MfsrTextureDescriptor { + std::uint32_t words[8]; +}; + +struct Mfsr2DispatchParam { + const MfsrTextureDescriptor* outputColor; + const MfsrTextureDescriptor* color; + const MfsrTextureDescriptor* motionVectors; + const MfsrTextureDescriptor* depth; + const MfsrTextureDescriptor* exposure; + std::uint64_t reserved0; + float preExposure; + std::uint32_t renderX; + std::uint32_t renderY; + std::uint32_t renderWidth; + std::uint32_t renderHeight; + float jitter[2]; + float motionVectorScale[2]; + float motionVectorOffset[2]; + float depthRange[2]; + std::uint8_t resetHistory; + std::uint8_t reserved1[3]; + std::uint32_t optionFlags; + float sharpness; +}; + +static_assert(sizeof(Mfsr2ContextParam) == 48); +static_assert(offsetof(Mfsr2ContextParam, sharedResources) == 8); +static_assert(offsetof(Mfsr2ContextParam, blocks) == 16); +static_assert(offsetof(Mfsr2ContextParam, blockCount) == 24); +static_assert(offsetof(Mfsr2ContextParam, outputWidth) == 40); +static_assert(offsetof(Mfsr2ContextParam, inputColorSpace) == 44); +static_assert(sizeof(MfsrCommandBuffer) == 16); +static_assert(offsetof(MfsrCommandBuffer, sizeInDwords) == 8); +static_assert(sizeof(MfsrTextureDescriptor) == 32); +static_assert(sizeof(Mfsr2DispatchParam) == 112); +static_assert(offsetof(Mfsr2DispatchParam, preExposure) == 48); +static_assert(offsetof(Mfsr2DispatchParam, renderX) == 52); +static_assert(offsetof(Mfsr2DispatchParam, jitter) == 68); +static_assert(offsetof(Mfsr2DispatchParam, depthRange) == 92); +static_assert(offsetof(Mfsr2DispatchParam, resetHistory) == 100); +static_assert(offsetof(Mfsr2DispatchParam, optionFlags) == 104); +static_assert(offsetof(Mfsr2DispatchParam, sharpness) == 108); + struct MfsrContextParam { std::uint32_t reserved0[2]; const MfsrBlock* blocks; @@ -301,7 +362,7 @@ int APS5_VABI scePsmlMfsr2GetSharedResourcesInitRequirement(MfsrRequirement* req return 0; } -int APS5_VABI scePsmlMfsr2GetContextInitRequirement() { +int APS5_VABI scePsmlMfsr2GetContextInitRequirement(MfsrRequirement* requirement, const Mfsr2ContextParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -321,12 +382,12 @@ int APS5_VABI scePsmlMfsr2CreateSharedResources(void** sharedResources, const Mf return 0; } -int APS5_VABI scePsmlMfsr2GetDispatchPackets() { +int APS5_VABI scePsmlMfsr2GetDispatchPackets(void* context, const MfsrCommandBuffer* commandBuffer, const Mfsr2DispatchParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePsmlMfsr2CreateContext() { +int APS5_VABI scePsmlMfsr2CreateContext(void** context, const Mfsr2ContextParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePsml_debug.md b/docs/dev/debt/libScePsml_debug.md index 5b0a539687..f77c32d0b1 100644 --- a/docs/dev/debt/libScePsml_debug.md +++ b/docs/dev/debt/libScePsml_debug.md @@ -3,7 +3,7 @@ ### Unknown function info - [scePsmlMfsrGetSharedResourcesInitRequirement, scePsmlMfsrCreateSharedResources, scePsmlMfsrReleaseSharedResources, scePsmlMfsrGetContextBufferRequirement800M3_2, scePsmlMfsrCreateContext800M3_2, scePsmlMfsrReleaseContext](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - only resource type 0 is supported. The context requirement reports four 2 MiB blocks regardless of its parameters, while the system calculation depends on the context configuration. The descriptor pointer at 0x28 is not read. Validation throws for null pointers, short block lists and foreign handles instead of reproducing the system error and fault behaviour. -- [scePsmlMfsr2CreateContext, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters +- [scePsmlMfsr2GetDispatchPacketsSizeInDwords](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - writes a 32-bit size through the second argument; the unused first argument still needs caller verification. - [scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_debug) - argument layouts unknown; PPSA26344 imports but does not call the first two and at `0x87EFA` passes `(rdi, rsi=&stack, rdx)` as three pointers to `GetDispatchMfsrPacket1100`; before `scePsmlMfsrInit` they return 0x8A810001 (not initialized) and after it they throw. `scePsmlMfsrCreateContext1300` and `scePsmlMfsrGetDispatchMfsrPacket900`, `1000` and `1300` take the same shapes (context slot and parameter; context, command buffer and parameter) and behave the same way. PPSA08330 and PPSA03016 pass `GetDispatchMfsrPacket900`/`1000` the context handle, a `{cursor, size in dwords}` command buffer and the dispatch parameters, and advance their cursor by the size `scePsmlMfsrGetDispatchMfsrPacketSizeInDwords` returned. Whether the 1100 and 1300 versions take these arguments, and the console's error, are unverified - [scePsmlMfsrGetDispatchMfsrPacket1000](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA25344. libScePsml and libScePsml_debug are built from the same source - [scePsmlMfsrCreateContext1300, scePsmlMfsrGetDispatchMfsrPacket1300](../../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml) - imported by PPSA28416 next to `scePsmlMfsrGetContextBufferRequirement1100`. The names are not in the aerolib NID list; they hash to the imported NIDs `kLF7WtQ1-Dg` and `DDVUyPyYHTA` (`tools/nid_names.py`) From 0f6161f885f3728f614dfc2ba9861e47fa53fdd0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:35:13 +0300 Subject: [PATCH 235/523] fix(net): enforce initialization for sleep and random calls --- core/libs/prx/libSceNet/Export.cpp | 6 +++++- core/libs/tests/GuestSceNet.cpp | 6 ++++++ docs/dev/debt/libSceNet.md | 5 +---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 5214a1b339..36704406eb 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2235,6 +2235,7 @@ int APS5_VABI sceNetGetNameToIndex(std::uint32_t interfaceId) { } int APS5_VABI sceNetGetRandom(std::uint32_t* output) { + if (!g_net_inited) return fail(NET_ENOTINIT); if (output == nullptr) return fail(NET_EINVAL); static std::mutex mutex; static std::random_device device; @@ -2371,8 +2372,11 @@ int APS5_VABI sceNetThreadJoin(void* thread) { } int APS5_VABI sceNetUsleep(int microseconds) { + if (!g_net_inited) return fail(NET_ENOTINIT); if (microseconds < 0) return fail(NET_EINVAL); - return sceKernelUsleep_nid_postfix(static_cast(microseconds)); + const int result = sceKernelUsleep_nid_postfix(static_cast(microseconds)); + if (result < 0) set_errno((result & 0xFF00) == 0x100 ? result & 0xFF : 205); + return result; } int APS5_VABI sceNetShowIfconfigForBuffer(char* buffer, std::int32_t bufferSize, std::int32_t poolId) { diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 52b2c027b0..713a2c0186 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -1443,6 +1443,12 @@ int main() { Require(sceNetClearDnsCache(flags) == static_cast(0x804101C8)); Require(*sceNetErrnoLoc() == 200); } + std::uint32_t randomOutput = 0x12345678; + Require(Failed(sceNetGetRandom(&randomOutput), 200)); + Require(randomOutput == 0x12345678); + Require(Failed(sceNetGetRandom(nullptr), 200)); + Require(Failed(sceNetUsleep(0), 200)); + Require(Failed(sceNetUsleep(-1), 200)); Require(sceNetInit_nid_postfix() == 0); CheckEpollWritable(); CheckSocketHostErrors(); diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 6d3285577b..7f908bd380 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -26,10 +26,8 @@ - [sceNetGetIfList](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetIfListEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetIfListOnce](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetIfName](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t index)` signature and the table of ten interface names (`lo0`, `eth0`, `eth1`, `dbg0`, `wlan0`, `wlan1`, `gbe0`, `bt0`, `phone0`, `pppoe0`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); an index past the table sets `EINVAL` and returns an empty string. Whether the console has more names, or returns null, is unverified on hardware -- [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(uint32_t* output)` signature and the `EINVAL` failure for a null output follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the value comes from the host's `std::random_device`, as `kern.rng_pseudo` in libkernel does. Unverified on hardware +- [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - generates values with the host random device; the system library maintains its own seeded PRNG state. - [sceNetGetRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetSystemTime](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unverified on PS5 hardware: the `void(uint64_t*)` signature, process-relative microseconds and ignored null output follow [shadPS4 net.cpp](https://github.com/shadps4-emu/shadPS4/blob/d615b3887ba29025a59242e1a26f5b68bb176158/src/core/libraries/net/net.cpp#L1654-L1659). The clock is the same as `sceKernelGetProcessTime`, including `APS5_TIME_SCALE` - [sceNetGetSystemMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInitParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. @@ -37,7 +35,6 @@ - [sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(void* memory)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the block returns to the guest heap and a null pointer is ignored. Unverified on hardware - [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetUsleep](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int microseconds)` signature and the `EINVAL` failure for a negative count follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); other counts sleep through libkernel's `sceKernelUsleep`, a cancellation point, and return 0. Whether the console takes the count as signed, and what it returns, is unverified on hardware - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 1a214787cea00f9025c38364dd09b11a9fcac3c3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:37:26 +0300 Subject: [PATCH 236/523] fix(net): correct allocation size and flag handling --- core/libs/prx/libSceNet/Export.cpp | 8 ++++---- core/libs/tests/GuestSceNet.cpp | 12 ++++++++++-- docs/dev/debt/libSceNet.md | 4 +--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 36704406eb..39a828ebe0 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -2175,7 +2175,7 @@ int APS5_VABI sceNetEventCallbackWaitCB(std::int32_t timeout, void* waitData, st return 0; } -int APS5_VABI sceNetFreeAllRouteInfo(void) { +int APS5_VABI sceNetFreeAllRouteInfo(void* routes) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2290,11 +2290,11 @@ int APS5_VABI sceNetIoctl(int socketId, std::uint32_t command, void* argument) { return 0; } -void* APS5_VABI sceNetMemoryAllocate(std::int64_t size, int flags) { +void* APS5_VABI sceNetMemoryAllocate(std::int32_t size, int flags) { constexpr int zeroFill = 2; - if ((flags & ~zeroFill) != 0) - throw std::runtime_error("sceNetMemoryAllocate: flags " + std::to_string(flags) + " are not supported"); if (size <= 0) return nullptr; + if ((flags & 4) != 0) + throw std::runtime_error("sceNetMemoryAllocate: pool reserve allocation is not implemented"); void* memory = nullptr; try { memory = GuestHeap::GuestHeapAllocate_nid_postfix(static_cast(size)); diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 713a2c0186..6db4c99913 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -135,7 +135,7 @@ int APS5_VABI sceNetPoolDestroy(int); int APS5_VABI sceNetGetMemoryPoolStats(int, NetMemoryPoolStats*); const char* APS5_VABI sceNetGetIfName(std::uint32_t); int APS5_VABI sceNetGetRandom(std::uint32_t*); -void* APS5_VABI sceNetMemoryAllocate(std::int64_t, int); +void* APS5_VABI sceNetMemoryAllocate(std::int32_t, int); void APS5_VABI sceNetMemoryFree(void*); } @@ -303,6 +303,8 @@ static void CheckRandom() { static void CheckNetMemory() { Require(sceNetMemoryAllocate(0, 0) == nullptr); Require(sceNetMemoryAllocate(-1, 2) == nullptr); + Require(sceNetMemoryAllocate(0, 4) == nullptr); + Require(sceNetMemoryAllocate(INT32_MIN, -1) == nullptr); auto* zeroed = static_cast(sceNetMemoryAllocate(4096, 2)); Require(zeroed != nullptr); Require(std::all_of(zeroed, zeroed + 4096, [](std::uint8_t byte) { return byte == 0; })); @@ -314,9 +316,15 @@ static void CheckNetMemory() { sceNetMemoryFree(plain); sceNetMemoryFree(zeroed); sceNetMemoryFree(nullptr); + for (const int flags : {1, 0x40000001, -5}) { + auto* allocation = static_cast(sceNetMemoryAllocate(16, flags)); + Require(allocation != nullptr); + if ((flags & 2) != 0) Require(std::all_of(allocation, allocation + 16, [](std::uint8_t byte) { return byte == 0; })); + sceNetMemoryFree(allocation); + } bool threw = false; try { - sceNetMemoryAllocate(16, 1); + sceNetMemoryAllocate(16, 4); } catch (const std::runtime_error&) { threw = true; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 7f908bd380..391136c6b2 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -19,7 +19,6 @@ - [sceNetAllocateAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDbgInit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetFreeAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetArpInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetAppMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(info, int flags)` signature, the two IPv4 server slots in network byte order and the server count as the return value follow the PS4 [OpenOrbis musl resolver](https://github.com/OpenOrbis/musl/blob/2b0bb8ce2fa64ac255cb656785729d159a5f074c/src/network/resolvconf.c) and [OpenOrbis Net.h](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/Net.h); unverified on PS5 hardware. It reports the host DNS servers of the interface `sceNetCtlGetInfo` reports, never servers set through `sceNetSetDnsInfo`. Zeroing the unused slots and returning 0 without a host IPv4 interface are unverified. Nonzero flags and a null info throw because their console results are unknown @@ -31,8 +30,7 @@ - [sceNetGetSystemMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInitParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. -- [sceNetMemoryAllocate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int64_t size, int flags)` signature, flag 2 as zero fill and the null result for a size of 0 or less follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp). The block comes from the guest heap, and an allocation failure returns null without setting an errno. Any other flag bit throws, since its meaning is not known. Unverified on hardware -- [sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(void* memory)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/d5ba5fd06c49af29b8ec9200cad70bca676a9cd4/src/core/libraries/net/net.cpp); the block returns to the guest heap and a null pointer is ignored. Unverified on hardware +- [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced. Allocation flag 4 bypasses the system pool reserve threshold; it throws here because the pool reserve is not implemented. Flag 2 zero-fills and other bits are ignored, matching the system allocator. - [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature From 5862c362f49236a7a86e41d9e4acdf8d73d0ade2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:38:44 +0300 Subject: [PATCH 237/523] fix(user-service): restore initialization and accessibility signatures --- core/libs/prx/libSceUserService/Export.cpp | 12 +++++------- core/libs/prx/libSceUserService/UserService.hpp | 6 ++++++ core/libs/tests/GuestUserServiceLifecycle.cpp | 15 ++++++++------- docs/dev/debt/libSceUserService.md | 3 +-- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceUserService/Export.cpp b/core/libs/prx/libSceUserService/Export.cpp index 7a4c11d33e..a12d5c3263 100644 --- a/core/libs/prx/libSceUserService/Export.cpp +++ b/core/libs/prx/libSceUserService/Export.cpp @@ -26,7 +26,7 @@ int APS5_VABI sceUserServiceGetAccessibilityChatTranscription(int user_id, int32 return ReportAccessibilitySetting(user_id, chat_transcription, 0); } -int APS5_VABI sceUserServiceGetAccessibilityKeyremapEnable() { +int APS5_VABI sceUserServiceGetAccessibilityKeyremapEnable(std::int32_t userId, std::int32_t* enabled) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -43,7 +43,7 @@ int APS5_VABI sceUserServiceGetAccessibilityVibration(int user_id, int32_t* vibr return ReportAccessibilitySetting(user_id, vibration, 1); } -int APS5_VABI sceUserServiceGetAccessibilityZoom() { +int APS5_VABI sceUserServiceGetAccessibilityZoom(std::int32_t userId, std::int32_t* zoom) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -160,18 +160,16 @@ static int MarkInitialized() { return USER_SERVICE_OK; } -int APS5_VABI sceUserServiceInitialize(const void* params) { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* params) { if (params != nullptr) { - int priority = 0; - std::memcpy(&priority, params, sizeof(priority)); - if (!ValidThreadPriority(priority)) { + if (!ValidThreadPriority(params->priority)) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } } return MarkInitialized(); } -int APS5_VABI sceUserServiceInitialize2(int priority) { +int APS5_VABI sceUserServiceInitialize2(std::int32_t priority, KernelCpumask affinityMask) { if (!ValidThreadPriority(priority)) { return USER_SERVICE_ERROR_INVALID_ARGUMENT; } diff --git a/core/libs/prx/libSceUserService/UserService.hpp b/core/libs/prx/libSceUserService/UserService.hpp index 1e0f926aa3..eeb192f8aa 100644 --- a/core/libs/prx/libSceUserService/UserService.hpp +++ b/core/libs/prx/libSceUserService/UserService.hpp @@ -4,6 +4,12 @@ #include #include +struct UserServiceInitParam { + std::int32_t priority; +}; + +static_assert(sizeof(UserServiceInitParam) == 4); + constexpr int USER_SERVICE_OK = 0; constexpr int USER_SERVICE_ERROR_NOT_INITIALIZED = static_cast(0x80960002u); constexpr int USER_SERVICE_ERROR_ALREADY_INITIALIZED = static_cast(0x80960003u); diff --git a/core/libs/tests/GuestUserServiceLifecycle.cpp b/core/libs/tests/GuestUserServiceLifecycle.cpp index c169e42d26..550acb5353 100644 --- a/core/libs/tests/GuestUserServiceLifecycle.cpp +++ b/core/libs/tests/GuestUserServiceLifecycle.cpp @@ -5,8 +5,8 @@ #include extern "C" { -int APS5_VABI sceUserServiceInitialize(const void* params); -int APS5_VABI sceUserServiceInitialize2(int priority); +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* params); +int APS5_VABI sceUserServiceInitialize2(std::int32_t priority, KernelCpumask affinityMask); int APS5_VABI sceUserServiceTerminate(void); int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* user_id_list); int APS5_VABI sceUserServiceGetEvent(SceUserServiceEvent* event); @@ -34,25 +34,26 @@ void RequireLoginOnce() { } int InitializeWithPriority(int priority) { - return sceUserServiceInitialize(&priority); + const UserServiceInitParam param{priority}; + return sceUserServiceInitialize(¶m); } void TestThreadPriority() { for (int priority : {0, 0xFF, 0x300, -1}) { Require(InitializeWithPriority(priority) == USER_SERVICE_ERROR_INVALID_ARGUMENT); - Require(sceUserServiceInitialize2(priority) == USER_SERVICE_ERROR_INVALID_ARGUMENT); + Require(sceUserServiceInitialize2(priority, 1) == USER_SERVICE_ERROR_INVALID_ARGUMENT); } RequireNotInitialized(); - Require(sceUserServiceInitialize2(0x100) == USER_SERVICE_OK); + Require(sceUserServiceInitialize2(0x100, 1) == USER_SERVICE_OK); RequireLoginOnce(); for (int priority : {0x100, 700, 0x2FF}) { Require(InitializeWithPriority(priority) == USER_SERVICE_ERROR_ALREADY_INITIALIZED); - Require(sceUserServiceInitialize2(priority) == USER_SERVICE_ERROR_ALREADY_INITIALIZED); + Require(sceUserServiceInitialize2(priority, 1) == USER_SERVICE_ERROR_ALREADY_INITIALIZED); } for (int priority : {0, 0xFF, 0x300, -1}) { Require(InitializeWithPriority(priority) == USER_SERVICE_ERROR_INVALID_ARGUMENT); - Require(sceUserServiceInitialize2(priority) == USER_SERVICE_ERROR_INVALID_ARGUMENT); + Require(sceUserServiceInitialize2(priority, 1) == USER_SERVICE_ERROR_INVALID_ARGUMENT); } Require(sceUserServiceInitialize(nullptr) == USER_SERVICE_ERROR_ALREADY_INITIALIZED); Require(sceUserServiceTerminate() == USER_SERVICE_OK); diff --git a/docs/dev/debt/libSceUserService.md b/docs/dev/debt/libSceUserService.md index 4fca560df4..e320add914 100644 --- a/docs/dev/debt/libSceUserService.md +++ b/docs/dev/debt/libSceUserService.md @@ -3,9 +3,8 @@ ### Unknown function info - [sceUserServiceGetNpAccountId](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - the account id for a user with no linked PSN account is assumed to be 0 with success; libSceNpManager's `sceNpGetAccountIdA` answers the same question with `SCE_NP_ERROR_SIGNED_OUT` -- [sceUserServiceInitialize, sceUserServiceTerminate](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - not measured: whether an invalid thread priority leaves an uninitialized service uninitialized (it does here), the parameters after their first `int`, and any second argument of `sceUserServiceInitialize2`. Measured on a PS5: before initialization and after `sceUserServiceTerminate`, `GetLoginUserIdList`, `GetEvent` and `Terminate` return `0x80960002`; a second initialization returns `0x80960003` and a new one reports the initial user's login again; a priority outside 0x100 to 0x2FF, from the first `int` of the parameters or the `sceUserServiceInitialize2` argument, returns `0x80960005` before the initialized check. The other calls do not check initialization +- [sceUserServiceInitialize and sceUserServiceInitialize2](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - event delivery has no background service thread, so the confirmed priority and Initialize2 CPU affinity mask do not affect host scheduling. - [sceUserServiceGetUserColor](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - the default profile colour is assumed to be blue (0) - [sceUserServiceGetUserName](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - `0x80960105`, which a PS5 (firmware 13.60) returns for a user id it does not know, has no known name; it is `USER_SERVICE_ERROR_UNKNOWN_USER` here. On the console a registered user who is not logged in still gets their name; here only the initial user exists - [sceUserServiceGetAgeLevel](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - on a PS5 (firmware 13.60) the value is the age set in the user's parental restriction (0, 7, 12 and 15 were measured) and `0xFFFFFFFF` without one; a user who is not logged in returns `0x80960009` with or without an output. Parental restrictions are not modelled, so the initial user is always reported as unrestricted. PPSA12544 (Unity) only stores the value for its user profile - [sceUserServiceGetAccessibilityChatTranscription, PressAndHoldDelay, TriggerEffect, Vibration, ZoomEnabled, ZoomFollowFocus](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - report 0, 0, 1, 1, 0 and 1, what a PS5 (firmware 13.60) reports for a user whose accessibility settings are all at their defaults; the console reports the user's own settings (a changed vibration intensity read 3), which are not modelled. What each number means is unknown. A user who is not logged in returns `0x80960009` with or without an output -- [sceUserServiceGetAccessibilityKeyremapEnable, sceUserServiceGetAccessibilityZoom](../../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - declared without parameters as throwing stubs calling `NotImplemented_nid_no_patch`. Their signatures and values remain unverified on hardware From dd2a009f1efef001301e7a6c6ef6fda824902077 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:41:40 +0300 Subject: [PATCH 238/523] fix: restore batch priority and vr trigger query signatures --- core/libs/prx/libSceAcm/Export.cpp | 2 +- core/libs/prx/libScePad/Export.cpp | 4 ++-- docs/dev/debt/libSceAcm.md | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceAcm/Export.cpp b/core/libs/prx/libSceAcm/Export.cpp index 325afaa575..abc5dc83bc 100644 --- a/core/libs/prx/libSceAcm/Export.cpp +++ b/core/libs/prx/libSceAcm/Export.cpp @@ -91,7 +91,7 @@ int APS5_VABI sceAcm_Panner(void) { return 0; } -int APS5_VABI sceAcmBatchJobPriority(void) { +int APS5_VABI sceAcmBatchJobPriority(AcmBatchInfo* batch, std::uint32_t priority) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index c97a1e3c23..ab3e74552d 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -293,7 +293,7 @@ int APS5_VABI scePadVrControllerRead() { return 0; } -int APS5_VABI scePadVrControllerGetTriggerEffectState() { +int APS5_VABI scePadVrControllerGetTriggerEffectState(std::int32_t leftHandle, std::int32_t rightHandle, PadTriggerEffectStateInformation* states) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -334,7 +334,7 @@ int APS5_VABI scePadUnknown_n3kSX62fgNo() { return g_initialized.load(std::memory_order_acquire) ? PAD_OK : PAD_ERROR_NOT_INITIALIZED; } -int APS5_VABI scePadVrControllerGetTriggerEffectStates(void) { +int APS5_VABI scePadVrControllerGetTriggerEffectStates(std::int32_t leftHandle, std::int32_t rightHandle, PadTriggerEffectStateInformation* states) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAcm.md b/docs/dev/debt/libSceAcm.md index 59682c3221..7bdccfcf99 100644 --- a/docs/dev/debt/libSceAcm.md +++ b/docs/dev/debt/libSceAcm.md @@ -8,6 +8,5 @@ - Context validation error codes (0x81940002 for bad context and 0x81940006 for bad pointer) for [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) follow prosper's `hle_audio_spatial.cpp`. KytyPS5 (`acm.cpp`) and prosper do not track individual batch identifiers, so `sceAcmBatchWait` returns success for valid contexts without inspecting batch IDs. Hardware ACM DSP coprocessor scheduling and console error verification remain unmeasured. - [sceAcm_ConvReverb_SharedIr, sceAcm_IFFT](../../../core/libs/prx/libSceAcm/Export.cpp) (libSceAcm) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAcm.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim -- PPSA02530 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceAcmBatchJobPriority](../../../core/libs/prx/libSceAcm/Export.cpp) (libSceAcm) and [scePadVrControllerGetTriggerEffectStates](../../../core/libs/prx/libScePad/Export.cpp) (libScePad). - [`sceAcmBatchJobNotification`](../../../core/libs/prx/libSceAcm/Export.cpp) is a parameterless pending declaration for import resolution. Its signature and behavior are unverified; it throws through `NotImplemented_nid_no_patch`. From 11c5d585e6e52be0059735cec8fa7fc4555c115b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:41:51 +0300 Subject: [PATCH 239/523] fix(audio): correct mix level and 3d latency validation --- core/libs/prx/libSceAudioOut/src/AudioOut.cpp | 9 +++------ core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp | 2 +- core/libs/prx/libSceAudioOut/tests/AudioOut2Latency.cpp | 7 +++++-- .../prx/libSceAudioOut/tests/AudioOutMixLevelPadSpk.cpp | 6 +++--- docs/dev/debt/libSceAudioOut.md | 5 ++--- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp index 0eda307e26..c6a9930bb6 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp @@ -656,20 +656,17 @@ int APS5_VABI sceAudioOutSysOpen(int userId, std::uint32_t mode) { return allocatePort(PORT_TYPE_SYS, SYS_PORT_SAMPLES, SYS_PORT_FRAME_RATE, Format::S16_8Ch); } -int APS5_VABI sceAudioOutSetMixLevelPadSpk(int handle, int mixLevel) { +int APS5_VABI sceAudioOutSetMixLevelPadSpk(int handle, std::uint32_t mixLevel) { std::lock_guard lock(g_mutex); Port* port = getPort(handle); if (port == nullptr) { return -2144993277; } - if (port->type != PORT_TYPE_PADSPK) { - return -2144993270; - } if (mixLevel > DEFAULT_VOLUME) { return -2144993260; } - if (mixLevel < 0) { - throw std::runtime_error("sceAudioOutSetMixLevelPadSpk: negative mix level not supported"); + if (port->type != PORT_TYPE_PADSPK) { + return -2144993270; } port->mixLevel = mixLevel; return 0; diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp index c647f607ea..b9d9d152cf 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp @@ -16,8 +16,8 @@ int APS5_VABI sceAudioOut2Initialize(void) { } int APS5_VABI sceAudioOut2Set3DLatency(int userId, std::uint32_t output) { - (void)userId; if (output > MAX_SUPPORTED_3D_OUTPUT) return SCE_AUDIO_OUT2_ERROR_INVALID_PARAM; + if (userId != 0 && (userId != 0xFF || output == 0)) return SCE_AUDIO_OUT2_ERROR_INVALID_PARAM; return 0; } diff --git a/core/libs/prx/libSceAudioOut/tests/AudioOut2Latency.cpp b/core/libs/prx/libSceAudioOut/tests/AudioOut2Latency.cpp index 9a09ec4fba..3ab1cd4a0f 100644 --- a/core/libs/prx/libSceAudioOut/tests/AudioOut2Latency.cpp +++ b/core/libs/prx/libSceAudioOut/tests/AudioOut2Latency.cpp @@ -53,11 +53,14 @@ constexpr int errorMasteringInvalidApiParam = static_cast(0x80268201); constexpr int errorMasteringInvalidStatesId = static_cast(0x80268204); void TestSet3DLatency() { - Require(sceAudioOut2Set3DLatency(systemUser, 0) == 0, "output 0 for the system user must be accepted"); + Require(sceAudioOut2Set3DLatency(systemUser, 0) == errorInvalidParam, "output 0 for selector 255 must be refused"); + for (std::uint32_t output = 0; output <= 2; ++output) { + Require(sceAudioOut2Set3DLatency(0, output) == 0, "selector 0 must accept each supported output"); + } Require(sceAudioOut2Set3DLatency(systemUser, 1) == 0, "output 1 for the system user must be accepted"); Require(sceAudioOut2Set3DLatency(systemUser, 2) == 0, "output 2 for the system user must be accepted"); Require(sceAudioOut2Set3DLatency(systemUser, 2) == 0, "output 2 must be accepted again"); - Require(sceAudioOut2Set3DLatency(user, 1) == 0, "output 1 for another user must be accepted"); + Require(sceAudioOut2Set3DLatency(user, 1) == errorInvalidParam, "a user id is not a valid latency selector"); Require(sceAudioOut2Set3DLatency(systemUser, 3) == errorInvalidParam, "output 3 must be refused"); Require(sceAudioOut2Set3DLatency(systemUser, 0xFFFFFFFFu) == errorInvalidParam, "a large output must be refused"); } diff --git a/core/libs/prx/libSceAudioOut/tests/AudioOutMixLevelPadSpk.cpp b/core/libs/prx/libSceAudioOut/tests/AudioOutMixLevelPadSpk.cpp index 9c7ac4e9af..ca25acb711 100644 --- a/core/libs/prx/libSceAudioOut/tests/AudioOutMixLevelPadSpk.cpp +++ b/core/libs/prx/libSceAudioOut/tests/AudioOutMixLevelPadSpk.cpp @@ -16,7 +16,7 @@ int APS5_VABI sceAudioOutOpen(int, int, int, std::uint32_t, std::uint32_t, std:: int APS5_VABI sceAudioOutClose(int); int APS5_VABI sceAudioOutOutput(int, const void*); int APS5_VABI sceAudioOutSetVolume(int, std::uint32_t, int*); -int APS5_VABI sceAudioOutSetMixLevelPadSpk(int, int); +int APS5_VABI sceAudioOutSetMixLevelPadSpk(int, std::uint32_t); } static void Require(bool value) { if (!value) std::abort(); } @@ -153,7 +153,7 @@ void TestOtherPortType() { Require(Play(portTypeMain, formatS16Mono, block, [](int) {}) == block); Require(Play(portTypeMain, formatS16Mono, block, [](int handle) { Require(sceAudioOutSetMixLevelPadSpk(handle, unity / 2) == invalidPortType); - Require(sceAudioOutSetMixLevelPadSpk(handle, unity + 1) == invalidPortType); + Require(sceAudioOutSetMixLevelPadSpk(handle, unity + 1) == invalidMixLevel); }) == block); } @@ -164,7 +164,7 @@ void TestRejectedLevels() { closed = handle; Require(sceAudioOutSetMixLevelPadSpk(handle, unity / 2) == 0); Require(sceAudioOutSetMixLevelPadSpk(handle, unity + 1) == invalidMixLevel); - Require(ThrowsRuntimeError([handle] { sceAudioOutSetMixLevelPadSpk(handle, -1); })); + Require(sceAudioOutSetMixLevelPadSpk(handle, UINT32_MAX) == invalidMixLevel); Require(sceAudioOutSetMixLevelPadSpk(0, unity) == invalidPort); Require(sceAudioOutSetMixLevelPadSpk(handle + 1, unity) == invalidPort); }) == Scaled(block, unity / 2)); diff --git a/docs/dev/debt/libSceAudioOut.md b/docs/dev/debt/libSceAudioOut.md index d86f3e1808..80f284769c 100644 --- a/docs/dev/debt/libSceAudioOut.md +++ b/docs/dev/debt/libSceAudioOut.md @@ -7,12 +7,11 @@ - [sceAudioOut2UserGetSupportedAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp) (libSceAudioOut) - the user handle and two uint32 output pointers follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. The masks describe this runtime: no context attributes and port attributes PCM (bit 0) and gain (bit 1). Null outputs and unknown or destroyed user handles throw; console validation order and error values are unknown. Users are tracked for this query, but port ownership and busy-user destruction are not implemented. - [sceAudioOutExOpen, sceAudioOutExConfigureOutput, sceAudioOutExClose](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - the HDMI bitstream calls follow [PS5-Audio-Passthrough-Research](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/docs/FINDINGS.md#L31-L80), which disassembles firmware 12.70's libSceAudioOut and logs the calls on a console. `sceAudioOutExOpen(user, mode)` returns `0x80260015` for a mode above 10 or outside mask `0x61F`; otherwise it opens a port of internal type 6 in S16 stereo, 256 frames at 48000 Hz for modes 0, 1, 2 and 9 and 1024 frames at 192000 Hz for modes 3, 4 and 10. `sceAudioOutExConfigureOutput(control, flags, mode, target, option)` returns `0x80268009` when `control` is not 0, takes modes 0 to 10 and 0xFF and targets 1 to 3 and 0xFF, and returned 0 on the console; `sceAudioOutExClose` returned 0. The argument registers match [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L26-L28) and PPSA99051's [bitstream_out.cpp](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/sample-app/src/platform/ps5/bitstream_out.cpp#L29-L32). Audio reaches the host through SDL as PCM and the host has no HDMI bitstream output, so an Ex port has no host device: `sceAudioOutOutput` paces it at its own rate and drops its IEC 61937 bursts, and `sceAudioOutExConfigureOutput` changes nothing. Nonzero `flags` or `option`, other modes or targets, `sceAudioOutExClose` of a handle that is not an Ex port, and `sceAudioOutGetPortState` on an Ex port throw, since the console's answers are unknown - [sceAudioOutExGetMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) reports a sink without audio descriptors: it zeroes the buffer and returns 0, as `sceAudioOutSysGetHdmiMonitorInfo` does, since audio reaches the host through SDL as PCM and there is no EDID to describe. The arguments `(0, 0, info, size)` and the descriptor count as a 32-bit value at +0x98, with 8-byte descriptors from +0xA0, follow [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L62-L117) (PPSA99505), which passes a 0x400-byte buffer and read that layout from one TV; with no descriptor listed it decodes the audio itself instead of opening a bitstream port. The meaning of the first two arguments and the structure's size are unknown: nonzero first or second arguments and sizes below 0xA0, which cannot hold the count, throw, and every byte of the declared size is written as 0. A null buffer returns `0x80260004` like the other AudioOut calls; the console's code is not measured -- [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - assumed to take `(user_id, output)`: [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp) reads `(user_id, output, latency_us)`, but the known calls set just edi and esi. The validation follows KytyPS5: an output above 2 returns `0x80268001` and every user id is accepted, where before only `(0xFF, 2)` (PPSA26344) and `(0xFF, 1)` (PPSA14632) were; the latency value of the three-argument reading is never received, and there is no 3D rendering stage to apply anything to +- [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - the two 32-bit arguments and selector validation match the system library: selector 0 accepts outputs 0 through 2; selector 255 accepts outputs 1 and 2. No third latency argument exists. The host has no system 3D rendering stage, so the call still does not apply latency and does not reproduce the system initialization and output-availability checks. - [sceAudioOut2MasteringInit](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) - flag values unknown; only 0 is accepted. Whether the console reports an error for a repeated `sceAudioOut2MasteringInit` or for `sceAudioOut2MasteringTerm` without a matching initialization is unknown; both return 0. PPSA01289 creates the audio device of its next world before it destroys the previous one, so it calls Init twice before Term twice, and only logs a non-zero result - [sceAudioOut2MasteringGetState and sceAudioOut2MasteringSetParam](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) - the state layout, the states ids 1 (default) and 2 (V2), the descriptor ids `0x01010001`, `0x01020001` and `0x01010003` and the errors `0x80268201` and `0x80268204` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. GetState zeroes the states and writes the two descriptors; with no mastering stage the compressor and limiter values stay zero. SetParam accepts any parameter set and records nothing, as KytyPS5 does; a null parameter returns `0x80268201`, the mastering invalid-API-parameter error KytyPS5 returns for a null state, which is an inference rather than a measurement. The meaning of `output` and `user` is unknown and both are ignored - [sceAudioOut2SetSystemDebugState](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - records the debug state id in the trace and returns 0, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp) does; the host has no system mix to apply it to. The debug state ids are unknown, so every id is accepted, and a null param returns `0x80260502` like the other AudioOut2 calls. Not measured on a console - [sceAudioOut2GetSpeakerArrayCoefficients](../../../core/libs/prx/libSceAudioOut/src/AudioOut2SpeakerArray.cpp) (libSceAudioOut) - validates the handle and the coefficient count like the ambisonics call, then writes the front pair at full gain and silence for every other speaker, ignoring the position, spread, height awareness and downmix spread radius: [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp) and [prosper](https://github.com/mattias800/prosper/blob/main/prosper/src/hle/audio/hle_audio.cpp) both return that pair. The console's VBAP gains over the array's directions are not measured -- [sceAudioOutSetMixLevelPadSpk](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - effect of a negative mix level unknown; only 0 to 32768 (0 dB) is accepted - [sceAudioOutSetMixLevelPadSpk](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - no source applies the level (shadPS4 only stores it); it is assumed to be a linear gain on the port's own output, and the -9 dB default (11626) is applied to every pad speaker port, including those of titles that never call the function - [sceAudioOutGetLastOutputTime](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - signature and behaviour taken from the [shadPS4](https://github.com/shadps4-emu/shadPS4) and [fpPS4](https://github.com/red-prig/fpPS4) reimplementations of the PS4 library, not confirmed on a PS5 title: the time is the process time at which `sceAudioOutOutput` or `sceAudioOutOutputs` accepted the port's last block, not the time the block reached the device - [sceAudioOutSysOpen, sceAudioOutSysConfigureOutput, sceAudioOutSysClose](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - imported by PPSA99051 and PPSA99997, which fail to load without them; the signatures follow [EVO-PLAYER-PS5](https://github.com/sainsaji/EVO-PLAYER-PS5/blob/da0c0451333bab82e79fffd2de4718e917f9dcb0/projects/evoplayer/core/src/audio/evo_pt.cpp#L24-L29), whose audio code both titles share, and are not confirmed on a console. `sceAudioOutSysOpen(user, mode)` opens a port only for mode 5, the TrueHD MAT route: [the same project's carrier table](https://github.com/sainsaji/EVO-PLAYER-PS5/blob/da0c0451333bab82e79fffd2de4718e917f9dcb0/projects/evoplayer/core/src/audio/iec61937.cpp#L125-L140), read from sceAudioOutExOpen's own tables in firmware 12.70, describes it as a 768 kHz port of 1024 S16 8-channel frames whose 16-byte frames are consumed at 192 kHz; every other mode throws. Audio reaches the host through SDL as PCM and the host has no HDMI bitstream output, so like an Ex port a Sys port has no host device: `sceAudioOutOutput` paces it at its own rate and drops the MAT bursts. `sceAudioOutSysConfigureOutput(type, flags, mode, target, option)` accepts only the observed `(1, 0, 5, 1, 0)` HDMI MAT switch and changes nothing; other arguments throw. `sceAudioOutSysClose` closes a Sys port and throws for any other handle. The restore path EVO-PLAYER-PS5 takes after a Sys port calls `sceAudioOutExConfigureOutput(0, 0, 0xFF, 0xFF, 0)`, so no Sys reset combination is known @@ -112,7 +111,7 @@ ### Functional - [sceAudioOut2EnableChat](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) takes no arguments and returns 0, as declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs#L460-L464) ("Allows chat audio alongside the application's own"). The host mixes no chat audio and AudioOut2 has no chat port, so the call only records that chat audio is allowed. Whether the console fails it without a chat session, when repeated or before `sceAudioOut2Initialize` is unknown. [sceAudioOut2DisableChat](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) is the counterpart, imported by PPSA18216: it takes no arguments, returns 0 and records that chat audio is no longer allowed; its signature is not confirmed by a console measurement, and whether the console fails it without a chat session or when repeated is unknown. -- [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) and [sceAudioOut2MasteringInit, sceAudioOut2MasteringTerm and sceAudioOut2MasteringSetParam](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) have no effect: there is no 3D rendering or mastering stage. SetParam accepts the parameter set, and SetSystemDebugState records the debug state id, without anything to apply them to. +- [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - the two 32-bit arguments and selector validation match the system library: selector 0 accepts outputs 0 through 2; selector 255 accepts outputs 1 and 2. No third latency argument exists. The host has no system 3D rendering stage, so the call still does not apply latency and does not reproduce the system initialization and output-availability checks. - [AudioOut2 contexts](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) with an SDL device and no `sceAudioOut2ContextAdvance` calls read a grain's port buffers at the next push, at least 100 us after the grain was pushed, and play the mix behind a 40 ms cushion that the queue level leaves out. When the hardware reads the buffers is not known. The timing follows a title whose mixer fills a buffer after pushing it, reuses it two pushes later, and may still be writing it when the next push follows within microseconds. The last grain pushed before a title stops pushing is never read or played. - [sceAudioOut2ContextGetQueueLevel](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) with an SDL device counts the grains that `sceAudioOut2ContextAdvance` completed and no push has consumed as queued, together with the pushed grains beyond the 40 ms cushion, up to `queue_depth`. Whether the console counts them is unknown. Pushes that fill the cushion leave the level at 0, so a title that renders as many grains as the level leaves room for and pushes them on another thread rendered a full queue again after each of those pushes: PPSA23226's Wwise sink passes the available count to the sound engine as the number of frames to render, advances once per frame and pushes on a separate thread, and exceeded 16 completed grains. Counted, the completed grains keep such a title at most `queue_depth` grains ahead of its pushes. - [sceAudioOut2LoInit, sceAudioOut2LoContext* and sceAudioOut2LoPort* calls](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Low.cpp) (libSceAudioOut) have no low-latency path: each forwards to its `sceAudioOut2` counterpart, so grain and queue depth are the context's `num_grains` and `queue_depth`, and playback keeps the 40 ms cushion. [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs#L489-L545) only says each call takes the same handles and parameter blocks as its counterpart and drives the low-latency clock with a shallow queue and a small grain ([docs](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/docs/audio.md#L662-L668)). Unverified: the console's grain and depth limits, its error for a handle of the other service (accepted here) and what `sceAudioOut2LoInit` returns when repeated (0 here). From d004e7a6f9f6e295832e31ea0fe45c38245e8782 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:46:05 +0300 Subject: [PATCH 240/523] fix(np): restore signaling initialization and peer lookup types --- .../prx/libSceNpSessionSignaling/Export.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index 290ef13d06..164bd13a78 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -10,6 +10,14 @@ static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND = static_cast static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE = static_cast(0x80552D06); static std::atomic g_nextContext{1}; +struct SessionSignalingInitParam { + std::int32_t userId; + std::uint64_t poolSize; + std::uint64_t affinityMask; + std::int32_t threadPriority; + std::uint64_t stackSize; +}; + struct SessionSignalingMemoryInfo { std::uint64_t poolSize; std::uint64_t usedSize; @@ -34,6 +42,11 @@ struct SessionSignalingGroupInfo { }; static_assert(sizeof(SessionSignalingMemoryInfo) == 24); +static_assert(sizeof(SessionSignalingInitParam) == 40); +static_assert(offsetof(SessionSignalingInitParam, poolSize) == 8); +static_assert(offsetof(SessionSignalingInitParam, affinityMask) == 16); +static_assert(offsetof(SessionSignalingInitParam, threadPriority) == 24); +static_assert(offsetof(SessionSignalingInitParam, stackSize) == 32); static_assert(offsetof(SessionSignalingMemoryInfo, usedSize) == 8); static_assert(offsetof(SessionSignalingMemoryInfo, peakUsedSize) == 16); static_assert(sizeof(SessionSignalingPeerAddress) == 16); @@ -47,7 +60,7 @@ static_assert(offsetof(SessionSignalingGroupInfo, connectionCount) == 256); extern "C" { -int APS5_VABI sceNpSessionSignalingInitialize(void* param) { +int APS5_VABI sceNpSessionSignalingInitialize(const SessionSignalingInitParam* param) { (void)param; return 0; } @@ -81,11 +94,11 @@ int APS5_VABI sceNpSessionSignalingGetConnectionInfo(void) { return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } -int APS5_VABI sceNpSessionSignalingActivateUser(void) { +int APS5_VABI sceNpSessionSignalingActivateUser(std::uint32_t contextId, const SessionSignalingPeerAddress* peerAddress, std::uint32_t* connectionId) { return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } -int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress2(void) { +int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress2(std::uint32_t contextId, std::uint32_t groupId, const SessionSignalingPeerAddress* peerAddress, std::uint32_t* connectionId) { return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } From c203dbb302d8324c04e6f3f546cab50ef00502a0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:48:53 +0300 Subject: [PATCH 241/523] fix(np): restore signaling context callbacks and session arguments --- .../prx/libSceNpSessionSignaling/Export.cpp | 48 ++++++++++++++++--- core/libs/tests/GuestNpSessionSignaling.cpp | 11 ++++- docs/dev/debt/libSceNpSessionSignaling.md | 2 + 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index 164bd13a78..cf7657d9e6 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -29,6 +29,30 @@ struct SessionSignalingPeerAddress { std::uint32_t platform; }; +struct SessionSignalingSessionParam { + std::uint32_t mode; + std::uint32_t option; +}; + +using SessionSignalingEventCallback = void (APS5_VABI *)(std::uint32_t, std::uint32_t, std::int32_t, const SessionSignalingPeerAddress*, std::int32_t, void*); +using SessionSignalingConnectionCallback = void (APS5_VABI *)(std::uint32_t, std::uint32_t, std::int32_t, std::int32_t, void*); +using SessionSignalingConnectionCallback2 = void (APS5_VABI *)(std::uint32_t, std::uint32_t, std::uint32_t, std::int32_t, std::int32_t, void*); + +template +struct SessionSignalingContextParam { + std::int32_t userId; + std::uint32_t serviceLabel; + SessionSignalingEventCallback prepareCallback; + void* prepareUserData; + SessionSignalingEventCallback sessionCallback; + void* sessionUserData; + TConnectionCallback connectionCallback; + void* connectionUserData; +}; + +using SessionSignalingContextParam1 = SessionSignalingContextParam; +using SessionSignalingContextParam2 = SessionSignalingContextParam; + struct SessionSignalingConnectionStatistics { std::uint32_t peakConnectionCount; std::uint32_t connectionCount; @@ -50,6 +74,15 @@ static_assert(offsetof(SessionSignalingInitParam, stackSize) == 32); static_assert(offsetof(SessionSignalingMemoryInfo, usedSize) == 8); static_assert(offsetof(SessionSignalingMemoryInfo, peakUsedSize) == 16); static_assert(sizeof(SessionSignalingPeerAddress) == 16); +static_assert(sizeof(SessionSignalingSessionParam) == 8); +static_assert(offsetof(SessionSignalingSessionParam, option) == 4); +static_assert(sizeof(SessionSignalingContextParam1) == 56); +static_assert(sizeof(SessionSignalingContextParam2) == 56); +static_assert(offsetof(SessionSignalingContextParam1, prepareCallback) == 8); +static_assert(offsetof(SessionSignalingContextParam1, sessionCallback) == 24); +static_assert(offsetof(SessionSignalingContextParam1, connectionCallback) == 40); +static_assert(offsetof(SessionSignalingContextParam2, connectionCallback) == 40); +static_assert(offsetof(SessionSignalingContextParam2, connectionUserData) == 48); static_assert(offsetof(SessionSignalingPeerAddress, platform) == 8); static_assert(sizeof(SessionSignalingConnectionStatistics) == 16); static_assert(offsetof(SessionSignalingConnectionStatistics, connectionCount) == 4); @@ -65,19 +98,20 @@ int APS5_VABI sceNpSessionSignalingInitialize(const SessionSignalingInitParam* p return 0; } -int APS5_VABI sceNpSessionSignalingActivateSession(void) { +int APS5_VABI sceNpSessionSignalingActivateSession(std::uint32_t contextId, const char* sessionId, std::uint32_t sessionType, const SessionSignalingSessionParam* param, std::uint32_t* groupId) { return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } -int APS5_VABI sceNpSessionSignalingCreateContext2(const void* param, uint32_t* context_id) { - (void)param; - if (!context_id) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; - *context_id = g_nextContext.fetch_add(1, std::memory_order_relaxed); +int APS5_VABI sceNpSessionSignalingCreateContext2(const SessionSignalingContextParam2* param, std::uint32_t* contextId) { + if (!param || !contextId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + *contextId = g_nextContext.fetch_add(1, std::memory_order_relaxed); return 0; } -int APS5_VABI sceNpSessionSignalingCreateContext(const void* param, uint32_t* context_id) { - return sceNpSessionSignalingCreateContext2(param, context_id); +int APS5_VABI sceNpSessionSignalingCreateContext(const SessionSignalingContextParam1* param, std::uint32_t* contextId) { + if (!param || !contextId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + *contextId = g_nextContext.fetch_add(1, std::memory_order_relaxed); + return 0; } int APS5_VABI sceNpSessionSignalingDeactivate(uint32_t context_id) { diff --git a/core/libs/tests/GuestNpSessionSignaling.cpp b/core/libs/tests/GuestNpSessionSignaling.cpp index 27bc077144..39b90f44f1 100644 --- a/core/libs/tests/GuestNpSessionSignaling.cpp +++ b/core/libs/tests/GuestNpSessionSignaling.cpp @@ -6,6 +6,7 @@ extern "C" { int APS5_VABI sceNpSessionSignalingInitialize(void* param); +int APS5_VABI sceNpSessionSignalingCreateContext(const void* param, std::uint32_t* contextId); int APS5_VABI sceNpSessionSignalingCreateContext2(const void* param, std::uint32_t* contextId); std::int32_t APS5_VABI sceNpSessionSignalingGetLocalNetInfo(std::int32_t contextId, void* info); int APS5_VABI sceNpSessionSignalingRequestPrepare(std::uint32_t contextId, std::uint32_t* requestId); @@ -29,10 +30,16 @@ void Require(bool condition, const char* message) { } int main() { - unsigned char initializeParam[32] = {}; + alignas(8) unsigned char initializeParam[40] = {}; Require(sceNpSessionSignalingInitialize(initializeParam) == 0, "initialize failed"); - unsigned char contextParam[64] = {}; + alignas(8) unsigned char contextParam[56] = {}; std::uint32_t contextId = 0; + Require(sceNpSessionSignalingCreateContext(nullptr, &contextId) == InvalidArgument, "null context parameter"); + Require(contextId == 0, "invalid context parameter wrote an id"); + Require(sceNpSessionSignalingCreateContext2(nullptr, &contextId) == InvalidArgument, "null context2 parameter"); + Require(contextId == 0, "invalid context2 parameter wrote an id"); + Require(sceNpSessionSignalingCreateContext(contextParam, nullptr) == InvalidArgument, "null context id"); + Require(sceNpSessionSignalingCreateContext2(contextParam, nullptr) == InvalidArgument, "null context2 id"); Require(sceNpSessionSignalingCreateContext2(contextParam, &contextId) == 0, "context creation failed"); std::uint32_t requestId = 0xa5a5a5a5u; diff --git a/docs/dev/debt/libSceNpSessionSignaling.md b/docs/dev/debt/libSceNpSessionSignaling.md index 80cb116b9a..7b26d4e439 100644 --- a/docs/dev/debt/libSceNpSessionSignaling.md +++ b/docs/dev/debt/libSceNpSessionSignaling.md @@ -2,5 +2,7 @@ ### Unknown function info +- [sceNpSessionSignalingInitialize, sceNpSessionSignalingCreateContext and sceNpSessionSignalingCreateContext2](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) - the initialization layout is 40 bytes; both context layouts are 56 bytes with callback and user-data pairs. The second context variant adds a group identifier to its connection callback. Pool allocation, initialization state, per-user context limits, context lookup, callback delivery and underlying NP account validation are not implemented. + - [sceNpSessionSignalingGetConnectionStatus](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - `(int32 context id, int32 connection id, int32* status, SceNetInAddr* peer address, uint16* peer port)` is ASTRO BOT's (PPSA21564 01.007.000) call at 0x30622B: the context id from a 32-bit field, the connection id, three stack outputs, a negative result taken as an error, then the address passed to `sceNetInetNtop(AF_INET, ...)` and the port to `sceNetNtohs`. It matches PS4's `sceNpSignalingGetConnectionStatus`. The title reaches it only for a connection id from the signaling library, which offline never hands one out. No session ever activates offline, so every connection id answers `CONN_NOT_FOUND` (0x8055330C, from [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/64d198190fa05d37cbb1fa29c50f967c376fce55/src/core/libraries/np/np_error.h)) without writing the outputs, and a null status answers `INVALID_ARGUMENT`. The context id is not checked, as contexts are not tracked. The console's answer for an unknown context or connection is unverified - [sceNpSessionSignalingCreateJoinActivate, sceNpSessionSignalingGetConnectionFromNetAddress, sceNpSessionSignalingGetCustomProperty, sceNpSessionSignalingGetGroupFromPeerAddress, sceNpSessionSignalingInitializeWithPort, sceNpSessionSignalingJoinActivate and sceNpSessionSignalingSetCustomProperty](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSessionSignaling.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From 5a0a494e120a0d71717cc94ddeed9baaf558e1f7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:51:06 +0300 Subject: [PATCH 242/523] fix(np): restore signaling connection query layouts --- .../prx/libSceNpSessionSignaling/Export.cpp | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index cf7657d9e6..99f4bf2412 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -34,6 +34,24 @@ struct SessionSignalingSessionParam { std::uint32_t option; }; +struct SessionSignalingNetAddress { + std::uint32_t address; + std::uint16_t port; +}; + +union SessionSignalingConnectionInfo { + std::uint32_t value; + SessionSignalingNetAddress netAddress; + SessionSignalingPeerAddress peerAddress; +}; + +struct SessionSignalingLocalNetInfo { + std::uint32_t localAddress; + std::uint32_t mappedAddress; + std::uint32_t mappedPort; + std::uint32_t mappingStatus; +}; + using SessionSignalingEventCallback = void (APS5_VABI *)(std::uint32_t, std::uint32_t, std::int32_t, const SessionSignalingPeerAddress*, std::int32_t, void*); using SessionSignalingConnectionCallback = void (APS5_VABI *)(std::uint32_t, std::uint32_t, std::int32_t, std::int32_t, void*); using SessionSignalingConnectionCallback2 = void (APS5_VABI *)(std::uint32_t, std::uint32_t, std::uint32_t, std::int32_t, std::int32_t, void*); @@ -76,6 +94,13 @@ static_assert(offsetof(SessionSignalingMemoryInfo, peakUsedSize) == 16); static_assert(sizeof(SessionSignalingPeerAddress) == 16); static_assert(sizeof(SessionSignalingSessionParam) == 8); static_assert(offsetof(SessionSignalingSessionParam, option) == 4); +static_assert(sizeof(SessionSignalingNetAddress) == 8); +static_assert(offsetof(SessionSignalingNetAddress, port) == 4); +static_assert(sizeof(SessionSignalingConnectionInfo) == 16); +static_assert(sizeof(SessionSignalingLocalNetInfo) == 16); +static_assert(offsetof(SessionSignalingLocalNetInfo, mappedAddress) == 4); +static_assert(offsetof(SessionSignalingLocalNetInfo, mappedPort) == 8); +static_assert(offsetof(SessionSignalingLocalNetInfo, mappingStatus) == 12); static_assert(sizeof(SessionSignalingContextParam1) == 56); static_assert(sizeof(SessionSignalingContextParam2) == 56); static_assert(offsetof(SessionSignalingContextParam1, prepareCallback) == 8); @@ -124,7 +149,7 @@ int APS5_VABI sceNpSessionSignalingDestroyContext(uint32_t context_id) { return 0; } -int APS5_VABI sceNpSessionSignalingGetConnectionInfo(void) { +int APS5_VABI sceNpSessionSignalingGetConnectionInfo(std::uint32_t contextId, std::uint32_t connectionId, std::uint32_t code, SessionSignalingConnectionInfo* info) { return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } @@ -140,17 +165,17 @@ int APS5_VABI sceNpSessionSignalingTerminate(void) { return 0; } -int32_t APS5_VABI sceNpSessionSignalingGetConnectionStatus(int32_t context_id, int32_t connection_id, int32_t* status, void* peer_address, uint16_t* peer_port) { - (void)context_id; - (void)connection_id; - (void)peer_address; - (void)peer_port; +int32_t APS5_VABI sceNpSessionSignalingGetConnectionStatus(std::uint32_t contextId, std::uint32_t connectionId, std::int32_t* status, std::uint32_t* peerAddress, std::uint16_t* peerPort) { + (void)contextId; + (void)connectionId; + (void)peerAddress; + (void)peerPort; if (!status) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; return SCE_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND; } -int32_t APS5_VABI sceNpSessionSignalingGetLocalNetInfo(int32_t context_id, void* info) { - (void)context_id; +int32_t APS5_VABI sceNpSessionSignalingGetLocalNetInfo(std::uint32_t contextId, SessionSignalingLocalNetInfo* info) { + (void)contextId; if (!info) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } From 9ff2cd1d18955ceb3e8b98ebdb2f355cc3b3b8b9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:54:02 +0300 Subject: [PATCH 243/523] fix(np): validate signaling context lifecycle and stale handles --- .../prx/libSceNpSessionSignaling/Export.cpp | 112 +++++++++++++++--- core/libs/tests/GuestNpSessionSignaling.cpp | 42 +++++++ docs/dev/debt/libSceNpSessionSignaling.md | 4 +- 3 files changed, 138 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceNpSessionSignaling/Export.cpp b/core/libs/prx/libSceNpSessionSignaling/Export.cpp index 99f4bf2412..79969f86ed 100644 --- a/core/libs/prx/libSceNpSessionSignaling/Export.cpp +++ b/core/libs/prx/libSceNpSessionSignaling/Export.cpp @@ -2,13 +2,28 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" -#include +#include +#include // Peer-to-peer signaling needs the network: contexts exist, but sessions never activate. static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT = static_cast(0x80553303); static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND = static_cast(0x8055330C); static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE = static_cast(0x80552D06); -static std::atomic g_nextContext{1}; +static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED = static_cast(0x80553301); +static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_ALREADY_INITIALIZED = static_cast(0x80553302); +static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_LIMIT = static_cast(0x80553307); +static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND = static_cast(0x80553308); +static constexpr int SCE_NP_SESSION_SIGNALING_ERROR_GROUP_NOT_FOUND = static_cast(0x8055330A); + +struct SessionSignalingContext { + std::uint32_t id; + std::int32_t userId; + bool active; +}; + +static std::mutex g_mutex; +static bool g_initialized = false; +static std::array g_contexts{}; struct SessionSignalingInitParam { std::int32_t userId; @@ -116,73 +131,134 @@ static_assert(offsetof(SessionSignalingConnectionStatistics, connectedCount) == static_assert(sizeof(SessionSignalingGroupInfo) == 264); static_assert(offsetof(SessionSignalingGroupInfo, connectionCount) == 256); +static SessionSignalingContext* FindContext(std::uint32_t contextId) { + auto& context = g_contexts[contextId % g_contexts.size()]; + return context.active && context.id == contextId ? &context : nullptr; +} + +template +static int CreateContext(const SessionSignalingContextParam* param, std::uint32_t* contextId) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + if (!param || !contextId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + for (const auto& context : g_contexts) { + if (context.active && context.userId == param->userId) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_LIMIT; + } + for (auto& context : g_contexts) { + if (!context.active) { + context.userId = param->userId; + context.active = true; + *contextId = context.id; + return 0; + } + } + return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_LIMIT; +} + extern "C" { int APS5_VABI sceNpSessionSignalingInitialize(const SessionSignalingInitParam* param) { - (void)param; + std::lock_guard lock(g_mutex); + if (g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_ALREADY_INITIALIZED; + if (!param || !param->poolSize) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + for (std::uint32_t index = 0; index < g_contexts.size(); ++index) { + g_contexts[index] = {1000 + index, 0, false}; + } + g_initialized = true; return 0; } int APS5_VABI sceNpSessionSignalingActivateSession(std::uint32_t contextId, const char* sessionId, std::uint32_t sessionType, const SessionSignalingSessionParam* param, std::uint32_t* groupId) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + if (!sessionId || !param || !groupId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; + if ((param->mode == 1 && param->option != 0) || (param->mode == 2 && param->option == 0)) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } int APS5_VABI sceNpSessionSignalingCreateContext2(const SessionSignalingContextParam2* param, std::uint32_t* contextId) { - if (!param || !contextId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; - *contextId = g_nextContext.fetch_add(1, std::memory_order_relaxed); - return 0; + return CreateContext(param, contextId); } int APS5_VABI sceNpSessionSignalingCreateContext(const SessionSignalingContextParam1* param, std::uint32_t* contextId) { - if (!param || !contextId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; - *contextId = g_nextContext.fetch_add(1, std::memory_order_relaxed); - return 0; + return CreateContext(param, contextId); } -int APS5_VABI sceNpSessionSignalingDeactivate(uint32_t context_id) { - (void)context_id; - return 0; +int APS5_VABI sceNpSessionSignalingDeactivate(std::uint32_t contextId, std::uint32_t connectionId) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; + return SCE_NP_SESSION_SIGNALING_ERROR_GROUP_NOT_FOUND; } -int APS5_VABI sceNpSessionSignalingDestroyContext(uint32_t context_id) { - (void)context_id; +int APS5_VABI sceNpSessionSignalingDestroyContext(std::uint32_t contextId) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + auto* context = FindContext(contextId); + if (!context) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; + context->active = false; + const auto nextId = context->id + static_cast(g_contexts.size()); + context->id = nextId < 100001 ? nextId : 1000 + contextId % static_cast(g_contexts.size()); return 0; } int APS5_VABI sceNpSessionSignalingGetConnectionInfo(std::uint32_t contextId, std::uint32_t connectionId, std::uint32_t code, SessionSignalingConnectionInfo* info) { - return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + if (!info) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; + return SCE_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND; } int APS5_VABI sceNpSessionSignalingActivateUser(std::uint32_t contextId, const SessionSignalingPeerAddress* peerAddress, std::uint32_t* connectionId) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + if (!peerAddress || !connectionId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } int APS5_VABI sceNpSessionSignalingGetConnectionFromPeerAddress2(std::uint32_t contextId, std::uint32_t groupId, const SessionSignalingPeerAddress* peerAddress, std::uint32_t* connectionId) { - return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + if (!peerAddress || !connectionId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; + return SCE_NP_SESSION_SIGNALING_ERROR_GROUP_NOT_FOUND; } int APS5_VABI sceNpSessionSignalingTerminate(void) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; + g_initialized = false; + for (auto& context : g_contexts) context.active = false; return 0; } int32_t APS5_VABI sceNpSessionSignalingGetConnectionStatus(std::uint32_t contextId, std::uint32_t connectionId, std::int32_t* status, std::uint32_t* peerAddress, std::uint16_t* peerPort) { - (void)contextId; (void)connectionId; (void)peerAddress; (void)peerPort; + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; if (!status) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; return SCE_NP_SESSION_SIGNALING_ERROR_CONN_NOT_FOUND; } int32_t APS5_VABI sceNpSessionSignalingGetLocalNetInfo(std::uint32_t contextId, SessionSignalingLocalNetInfo* info) { (void)contextId; + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; if (!info) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } int APS5_VABI sceNpSessionSignalingRequestPrepare(uint32_t contextId, uint32_t* requestId) { - (void)contextId; + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED; if (!requestId) return SCE_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT; + if (!FindContext(contextId)) return SCE_NP_SESSION_SIGNALING_ERROR_CONTEXT_NOT_FOUND; return SCE_NP_SESSION_SIGNALING_ERROR_UNAVAILABLE; } diff --git a/core/libs/tests/GuestNpSessionSignaling.cpp b/core/libs/tests/GuestNpSessionSignaling.cpp index 39b90f44f1..982664e90c 100644 --- a/core/libs/tests/GuestNpSessionSignaling.cpp +++ b/core/libs/tests/GuestNpSessionSignaling.cpp @@ -3,11 +3,14 @@ #include #include #include +#include extern "C" { int APS5_VABI sceNpSessionSignalingInitialize(void* param); int APS5_VABI sceNpSessionSignalingCreateContext(const void* param, std::uint32_t* contextId); int APS5_VABI sceNpSessionSignalingCreateContext2(const void* param, std::uint32_t* contextId); +int APS5_VABI sceNpSessionSignalingDestroyContext(std::uint32_t contextId); +int APS5_VABI sceNpSessionSignalingDeactivate(std::uint32_t contextId, std::uint32_t connectionId); std::int32_t APS5_VABI sceNpSessionSignalingGetLocalNetInfo(std::int32_t contextId, void* info); int APS5_VABI sceNpSessionSignalingRequestPrepare(std::uint32_t contextId, std::uint32_t* requestId); std::int32_t APS5_VABI sceNpSessionSignalingGetConnectionStatus(std::int32_t contextId, std::int32_t connectionId, std::int32_t* status, void* peerAddress, std::uint16_t* peerPort); @@ -19,6 +22,11 @@ namespace { constexpr int InvalidArgument = static_cast(0x80553303u); constexpr int Unavailable = static_cast(0x80552D06u); constexpr int ConnectionNotFound = static_cast(0x8055330Cu); +constexpr int NotInitialized = static_cast(0x80553301u); +constexpr int AlreadyInitialized = static_cast(0x80553302u); +constexpr int ContextLimit = static_cast(0x80553307u); +constexpr int ContextNotFound = static_cast(0x80553308u); +constexpr int GroupNotFound = static_cast(0x8055330Au); void Require(bool condition, const char* message) { if (!condition) { @@ -31,7 +39,15 @@ void Require(bool condition, const char* message) { int main() { alignas(8) unsigned char initializeParam[40] = {}; + Require(sceNpSessionSignalingTerminate() == NotInitialized, "terminate before initialize"); + Require(sceNpSessionSignalingCreateContext2(nullptr, nullptr) == NotInitialized, "initialization check precedes context arguments"); + Require(sceNpSessionSignalingGetConnectionStatus(0, 0, nullptr, nullptr, nullptr) == NotInitialized, "status before initialize"); + Require(sceNpSessionSignalingInitialize(nullptr) == InvalidArgument, "null initialize parameter"); + Require(sceNpSessionSignalingInitialize(initializeParam) == InvalidArgument, "zero pool size"); + const std::uint64_t poolSize = 4 * 1024 * 1024; + std::memcpy(initializeParam + 8, &poolSize, sizeof(poolSize)); Require(sceNpSessionSignalingInitialize(initializeParam) == 0, "initialize failed"); + Require(sceNpSessionSignalingInitialize(nullptr) == AlreadyInitialized, "initialization check precedes initialize arguments"); alignas(8) unsigned char contextParam[56] = {}; std::uint32_t contextId = 0; Require(sceNpSessionSignalingCreateContext(nullptr, &contextId) == InvalidArgument, "null context parameter"); @@ -41,8 +57,26 @@ int main() { Require(sceNpSessionSignalingCreateContext(contextParam, nullptr) == InvalidArgument, "null context id"); Require(sceNpSessionSignalingCreateContext2(contextParam, nullptr) == InvalidArgument, "null context2 id"); Require(sceNpSessionSignalingCreateContext2(contextParam, &contextId) == 0, "context creation failed"); + const auto firstId = contextId; + Require(sceNpSessionSignalingCreateContext2(contextParam, &contextId) == ContextLimit, "duplicate user context"); + Require(contextId == firstId, "duplicate context modified output"); + std::array otherContexts{}; + for (std::int32_t userId = 1; userId <= 9; ++userId) { + std::memcpy(contextParam, &userId, sizeof(userId)); + Require(sceNpSessionSignalingCreateContext2(contextParam, &otherContexts[userId - 1]) == 0, "create remaining contexts"); + } + const std::int32_t overflowUser = 10; + std::memcpy(contextParam, &overflowUser, sizeof(overflowUser)); + Require(sceNpSessionSignalingCreateContext2(contextParam, &contextId) == ContextLimit, "context limit"); + Require(contextId == firstId, "context limit modified output"); + Require(sceNpSessionSignalingDestroyContext(firstId) == 0, "destroy first context"); + Require(sceNpSessionSignalingDestroyContext(firstId) == ContextNotFound, "destroy stale context"); + Require(sceNpSessionSignalingCreateContext2(contextParam, &contextId) == 0, "reuse context slot"); + Require(contextId != firstId, "reused context kept stale id"); std::uint32_t requestId = 0xa5a5a5a5u; + Require(sceNpSessionSignalingRequestPrepare(firstId, &requestId) == ContextNotFound, "prepare stale context"); + Require(sceNpSessionSignalingRequestPrepare(firstId, nullptr) == InvalidArgument, "prepare argument precedence"); Require(sceNpSessionSignalingRequestPrepare(contextId, nullptr) == InvalidArgument, "null request id"); Require(sceNpSessionSignalingRequestPrepare(contextId, &requestId) == Unavailable, "prepare without network"); Require(requestId == 0xa5a5a5a5u, "failed prepare wrote a request id"); @@ -59,10 +93,18 @@ int main() { std::int32_t status = 0x5a5a5a5a; std::uint32_t peerAddress = 0xa5a5a5a5u; std::uint16_t peerPort = 0xa5a5; + Require(sceNpSessionSignalingGetConnectionStatus(firstId, 1, &status, &peerAddress, &peerPort) == ContextNotFound, "status stale context"); Require(sceNpSessionSignalingGetConnectionStatus(netContextId, 1, nullptr, &peerAddress, &peerPort) == InvalidArgument, "null connection status"); Require(sceNpSessionSignalingGetConnectionStatus(netContextId, 1, &status, &peerAddress, &peerPort) == ConnectionNotFound, "connection without network"); Require(sceNpSessionSignalingGetConnectionStatus(netContextId, 1, &status, nullptr, nullptr) == ConnectionNotFound, "connection without peer outputs"); Require(status == 0x5a5a5a5a && peerAddress == 0xa5a5a5a5u && peerPort == 0xa5a5, "failed status query wrote an output"); + Require(sceNpSessionSignalingDeactivate(firstId, 1) == ContextNotFound, "deactivate stale context"); + Require(sceNpSessionSignalingDeactivate(contextId, 1) == GroupNotFound, "deactivate nonexistent group"); Require(sceNpSessionSignalingTerminate() == 0, "terminate failed"); + Require(sceNpSessionSignalingDestroyContext(contextId) == NotInitialized, "destroy after terminate"); + Require(sceNpSessionSignalingTerminate() == NotInitialized, "repeated terminate"); + Require(sceNpSessionSignalingInitialize(initializeParam) == 0, "reinitialize"); + Require(sceNpSessionSignalingRequestPrepare(contextId, &requestId) == ContextNotFound, "reinitialize retained a context"); + Require(sceNpSessionSignalingTerminate() == 0, "final terminate"); } diff --git a/docs/dev/debt/libSceNpSessionSignaling.md b/docs/dev/debt/libSceNpSessionSignaling.md index 7b26d4e439..f6a89b9950 100644 --- a/docs/dev/debt/libSceNpSessionSignaling.md +++ b/docs/dev/debt/libSceNpSessionSignaling.md @@ -2,7 +2,7 @@ ### Unknown function info -- [sceNpSessionSignalingInitialize, sceNpSessionSignalingCreateContext and sceNpSessionSignalingCreateContext2](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) - the initialization layout is 40 bytes; both context layouts are 56 bytes with callback and user-data pairs. The second context variant adds a group identifier to its connection callback. Pool allocation, initialization state, per-user context limits, context lookup, callback delivery and underlying NP account validation are not implemented. +- [sceNpSessionSignalingInitialize, sceNpSessionSignalingCreateContext and sceNpSessionSignalingCreateContext2](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) - the initialization layout is 40 bytes; both context layouts are 56 bytes with callback and user-data pairs. The second context variant adds a group identifier to its connection callback. Pool allocation, callback delivery and underlying NP account validation are not implemented; priority and affinity settings do not affect host scheduling. -- [sceNpSessionSignalingGetConnectionStatus](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - `(int32 context id, int32 connection id, int32* status, SceNetInAddr* peer address, uint16* peer port)` is ASTRO BOT's (PPSA21564 01.007.000) call at 0x30622B: the context id from a 32-bit field, the connection id, three stack outputs, a negative result taken as an error, then the address passed to `sceNetInetNtop(AF_INET, ...)` and the port to `sceNetNtohs`. It matches PS4's `sceNpSignalingGetConnectionStatus`. The title reaches it only for a connection id from the signaling library, which offline never hands one out. No session ever activates offline, so every connection id answers `CONN_NOT_FOUND` (0x8055330C, from [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/64d198190fa05d37cbb1fa29c50f967c376fce55/src/core/libraries/np/np_error.h)) without writing the outputs, and a null status answers `INVALID_ARGUMENT`. The context id is not checked, as contexts are not tracked. The console's answer for an unknown context or connection is unverified +- [sceNpSessionSignalingGetConnectionStatus, sceNpSessionSignalingActivateSession and sceNpSessionSignalingActivateUser](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) - initialization, context lookup and output validation follow the system library. Connections never activate in the offline runtime; activation returns unavailable and a connection query on a valid context returns connection-not-found without writing outputs. SDK-dependent peer-address validation and local-network discovery remain incomplete. - [sceNpSessionSignalingCreateJoinActivate, sceNpSessionSignalingGetConnectionFromNetAddress, sceNpSessionSignalingGetCustomProperty, sceNpSessionSignalingGetGroupFromPeerAddress, sceNpSessionSignalingInitializeWithPort, sceNpSessionSignalingJoinActivate and sceNpSessionSignalingSetCustomProperty](../../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpSessionSignaling.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From e2ead10b22b15f4051a691f0b5cff6c7e3f65c99 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 13:57:22 +0300 Subject: [PATCH 244/523] fix(regmgr): restore binary access signatures --- core/libs/prx/libSceRegMgr/Export.cpp | 4 ++-- docs/dev/debt/libSceRegMgr.md | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceRegMgr/Export.cpp b/core/libs/prx/libSceRegMgr/Export.cpp index e1e4aad2c1..9d71463de6 100644 --- a/core/libs/prx/libSceRegMgr/Export.cpp +++ b/core/libs/prx/libSceRegMgr/Export.cpp @@ -5,7 +5,7 @@ extern "C" { -int APS5_VABI sceRegMgrGetBin() { +int APS5_VABI sceRegMgrGetBin(std::uint32_t optionId, void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -17,7 +17,7 @@ int APS5_VABI sceRegMgrGetStr(int32_t optionId, char* outString, std::size_t str NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceRegMgrSetBin() { +int APS5_VABI sceRegMgrSetBin(std::uint32_t optionId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceRegMgr.md b/docs/dev/debt/libSceRegMgr.md index 9b6dbd6441..24cd287ce7 100644 --- a/docs/dev/debt/libSceRegMgr.md +++ b/docs/dev/debt/libSceRegMgr.md @@ -2,8 +2,6 @@ ### Unknown function info -- [sceRegMgrNonSysGetInt](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr, NID `dKeshzt29G4`) - observed as an import needed for module loading (issue 4720); its `(int64_t key, int32_t unk, int32_t* data)` signature follows [ChonkyStation4](https://github.com/liuk7071/ChonkyStation4/blob/c101a23ecbc15e56c6e30d5dd24d3ed8880a957a/ChonkyStation4/OS/Libraries/SceRegMgr/SceRegMgr.cpp#L38-L47), not measured on a console, so every call throws -- [sceRegMgrGetBin and sceRegMgrSetBin](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRegMgr.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. The string and integer prototypes follow [SharpProspero's SystemSettings bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Platform/SystemSettings.cs) - [sceRegMgrCheckError](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature - [sceRegMgrCheckIntVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature - [sceRegMgrGetBinInitVal](../../../core/libs/prx/libSceRegMgr/Export.cpp) (libSceRegMgr) - unknown signature From 99e5cb5c1a87ab6cb0243db562d9655d8b4b484e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:01:31 +0300 Subject: [PATCH 245/523] fix(appinst): restore verified installation query arguments --- core/libs/prx/libSceAppInstUtil/Export.cpp | 18 ++++++++++-------- docs/dev/debt/libSceAppInstUtil.md | 4 +++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceAppInstUtil/Export.cpp b/core/libs/prx/libSceAppInstUtil/Export.cpp index 845fd759b0..c3485fbe05 100644 --- a/core/libs/prx/libSceAppInstUtil/Export.cpp +++ b/core/libs/prx/libSceAppInstUtil/Export.cpp @@ -3,6 +3,8 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct AppInstallProgressInfo; + extern "C" { int APS5_VABI sceAppInstUtilInitialize(void) { @@ -25,7 +27,7 @@ int APS5_VABI sceAppInstUtilAppIsInInstalling(int32_t* installing) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilAppIsInUpdating() { +int APS5_VABI sceAppInstUtilAppIsInUpdating(const char* titleId, bool* updating) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -33,7 +35,7 @@ int APS5_VABI sceAppInstUtilAppExists(const char* titleId, int32_t* exists) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilAppGetSize() { +int APS5_VABI sceAppInstUtilAppGetSize(const char* titleId, std::uint64_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -53,11 +55,11 @@ int APS5_VABI sceAppInstUtilAppUnInstall(const char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilAppUnInstallAddcont() { +int APS5_VABI sceAppInstUtilAppUnInstallAddcont(const char* titleId, const char* contentLabel) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilAppUnInstallPat() { +int APS5_VABI sceAppInstUtilAppUnInstallPat(const char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -65,19 +67,19 @@ int APS5_VABI sceAppInstUtilAppUnInstallTheme() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilAppUnInstallTypes() { +int APS5_VABI sceAppInstUtilAppUnInstallTypes(const char* titleId, std::uint32_t types) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilGetInstallProgressInfo() { +int APS5_VABI sceAppInstUtilGetInstallProgressInfo(const char* titleId, AppInstallProgressInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilGetInstallProgress() { +int APS5_VABI sceAppInstUtilGetInstallProgress(const char* titleId, void* progress) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilGetPrimaryAppSlot() { +int APS5_VABI sceAppInstUtilGetPrimaryAppSlot(const char* titleId, void* slot) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAppInstUtil.md b/docs/dev/debt/libSceAppInstUtil.md index ecf5fb2a82..92d88fb4a6 100644 --- a/docs/dev/debt/libSceAppInstUtil.md +++ b/docs/dev/debt/libSceAppInstUtil.md @@ -2,4 +2,6 @@ ### Unknown function info -- [sceAppInstUtilAppIsInUpdating, sceAppInstUtilAppGetSize, sceAppInstUtilAppInstallPkg, sceAppInstUtilAppPrepareOverwritePkg, sceAppInstUtilAppRecoverApp, sceAppInstUtilAppUnInstallAddcont, sceAppInstUtilAppUnInstallPat, sceAppInstUtilAppUnInstallTheme, sceAppInstUtilAppUnInstallTypes, sceAppInstUtilGetInstallProgressInfo, sceAppInstUtilGetInstallProgress and sceAppInstUtilGetPrimaryAppSlot](../../../core/libs/prx/libSceAppInstUtil/Export.cpp) (libSceAppInstUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppInstUtil.c), but no credible prototype was found. They are declared without parameters only to resolve the known imports; this is not an ABI claim and callers must not call them until a prototype is verified. The other seven declarations follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppInstUtil/AppInstUtil.cs) +- [sceAppInstUtilAppInstallPkg](../../../core/libs/prx/libSceAppInstUtil/Export.cpp) - the package path is followed by an opaque 64-bit value sent to the install service; its meaning and pointer-versus-integer type remain unresolved. +- [sceAppInstUtilGetInstallProgressInfo, sceAppInstUtilGetInstallProgress and sceAppInstUtilGetPrimaryAppSlot](../../../core/libs/prx/libSceAppInstUtil/Export.cpp) - take a title-id string and an output pointer. The IPC replies contain 48, 4 and 4 bytes respectively; callers and reply producers are needed to establish the field types. +- [sceAppInstUtilAppPrepareOverwritePkg, sceAppInstUtilAppRecoverApp and sceAppInstUtilAppUnInstallTheme](../../../core/libs/prx/libSceAppInstUtil/Export.cpp) - absent from the supplied system module and from the supplied decompiled callers; the old export inventory establishes names but not prototypes. From 8add4fe2580eeecb72f386a26dd68b8cafc8f3fa Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:03:45 +0300 Subject: [PATCH 246/523] fix(bgft): restore task control and progress signatures --- core/libs/prx/libSceBgft/Export.cpp | 43 ++++++++++++++++++----------- docs/dev/debt/libSceBgft.md | 4 ++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceBgft/Export.cpp b/core/libs/prx/libSceBgft/Export.cpp index bb89f27861..8480641566 100644 --- a/core/libs/prx/libSceBgft/Export.cpp +++ b/core/libs/prx/libSceBgft/Export.cpp @@ -3,9 +3,20 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct BgftInitParam { + void* heap; + uint64_t heapSize; +}; + +struct BgftProgress; +struct BgftStorageTaskParam; + +static_assert(sizeof(BgftInitParam) == 16); +static_assert(offsetof(BgftInitParam, heapSize) == 8); + extern "C" { -int APS5_VABI sceBgftServiceInit(void* params) { +int APS5_VABI sceBgftServiceInit(const BgftInitParam* params) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -29,11 +40,11 @@ int APS5_VABI sceBgftServiceDownloadStopTaskAll() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceDownloadFindActivePatchTask() { +int APS5_VABI sceBgftServiceDownloadFindActivePatchTask(const char* contentId, int32_t* taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceDownloadFindActivePupTask() { +int APS5_VABI sceBgftServiceDownloadFindActivePupTask(int32_t* taskId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -41,7 +52,7 @@ int APS5_VABI sceBgftServiceDownloadFindTaskByContentId(const char* contentId, i NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceDownloadGetProgress(int32_t taskId, void* progress) { +int APS5_VABI sceBgftServiceDownloadGetProgress(int32_t taskId, BgftProgress* progress) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -61,15 +72,15 @@ int APS5_VABI sceBgftServiceDownloadStopTask(int32_t taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadFindActiveGameAndGameAcTask() { +int APS5_VABI sceBgftServiceIntDownloadFindActiveGameAndGameAcTask(const char* contentId, int32_t* taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadFindActiveTask() { +int APS5_VABI sceBgftServiceIntDownloadFindActiveTask(const char* contentId, int32_t subType, int32_t* taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadGetGameAndGameAcProgress() { +int APS5_VABI sceBgftServiceIntDownloadGetGameAndGameAcProgress(const char* contentId, BgftProgress* progress) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -77,11 +88,11 @@ int APS5_VABI sceBgftServiceIntDownloadGetPatchProgress() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadGetPlayGoProgress() { +int APS5_VABI sceBgftServiceIntDownloadGetPlayGoProgress(const char* contentId, BgftProgress* progress) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadGetProgress() { +int APS5_VABI sceBgftServiceIntDownloadGetProgress(int32_t taskId, BgftProgress* progress) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -89,31 +100,31 @@ int APS5_VABI sceBgftServiceIntDownloadGetTaskInfo() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadPauseTask() { +int APS5_VABI sceBgftServiceIntDownloadPauseTask(int32_t taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadRegisterTaskByStorageEx() { +int APS5_VABI sceBgftServiceIntDownloadRegisterTaskByStorageEx(const BgftStorageTaskParam* params, int32_t* taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadResumeTask() { +int APS5_VABI sceBgftServiceIntDownloadResumeTask(int32_t taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadStopTask() { +int APS5_VABI sceBgftServiceIntDownloadStopTask(int32_t taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadStartTask() { +int APS5_VABI sceBgftServiceIntDownloadStartTask(int32_t taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntDownloadUnregisterTask() { +int APS5_VABI sceBgftServiceIntDownloadUnregisterTask(int32_t taskId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBgftServiceIntInit() { +int APS5_VABI sceBgftServiceIntInit(const BgftInitParam* params) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceBgft.md b/docs/dev/debt/libSceBgft.md index 8d8e3ccbe7..3da7019e92 100644 --- a/docs/dev/debt/libSceBgft.md +++ b/docs/dev/debt/libSceBgft.md @@ -2,4 +2,6 @@ ### Unknown function info -- [sceBgftServiceDownloadPauseTaskAll, sceBgftServiceDownloadResumeTaskAll, sceBgftServiceDownloadStartTaskAll, sceBgftServiceDownloadStopTaskAll, sceBgftServiceDownloadFindActivePatchTask, sceBgftServiceDownloadFindActivePupTask, sceBgftServiceIntDownloadFindActiveGameAndGameAcTask, sceBgftServiceIntDownloadFindActiveTask, sceBgftServiceIntDownloadGetGameAndGameAcProgress, sceBgftServiceIntDownloadGetPatchProgress, sceBgftServiceIntDownloadGetPlayGoProgress, sceBgftServiceIntDownloadGetProgress, sceBgftServiceIntDownloadGetTaskInfo, sceBgftServiceIntDownloadPauseTask, sceBgftServiceIntDownloadRegisterTaskByStorageEx, sceBgftServiceIntDownloadResumeTask, sceBgftServiceIntDownloadStopTask, sceBgftServiceIntDownloadStartTask, sceBgftServiceIntDownloadUnregisterTask and sceBgftServiceIntInit](../../../core/libs/prx/libSceBgft/Export.cpp) (libSceBgft) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceBgft.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. The eight task-control declarations not listed here follow [SharpProspero's DownloadService bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Platform/DownloadService.cs) +- `sceBgftServiceDownloadGetProgress`, `sceBgftServiceIntDownloadGetProgress`, `sceBgftServiceIntDownloadGetGameAndGameAcProgress` and `sceBgftServiceIntDownloadGetPlayGoProgress`: signatures and the shared 88-byte output are confirmed. The library forwards the output through IPC without accessing individual fields; their types remain unresolved. +- `sceBgftServiceIntDownloadRegisterTaskByStorageEx`: confirmed parameter pointer and 32-bit task ID output. The input contains 32-bit scalars at offsets 0, 4, 56 and 104, strings at offsets 8, 16, 32, 40, 48, 64, 72, 80 and 88, and a 64-bit scalar at offset 96. Scalar meanings and the field at offset 24 remain unresolved; the parameter type is forward-declared. +- `sceBgftServiceIntDownloadGetPatchProgress` and `sceBgftServiceIntDownloadGetTaskInfo`: neither export nor a caller was found in the supplied decompiled library corpus. Their signatures remain unresolved. From 0d237011c704520b815486cbc35b15a2ce590bb6 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:07:58 +0300 Subject: [PATCH 247/523] fix(keycode): restore virtual key mappings and argument types --- core/libs/prx/libSceConvertKeycode/Export.cpp | 226 +++++++++++++++++- core/libs/tests/GuestConvertKeycode.cpp | 23 ++ docs/dev/debt/libSceConvertKeycode.md | 2 + 3 files changed, 246 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceConvertKeycode/Export.cpp b/core/libs/prx/libSceConvertKeycode/Export.cpp index 6d6a3f0048..7eb667382d 100644 --- a/core/libs/prx/libSceConvertKeycode/Export.cpp +++ b/core/libs/prx/libSceConvertKeycode/Export.cpp @@ -1,13 +1,225 @@ +#include #include #include #include #include "SceTypes.hpp" -#include "HitLog.hpp" #include "prx/libc/include/General.hpp" namespace { constexpr int kErrInvalidAddress = static_cast(0x80BC0031u); constexpr int kErrInvalidUserId = static_cast(0x80BC0010u); +constexpr std::array BaseVirtualKeys = { + 255, 255, 255, 255, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 48, 13, 27, 8, 9, 32, 187, 219, 221, + 186, 191, 191, 192, 222, 220, 188, 190, 189, 20, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 44, 145, 19, 45, 36, 33, 46, 35, 34, 39, + 37, 40, 38, 144, 111, 106, 109, 107, 13, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 96, 110, 226, 93, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 12, 255, 255, 255, 255, 255, 255, 255, + 162, 160, 164, 91, 163, 161, 165, 92, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +}; + +constexpr auto VirtualKeys = [] { + std::array, 39> layouts{}; + for (auto& layout : layouts) layout = BaseVirtualKeys; + layouts[1][28] = 90; + layouts[1][29] = 89; + layouts[1][45] = 189; + layouts[1][46] = 221; + layouts[1][47] = 186; + layouts[1][48] = 187; + layouts[2][28] = 90; + layouts[2][29] = 89; + layouts[2][45] = 219; + layouts[2][46] = 221; + layouts[2][47] = 186; + layouts[2][48] = 192; + layouts[2][49] = 223; + layouts[2][50] = 223; + layouts[2][51] = 222; + layouts[2][52] = 220; + layouts[2][53] = 191; + layouts[3][45] = 189; + layouts[3][46] = 187; + layouts[3][47] = 219; + layouts[3][48] = 221; + layouts[3][49] = 220; + layouts[3][50] = 220; + layouts[3][51] = 186; + layouts[3][53] = 192; + layouts[3][56] = 191; + layouts[3][100] = 255; + layouts[4][45] = 189; + layouts[4][46] = 187; + layouts[4][47] = 219; + layouts[4][48] = 221; + layouts[4][49] = 222; + layouts[4][50] = 222; + layouts[4][51] = 186; + layouts[4][52] = 192; + layouts[4][53] = 223; + layouts[4][56] = 191; + layouts[5][45] = 219; + layouts[5][46] = 221; + layouts[5][47] = 186; + layouts[5][48] = 187; + layouts[6][45] = 219; + layouts[6][46] = 221; + layouts[6][47] = 186; + layouts[6][48] = 187; + layouts[6][100] = 255; + layouts[8][4] = 81; + layouts[8][16] = 188; + layouts[8][20] = 65; + layouts[8][26] = 90; + layouts[8][29] = 87; + layouts[8][45] = 219; + layouts[8][46] = 187; + layouts[8][49] = 220; + layouts[8][50] = 220; + layouts[8][51] = 77; + layouts[8][52] = 192; + layouts[8][53] = 222; + layouts[8][54] = 190; + layouts[8][55] = 191; + layouts[8][56] = 223; + layouts[9][4] = 81; + layouts[9][16] = 188; + layouts[9][20] = 65; + layouts[9][26] = 90; + layouts[9][29] = 87; + layouts[9][45] = 219; + layouts[9][46] = 189; + layouts[9][49] = 220; + layouts[9][50] = 220; + layouts[9][51] = 77; + layouts[9][52] = 192; + layouts[9][53] = 222; + layouts[9][54] = 190; + layouts[9][55] = 191; + layouts[9][56] = 187; + layouts[10][45] = 189; + layouts[10][46] = 187; + layouts[10][47] = 219; + layouts[10][48] = 221; + layouts[10][49] = 220; + layouts[10][50] = 220; + layouts[10][51] = 186; + layouts[10][52] = 192; + layouts[10][53] = 222; + layouts[10][56] = 191; + layouts[11] = layouts[2]; + layouts[12] = layouts[5]; + layouts[13][45] = 219; + layouts[13][46] = 191; + layouts[13][49] = 220; + layouts[13][50] = 220; + layouts[13][51] = 187; + layouts[13][52] = 192; + layouts[13][53] = 222; + layouts[15][46] = 191; + layouts[15][47] = 219; + layouts[15][48] = 221; + layouts[15][49] = 220; + layouts[15][50] = 220; + layouts[15][51] = 186; + layouts[15][53] = 192; + layouts[16][45] = 189; + layouts[16][46] = 187; + layouts[16][47] = 219; + layouts[16][48] = 221; + layouts[16][49] = 220; + layouts[16][50] = 220; + layouts[16][51] = 186; + layouts[16][53] = 192; + layouts[16][56] = 191; + layouts[17] = layouts[5]; + layouts[18] = layouts[3]; + layouts[20][45] = 223; + layouts[20][46] = 189; + layouts[20][47] = 219; + layouts[20][48] = 221; + layouts[20][49] = 188; + layouts[20][50] = 188; + layouts[20][51] = 186; + layouts[20][52] = 190; + layouts[20][53] = 192; + layouts[20][54] = 191; + layouts[20][55] = 220; + layouts[20][56] = 190; + layouts[21][45] = 189; + layouts[21][46] = 222; + layouts[21][47] = 192; + layouts[21][48] = 219; + layouts[21][49] = 221; + layouts[21][50] = 221; + layouts[21][51] = 187; + layouts[21][52] = 186; + layouts[21][53] = 243; + layouts[21][56] = 191; + layouts[21][100] = 255; + layouts[21][135] = 226; + layouts[21][136] = 242; + layouts[21][137] = 220; + layouts[21][138] = 28; + layouts[21][139] = 247; + layouts[22] = layouts[21]; + layouts[23][45] = 189; + layouts[23][46] = 187; + layouts[23][47] = 219; + layouts[23][48] = 221; + layouts[23][49] = 220; + layouts[23][50] = 220; + layouts[23][51] = 186; + layouts[23][53] = 192; + layouts[23][56] = 191; + layouts[23][136] = 21; + layouts[23][138] = 25; + layouts[24] = layouts[3]; + layouts[25] = layouts[3]; + layouts[26] = layouts[3]; + layouts[27] = layouts[3]; + layouts[28] = layouts[3]; + layouts[29] = layouts[16]; + layouts[30] = layouts[16]; + layouts[31][28] = 90; + layouts[31][29] = 89; + layouts[31][46] = 191; + layouts[31][47] = 219; + layouts[31][48] = 221; + layouts[31][49] = 220; + layouts[31][50] = 220; + layouts[31][51] = 186; + layouts[31][53] = 192; + layouts[32] = layouts[16]; + layouts[33] = layouts[16]; + layouts[34] = layouts[16]; + layouts[35] = layouts[16]; + layouts[36][28] = 90; + layouts[36][29] = 89; + layouts[36][39] = 192; + layouts[36][45] = 191; + layouts[36][46] = 187; + layouts[36][47] = 219; + layouts[36][48] = 221; + layouts[36][49] = 220; + layouts[36][50] = 220; + layouts[36][51] = 186; + layouts[36][53] = 48; + layouts[37] = layouts[3]; + layouts[38] = layouts[3]; + return layouts; +}(); + +constexpr int kErrInvalidKeyboardType = static_cast(0x80BC001Du); constexpr std::uint32_t kIgnoredStateBits = 0x4000000u; char16_t UsCharacter(std::uint16_t keycode) { @@ -32,10 +244,14 @@ int APS5_VABI sceConvertKeycodeGetImeKeyboardType(int32_t userId, uint32_t* type *type = 0; return 0; } -int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint64_t a0, std::uint64_t a1, std::uint64_t a2, std::uint64_t a3) { - APS5_HIT("CONVERTKEYCODE", "sceConvertKeycodeGetVirtualKeycode args 0x%llx 0x%llx 0x%llx 0x%llx -> error (signature unknown)", - static_cast(a0), static_cast(a1), static_cast(a2), static_cast(a3)); - return kErrInvalidAddress; +int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint16_t keycode, std::uint32_t keyboardType, std::uint16_t* virtualKeycode) { + if (virtualKeycode == nullptr) return kErrInvalidAddress; + if (keyboardType == 0 || keyboardType > VirtualKeys.size()) return kErrInvalidKeyboardType; + if (keyboardType >= 31) throw std::runtime_error("sceConvertKeycodeGetVirtualKeycode: guest sdk version is unavailable"); + auto value = keycode < BaseVirtualKeys.size() ? VirtualKeys[keyboardType - 1][keycode] : 255; + if ((keyboardType == 2 || keyboardType == 3 || keyboardType == 12) && (value == 89 || value == 90)) throw std::runtime_error("sceConvertKeycodeGetVirtualKeycode: guest sdk version is unavailable"); + *virtualKeycode = static_cast(value); + return 0; } int APS5_VABI sceConvertKeycodeGetCharacterFromKeyboardData(const KeyboardData* keyData, std::int32_t index, std::uint32_t state, std::uint32_t keyboardType, char16_t* character) { diff --git a/core/libs/tests/GuestConvertKeycode.cpp b/core/libs/tests/GuestConvertKeycode.cpp index 0e7095e097..1f2ec31789 100644 --- a/core/libs/tests/GuestConvertKeycode.cpp +++ b/core/libs/tests/GuestConvertKeycode.cpp @@ -3,8 +3,10 @@ #include #include #include +#include extern "C" { +int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint16_t, std::uint32_t, std::uint16_t*); int APS5_VABI sceConvertKeycodeGetImeKeyboardType(std::int32_t, std::uint32_t*); int APS5_VABI sceConvertKeycodeGetCharacter(std::uint16_t, std::uint32_t, std::uint32_t, char16_t*); } @@ -18,6 +20,27 @@ int main() { Require(sceConvertKeycodeGetImeKeyboardType(0, nullptr) == invalidAddress); Require(sceConvertKeycodeGetImeKeyboardType(-1, nullptr) == invalidAddress); + std::uint16_t virtualKey = 0xABCD; + Require(sceConvertKeycodeGetVirtualKeycode(4, 0, nullptr) == invalidAddress); + Require(sceConvertKeycodeGetVirtualKeycode(4, 0, &virtualKey) == static_cast(0x80BC001Du)); + Require(virtualKey == 0xABCD); + Require(sceConvertKeycodeGetVirtualKeycode(4, 40, &virtualKey) == static_cast(0x80BC001Du)); + Require(sceConvertKeycodeGetVirtualKeycode(4, 4, &virtualKey) == 0 && virtualKey == 65); + Require(sceConvertKeycodeGetVirtualKeycode(4, 9, &virtualKey) == 0 && virtualKey == 81); + Require(sceConvertKeycodeGetVirtualKeycode(100, 4, &virtualKey) == 0 && virtualKey == 255); + Require(sceConvertKeycodeGetVirtualKeycode(136, 24, &virtualKey) == 0 && virtualKey == 21); + Require(sceConvertKeycodeGetVirtualKeycode(65535, 4, &virtualKey) == 0 && virtualKey == 255); + for (std::uint32_t layout : {2u, 3u, 12u, 31u, 39u}) { + virtualKey = 0xABCD; + bool rejected = false; + try { + sceConvertKeycodeGetVirtualKeycode(28, layout, &virtualKey); + } catch (const std::runtime_error&) { + rejected = true; + } + Require(rejected && virtualKey == 0xABCD); + } + std::uint32_t type = 0xFFFFFFFFu; Require(sceConvertKeycodeGetImeKeyboardType(-1, &type) == invalidUserId); Require(type == 0xFFFFFFFFu); diff --git a/docs/dev/debt/libSceConvertKeycode.md b/docs/dev/debt/libSceConvertKeycode.md index 706e31a66e..fbf5f9d6cd 100644 --- a/docs/dev/debt/libSceConvertKeycode.md +++ b/docs/dev/debt/libSceConvertKeycode.md @@ -7,3 +7,5 @@ ### Functional - [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) takes the keycode as a USB HID keyboard usage and returns the unshifted character of the US layout for letters, digits, punctuation, space and the keypad. A keycode without a character returns 0 with the character 0; whether the console returns an error instead is not measured. + +- `sceConvertKeycodeGetVirtualKeycode`: HID-to-virtual-key mappings and the three-argument signature are confirmed. Layouts 31–39 and version-dependent Y/Z mappings for layouts 2, 3 and 12 throw until the guest platform and compiled SDK version are exposed; no version is guessed. From b5dbd377ed62b5b7f1bcc57ed8d69895dbb2eaa8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:12:35 +0300 Subject: [PATCH 248/523] fix(keycode): restore layout modifiers and character conversion --- core/libs/prx/libSceConvertKeycode/Export.cpp | 2194 ++++++++++++++++- core/libs/tests/GuestConvertKeycode.cpp | 70 +- docs/dev/debt/libSceConvertKeycode.md | 9 +- 3 files changed, 2218 insertions(+), 55 deletions(-) diff --git a/core/libs/prx/libSceConvertKeycode/Export.cpp b/core/libs/prx/libSceConvertKeycode/Export.cpp index 7eb667382d..a38cb0293c 100644 --- a/core/libs/prx/libSceConvertKeycode/Export.cpp +++ b/core/libs/prx/libSceConvertKeycode/Export.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" @@ -220,19 +219,2179 @@ constexpr auto VirtualKeys = [] { }(); constexpr int kErrInvalidKeyboardType = static_cast(0x80BC001Du); -constexpr std::uint32_t kIgnoredStateBits = 0x4000000u; +constexpr auto CharacterTables = [] { + std::array, 111> tables{}; + tables[1][4] = 327777; + tables[1][5] = 327778; + tables[1][6] = 327779; + tables[1][7] = 327780; + tables[1][8] = 327781; + tables[1][9] = 327782; + tables[1][10] = 327783; + tables[1][11] = 327784; + tables[1][12] = 327785; + tables[1][13] = 327786; + tables[1][14] = 327787; + tables[1][15] = 327788; + tables[1][16] = 327789; + tables[1][17] = 327790; + tables[1][18] = 327791; + tables[1][19] = 327792; + tables[1][20] = 327793; + tables[1][21] = 327794; + tables[1][22] = 327795; + tables[1][23] = 327796; + tables[1][24] = 327797; + tables[1][25] = 327798; + tables[1][26] = 327799; + tables[1][27] = 327800; + tables[1][28] = 327802; + tables[1][29] = 327801; + tables[1][30] = 262193; + tables[1][31] = 262194; + tables[1][32] = 262195; + tables[1][33] = 262196; + tables[1][34] = 262197; + tables[1][35] = 262198; + tables[1][36] = 262199; + tables[1][37] = 262200; + tables[1][38] = 262201; + tables[1][39] = 262192; + tables[1][40] = 262154; + tables[1][42] = 262152; + tables[1][43] = 262153; + tables[1][44] = 262176; + tables[1][45] = 262183; + tables[1][46] = 262238; + tables[1][47] = 262396; + tables[1][48] = 262312; + tables[1][49] = 262180; + tables[1][50] = 262180; + tables[1][51] = 262390; + tables[1][52] = 262372; + tables[1][53] = 262311; + tables[1][54] = 262188; + tables[1][55] = 262190; + tables[1][56] = 262189; + tables[1][83] = 393299; + tables[1][84] = 393263; + tables[1][85] = 393258; + tables[1][86] = 393261; + tables[1][87] = 393259; + tables[1][88] = 393226; + tables[1][89] = 393293; + tables[1][90] = 393297; + tables[1][91] = 393294; + tables[1][92] = 393296; + tables[1][93] = 393309; + tables[1][94] = 393295; + tables[1][95] = 393290; + tables[1][96] = 393298; + tables[1][97] = 393291; + tables[1][98] = 393289; + tables[1][99] = 393292; + tables[1][100] = 262204; + tables[1][103] = 393277; + tables[1][133] = 393260; + tables[1][134] = 393277; + tables[1][140] = 393260; + tables[2] = tables[1]; + tables[2][4] = 327745; + tables[2][5] = 327746; + tables[2][6] = 327747; + tables[2][7] = 327748; + tables[2][8] = 327749; + tables[2][9] = 327750; + tables[2][10] = 327751; + tables[2][11] = 327752; + tables[2][12] = 327753; + tables[2][13] = 327754; + tables[2][14] = 327755; + tables[2][15] = 327756; + tables[2][16] = 327757; + tables[2][17] = 327758; + tables[2][18] = 327759; + tables[2][19] = 327760; + tables[2][20] = 327761; + tables[2][21] = 327762; + tables[2][22] = 327763; + tables[2][23] = 327764; + tables[2][24] = 327765; + tables[2][25] = 327766; + tables[2][26] = 327767; + tables[2][27] = 327768; + tables[2][28] = 327770; + tables[2][29] = 327769; + tables[2][30] = 262187; + tables[2][31] = 262178; + tables[2][32] = 262186; + tables[2][33] = 262375; + tables[2][34] = 262181; + tables[2][35] = 262182; + tables[2][36] = 262191; + tables[2][37] = 262184; + tables[2][38] = 262185; + tables[2][39] = 262205; + tables[2][45] = 262207; + tables[2][46] = 262240; + tables[2][47] = 262376; + tables[2][48] = 262177; + tables[2][49] = 262307; + tables[2][50] = 262307; + tables[2][51] = 262377; + tables[2][52] = 262368; + tables[2][53] = 262320; + tables[2][54] = 262203; + tables[2][55] = 262202; + tables[2][56] = 262239; + tables[2][89] = 393265; + tables[2][90] = 393266; + tables[2][91] = 393267; + tables[2][92] = 393268; + tables[2][93] = 393269; + tables[2][94] = 393270; + tables[2][95] = 393271; + tables[2][96] = 393272; + tables[2][97] = 393273; + tables[2][98] = 393264; + tables[2][99] = 393262; + tables[2][100] = 262206; + tables[3][8] = 336044; + tables[3][30] = 262310; + tables[3][31] = 262208; + tables[3][32] = 262179; + tables[3][33] = 262320; + tables[3][34] = 262311; + tables[3][35] = 262316; + tables[3][36] = 262268; + tables[3][37] = 262306; + tables[3][45] = 262324; + tables[3][46] = 262270; + tables[3][47] = 262235; + tables[3][48] = 262237; + tables[3][49] = 262269; + tables[3][50] = 262269; + tables[3][52] = 262267; + tables[3][100] = 262236; + tables[3][136] = 262324; + tables[3][137] = 262324; + tables[3][138] = 262324; + tables[3][146] = 262324; + tables[3][147] = 262324; + tables[4] = tables[1]; + tables[4][28] = 327801; + tables[4][29] = 327802; + tables[4][45] = 262189; + tables[4][46] = 262205; + tables[4][47] = 262235; + tables[4][48] = 262237; + tables[4][49] = 262236; + tables[4][50] = 262236; + tables[4][51] = 262203; + tables[4][52] = 262183; + tables[4][53] = 262240; + tables[4][56] = 262191; + tables[4][100] = 262236; + tables[5] = tables[2]; + tables[5][28] = 327769; + tables[5][29] = 327770; + tables[5][30] = 262177; + tables[5][31] = 262208; + tables[5][32] = 262179; + tables[5][33] = 262180; + tables[5][35] = 262238; + tables[5][36] = 262182; + tables[5][37] = 262186; + tables[5][38] = 262184; + tables[5][39] = 262185; + tables[5][45] = 262239; + tables[5][46] = 262187; + tables[5][47] = 262267; + tables[5][48] = 262269; + tables[5][49] = 262268; + tables[5][50] = 262268; + tables[5][51] = 262202; + tables[5][52] = 262178; + tables[5][53] = 262270; + tables[5][54] = 262204; + tables[5][55] = 262206; + tables[5][56] = 262207; + tables[5][100] = 262268; + tables[6][34] = 270508; + tables[7] = tables[1]; + tables[7][4] = 393025; + tables[7][5] = 393026; + tables[7][6] = 393027; + tables[7][7] = 393028; + tables[7][8] = 393029; + tables[7][9] = 393030; + tables[7][10] = 393031; + tables[7][11] = 393032; + tables[7][12] = 393033; + tables[7][13] = 393034; + tables[7][14] = 393035; + tables[7][15] = 393036; + tables[7][16] = 393037; + tables[7][17] = 393038; + tables[7][18] = 393039; + tables[7][19] = 393040; + tables[7][20] = 393041; + tables[7][21] = 393042; + tables[7][22] = 393043; + tables[7][23] = 393044; + tables[7][24] = 393045; + tables[7][25] = 393046; + tables[7][26] = 393047; + tables[7][27] = 393048; + tables[7][28] = 393049; + tables[7][29] = 393050; + tables[7][30] = 327441; + tables[7][31] = 327442; + tables[7][32] = 327443; + tables[7][33] = 327444; + tables[7][34] = 327445; + tables[7][35] = 327446; + tables[7][36] = 327447; + tables[7][37] = 327448; + tables[7][38] = 327449; + tables[7][39] = 327440; + tables[7][44] = 274432; + tables[7][45] = 327437; + tables[7][46] = 327486; + tables[7][47] = 327456; + tables[7][48] = 327483; + tables[7][49] = 327485; + tables[7][50] = 327485; + tables[7][51] = 327451; + tables[7][52] = 327450; + tables[7][53] = 0; + tables[7][54] = 327436; + tables[7][55] = 327438; + tables[7][56] = 327439; + tables[7][100] = 0; + tables[7][135] = 327653; + tables[8] = tables[2]; + tables[8][4] = 392993; + tables[8][5] = 392994; + tables[8][6] = 392995; + tables[8][7] = 392996; + tables[8][8] = 392997; + tables[8][9] = 392998; + tables[8][10] = 392999; + tables[8][11] = 393000; + tables[8][12] = 393001; + tables[8][13] = 393002; + tables[8][14] = 393003; + tables[8][15] = 393004; + tables[8][16] = 393005; + tables[8][17] = 393006; + tables[8][18] = 393007; + tables[8][19] = 393008; + tables[8][20] = 393009; + tables[8][21] = 393010; + tables[8][22] = 393011; + tables[8][23] = 393012; + tables[8][24] = 393013; + tables[8][25] = 393014; + tables[8][26] = 393015; + tables[8][27] = 393016; + tables[8][28] = 393017; + tables[8][29] = 393018; + tables[8][30] = 327425; + tables[8][31] = 270364; + tables[8][32] = 327427; + tables[8][33] = 327428; + tables[8][34] = 327429; + tables[8][35] = 327430; + tables[8][36] = 270360; + tables[8][37] = 327432; + tables[8][38] = 327433; + tables[8][39] = 0; + tables[8][45] = 327453; + tables[8][46] = 327518; + tables[8][47] = 327488; + tables[8][48] = 327515; + tables[8][49] = 327517; + tables[8][50] = 327517; + tables[8][51] = 327435; + tables[8][52] = 327434; + tables[8][53] = 0; + tables[8][54] = 327452; + tables[8][55] = 327454; + tables[8][56] = 327455; + tables[8][100] = 0; + tables[8][135] = 327487; + tables[8][137] = 327516; + tables[9] = tables[4]; + tables[9][46] = 262238; + tables[9][47] = 262208; + tables[9][48] = 262235; + tables[9][49] = 262237; + tables[9][50] = 262237; + tables[9][52] = 262202; + tables[9][53] = 0; + tables[9][100] = 0; + tables[9][135] = 262236; + tables[9][137] = 262309; + tables[10] = tables[5]; + tables[10][31] = 262178; + tables[10][35] = 262182; + tables[10][36] = 262183; + tables[10][37] = 262184; + tables[10][38] = 262185; + tables[10][39] = 0; + tables[10][45] = 262205; + tables[10][46] = 262270; + tables[10][47] = 262240; + tables[10][48] = 262267; + tables[10][49] = 262269; + tables[10][50] = 262269; + tables[10][51] = 262187; + tables[10][52] = 262186; + tables[10][53] = 0; + tables[10][100] = 0; + tables[10][135] = 262239; + tables[10][137] = 262268; + tables[11] = tables[9]; + tables[11][4] = 274625; + tables[11][5] = 274611; + tables[11][6] = 274621; + tables[11][7] = 274615; + tables[11][8] = 274596; + tables[11][9] = 274639; + tables[11][10] = 274605; + tables[11][11] = 274607; + tables[11][12] = 274635; + tables[11][13] = 274654; + tables[11][14] = 274638; + tables[11][15] = 274666; + tables[11][16] = 274658; + tables[11][17] = 274655; + tables[11][18] = 274665; + tables[11][19] = 274619; + tables[11][20] = 274623; + tables[11][21] = 274617; + tables[11][22] = 274632; + tables[11][23] = 274603; + tables[11][24] = 274634; + tables[11][25] = 274642; + tables[11][26] = 274630; + tables[11][27] = 274613; + tables[11][28] = 274675; + tables[11][29] = 274628; + tables[11][30] = 274636; + tables[11][31] = 274645; + tables[11][32] = 274594; + tables[11][33] = 274598; + tables[11][34] = 274600; + tables[11][35] = 274602; + tables[11][36] = 274660; + tables[11][37] = 274662; + tables[11][38] = 274664; + tables[11][39] = 274671; + tables[11][45] = 274651; + tables[11][46] = 274648; + tables[11][47] = 274587; + tables[11][48] = 274588; + tables[11][49] = 274656; + tables[11][50] = 274656; + tables[11][51] = 274668; + tables[11][52] = 274609; + tables[11][54] = 274637; + tables[11][55] = 274667; + tables[11][56] = 274657; + tables[11][135] = 274669; + tables[11][137] = 274684; + tables[12] = tables[11]; + tables[12][8] = 274595; + tables[12][29] = 274627; + tables[12][32] = 274593; + tables[12][33] = 274597; + tables[12][34] = 274599; + tables[12][35] = 274601; + tables[12][36] = 274659; + tables[12][37] = 274661; + tables[12][38] = 274663; + tables[12][39] = 274674; + tables[12][48] = 274444; + tables[12][49] = 274445; + tables[12][50] = 274445; + tables[12][54] = 274433; + tables[12][55] = 274434; + tables[12][56] = 274683; + tables[12][89] = 393265; + tables[12][90] = 393266; + tables[12][91] = 393267; + tables[12][92] = 393268; + tables[12][93] = 393269; + tables[12][94] = 393270; + tables[12][95] = 393271; + tables[12][96] = 393272; + tables[12][97] = 393273; + tables[12][98] = 393264; + tables[12][99] = 393262; + tables[13] = tables[9]; + tables[13][4] = 327553; + tables[13][5] = 327546; + tables[13][6] = 327551; + tables[13][7] = 327548; + tables[13][8] = 327538; + tables[13][9] = 327562; + tables[13][10] = 327543; + tables[13][11] = 327544; + tables[13][12] = 327558; + tables[13][13] = 327567; + tables[13][14] = 327561; + tables[13][15] = 327576; + tables[13][16] = 327571; + tables[13][17] = 327568; + tables[13][18] = 327575; + tables[13][19] = 327550; + tables[13][20] = 327552; + tables[13][21] = 327549; + tables[13][22] = 327556; + tables[13][23] = 327542; + tables[13][24] = 327557; + tables[13][25] = 327563; + tables[13][26] = 327555; + tables[13][27] = 327547; + tables[13][28] = 327581; + tables[13][29] = 327554; + tables[13][30] = 327559; + tables[13][31] = 327564; + tables[13][32] = 327537; + tables[13][33] = 327539; + tables[13][34] = 327540; + tables[13][35] = 327541; + tables[13][36] = 327572; + tables[13][37] = 327573; + tables[13][38] = 327574; + tables[13][39] = 327580; + tables[13][45] = 327566; + tables[13][46] = 327565; + tables[13][47] = 327582; + tables[13][48] = 327583; + tables[13][49] = 327569; + tables[13][50] = 327569; + tables[13][51] = 327578; + tables[13][52] = 327545; + tables[13][54] = 327560; + tables[13][55] = 327577; + tables[13][56] = 327570; + tables[13][135] = 327579; + tables[13][137] = 327536; + tables[14] = tables[13]; + tables[14][8] = 327528; + tables[14][29] = 327535; + tables[14][32] = 327527; + tables[14][33] = 327529; + tables[14][34] = 327530; + tables[14][35] = 327531; + tables[14][36] = 327532; + tables[14][37] = 327533; + tables[14][38] = 327534; + tables[14][39] = 327526; + tables[14][48] = 327522; + tables[14][49] = 327523; + tables[14][50] = 327523; + tables[14][54] = 327524; + tables[14][55] = 327521; + tables[14][56] = 327525; + tables[14][89] = 393265; + tables[14][90] = 393266; + tables[14][91] = 393267; + tables[14][92] = 393268; + tables[14][93] = 393269; + tables[14][94] = 393270; + tables[14][95] = 393271; + tables[14][96] = 393272; + tables[14][97] = 393273; + tables[14][98] = 393264; + tables[14][99] = 393262; + tables[15] = tables[11]; + tables[15][4] = 274529; + tables[15][5] = 274515; + tables[15][6] = 274525; + tables[15][7] = 274519; + tables[15][8] = 274500; + tables[15][9] = 274543; + tables[15][10] = 274509; + tables[15][11] = 274511; + tables[15][12] = 274539; + tables[15][13] = 274558; + tables[15][14] = 274542; + tables[15][15] = 274570; + tables[15][16] = 274562; + tables[15][17] = 274559; + tables[15][18] = 274569; + tables[15][19] = 274523; + tables[15][20] = 274527; + tables[15][21] = 274521; + tables[15][22] = 274536; + tables[15][23] = 274507; + tables[15][24] = 274538; + tables[15][25] = 274546; + tables[15][26] = 274534; + tables[15][27] = 274517; + tables[15][28] = 274579; + tables[15][29] = 274532; + tables[15][30] = 274540; + tables[15][31] = 274549; + tables[15][32] = 274498; + tables[15][33] = 274502; + tables[15][34] = 274504; + tables[15][35] = 274506; + tables[15][36] = 274564; + tables[15][37] = 274566; + tables[15][38] = 274568; + tables[15][39] = 274575; + tables[15][45] = 274555; + tables[15][46] = 274552; + tables[15][49] = 274560; + tables[15][50] = 274560; + tables[15][51] = 274572; + tables[15][52] = 274513; + tables[15][54] = 274541; + tables[15][55] = 274571; + tables[15][56] = 274561; + tables[15][135] = 274573; + tables[16] = tables[15]; + tables[16][8] = 274499; + tables[16][29] = 274531; + tables[16][32] = 274497; + tables[16][33] = 274501; + tables[16][34] = 274503; + tables[16][35] = 274505; + tables[16][36] = 274563; + tables[16][37] = 274565; + tables[16][38] = 274567; + tables[16][39] = 274578; + tables[16][48] = 274444; + tables[16][49] = 274445; + tables[16][50] = 274445; + tables[16][54] = 274433; + tables[16][55] = 274434; + tables[16][56] = 274683; + tables[16][89] = 393265; + tables[16][90] = 393266; + tables[16][91] = 393267; + tables[16][92] = 393268; + tables[16][93] = 393269; + tables[16][94] = 393270; + tables[16][95] = 393271; + tables[16][96] = 393272; + tables[16][97] = 393273; + tables[16][98] = 393264; + tables[16][99] = 393262; + tables[17] = tables[1]; + tables[17][30] = 327729; + tables[17][31] = 327730; + tables[17][32] = 327731; + tables[17][33] = 327732; + tables[17][34] = 327733; + tables[17][35] = 327734; + tables[17][36] = 327735; + tables[17][37] = 327736; + tables[17][38] = 327737; + tables[17][39] = 327728; + tables[17][45] = 327903; + tables[17][46] = 262324; + tables[17][47] = 327932; + tables[17][48] = 327723; + tables[17][49] = 327715; + tables[17][50] = 327715; + tables[17][51] = 327926; + tables[17][52] = 327908; + tables[17][53] = 262238; + tables[17][54] = 327724; + tables[17][55] = 327726; + tables[17][136] = 262238; + tables[17][137] = 262238; + tables[17][138] = 262238; + tables[17][146] = 262238; + tables[17][147] = 262238; + tables[18] = tables[2]; + tables[18][30] = 327713; + tables[18][31] = 327714; + tables[18][32] = 327847; + tables[18][33] = 327716; + tables[18][34] = 327717; + tables[18][35] = 327718; + tables[18][36] = 327727; + tables[18][37] = 327720; + tables[18][38] = 327721; + tables[18][39] = 327741; + tables[18][45] = 327743; + tables[18][47] = 327900; + tables[18][48] = 327722; + tables[18][49] = 327719; + tables[18][50] = 327719; + tables[18][51] = 327894; + tables[18][52] = 327876; + tables[18][54] = 327739; + tables[18][55] = 327738; + tables[19][8] = 336044; + tables[19][16] = 327861; + tables[19][20] = 327744; + tables[19][31] = 327858; + tables[19][32] = 327859; + tables[19][36] = 327803; + tables[19][37] = 327771; + tables[19][38] = 327773; + tables[19][39] = 327805; + tables[19][45] = 327772; + tables[19][48] = 327806; + tables[19][100] = 262268; + tables[20] = tables[1]; + tables[20][28] = 327801; + tables[20][29] = 327802; + tables[20][46] = 262305; + tables[20][47] = 262240; + tables[20][48] = 262187; + tables[20][49] = 327911; + tables[20][50] = 327911; + tables[20][51] = 327921; + tables[20][52] = 262324; + tables[20][53] = 262330; + tables[21] = tables[2]; + tables[21][28] = 327769; + tables[21][29] = 327770; + tables[21][30] = 262177; + tables[21][32] = 262327; + tables[21][33] = 262180; + tables[21][46] = 262335; + tables[21][47] = 262238; + tables[21][48] = 262186; + tables[21][49] = 327879; + tables[21][50] = 327879; + tables[21][51] = 327889; + tables[21][52] = 262312; + tables[21][53] = 262314; + tables[22] = tables[3]; + tables[22][30] = 262268; + tables[22][33] = 262270; + tables[22][34] = 262272; + tables[22][36] = 0; + tables[22][37] = 0; + tables[22][45] = 0; + tables[22][46] = 0; + tables[22][49] = 327805; + tables[22][50] = 327805; + tables[22][53] = 262236; + tables[22][100] = 0; + tables[22][136] = 262270; + tables[22][137] = 262270; + tables[22][138] = 262270; + tables[22][146] = 262270; + tables[22][147] = 262270; + tables[23] = tables[17]; + tables[23][4] = 327793; + tables[23][16] = 327724; + tables[23][20] = 327777; + tables[23][26] = 327802; + tables[23][28] = 327801; + tables[23][29] = 327799; + tables[23][30] = 327718; + tables[23][31] = 327913; + tables[23][32] = 327714; + tables[23][33] = 327719; + tables[23][34] = 327720; + tables[23][35] = 327725; + tables[23][36] = 327912; + tables[23][37] = 327775; + tables[23][38] = 327911; + tables[23][39] = 327904; + tables[23][45] = 327721; + tables[23][46] = 327741; + tables[23][47] = 327774; + tables[23][48] = 327716; + tables[23][49] = 327722; + tables[23][50] = 327722; + tables[23][51] = 327789; + tables[23][52] = 327929; + tables[23][53] = 262322; + tables[23][54] = 327739; + tables[23][55] = 327738; + tables[23][56] = 327713; + tables[24] = tables[21]; + tables[24][4] = 327761; + tables[24][16] = 327743; + tables[24][20] = 327745; + tables[24][26] = 327770; + tables[24][29] = 327767; + tables[24][30] = 327729; + tables[24][31] = 327730; + tables[24][32] = 327731; + tables[24][33] = 327732; + tables[24][34] = 327733; + tables[24][35] = 327734; + tables[24][36] = 327735; + tables[24][37] = 327736; + tables[24][38] = 327737; + tables[24][39] = 327728; + tables[24][45] = 327856; + tables[24][46] = 327723; + tables[24][47] = 327848; + tables[24][48] = 327843; + tables[24][49] = 327861; + tables[24][50] = 327861; + tables[24][51] = 327757; + tables[24][52] = 327717; + tables[24][53] = 0; + tables[24][54] = 327726; + tables[24][55] = 327727; + tables[24][56] = 327847; + tables[24][136] = 262312; + tables[24][137] = 262312; + tables[24][138] = 262312; + tables[24][146] = 262312; + tables[24][147] = 262312; + tables[25][8] = 336044; + tables[25][31] = 262270; + tables[25][32] = 327715; + tables[25][33] = 327803; + tables[25][34] = 327771; + tables[25][35] = 327804; + tables[25][36] = 262240; + tables[25][37] = 327772; + tables[25][38] = 327774; + tables[25][39] = 327744; + tables[25][45] = 327773; + tables[25][46] = 327805; + tables[25][48] = 327844; + tables[25][136] = 262270; + tables[25][137] = 262270; + tables[25][138] = 262270; + tables[25][146] = 262270; + tables[25][147] = 262270; + tables[26] = tables[20]; + tables[26][46] = 262380; + tables[26][47] = 262376; + tables[26][49] = 262393; + tables[26][50] = 262393; + tables[26][51] = 262386; + tables[26][52] = 262368; + tables[26][53] = 262236; + tables[27] = tables[21]; + tables[27][32] = 262307; + tables[27][46] = 262238; + tables[27][47] = 262377; + tables[27][49] = 262311; + tables[27][50] = 262311; + tables[27][51] = 262375; + tables[27][52] = 262320; + tables[27][53] = 262268; + tables[28] = tables[6]; + tables[28][8] = 336044; + tables[28][47] = 262235; + tables[28][48] = 262237; + tables[28][51] = 262208; + tables[28][52] = 262179; + tables[29][47] = 262267; + tables[29][48] = 262269; + tables[30] = tables[20]; + tables[30][45] = 262191; + tables[30][46] = 262320; + tables[30][47] = 262312; + tables[30][48] = 262186; + tables[30][49] = 262204; + tables[30][50] = 262204; + tables[30][51] = 262187; + tables[30][53] = 262208; + tables[30][100] = 262237; + tables[31] = tables[21]; + tables[31][32] = 262179; + tables[31][36] = 262239; + tables[31][39] = 262183; + tables[31][46] = 262270; + tables[31][48] = 262268; + tables[31][49] = 262206; + tables[31][50] = 262206; + tables[31][51] = 262321; + tables[31][52] = 262240; + tables[31][53] = 262311; + tables[31][56] = 262205; + tables[31][100] = 262235; + tables[31][136] = 262270; + tables[31][137] = 262270; + tables[31][138] = 262270; + tables[31][146] = 262270; + tables[31][147] = 262270; + tables[32][6] = 327842; + tables[32][8] = 336044; + tables[32][16] = 327861; + tables[32][21] = 327862; + tables[32][22] = 327903; + tables[32][27] = 327867; + tables[32][29] = 327851; + tables[32][30] = 262329; + tables[32][31] = 262322; + tables[32][32] = 262323; + tables[32][33] = 262332; + tables[32][34] = 262333; + tables[32][35] = 262334; + tables[32][36] = 262307; + tables[32][37] = 262267; + tables[32][38] = 262269; + tables[32][45] = 262236; + tables[32][46] = 262328; + tables[32][53] = 262316; + tables[32][55] = 262327; + tables[32][100] = 262310; + tables[32][136] = 262328; + tables[32][137] = 262328; + tables[32][138] = 262328; + tables[32][146] = 262328; + tables[32][147] = 262328; + tables[33] = tables[26]; + tables[33][46] = 262315; + tables[33][47] = 262187; + tables[33][48] = 262324; + tables[33][49] = 262270; + tables[33][50] = 262270; + tables[33][51] = 327911; + tables[33][52] = 262330; + tables[34] = tables[27]; + tables[34][32] = 262179; + tables[34][46] = 262331; + tables[34][47] = 262186; + tables[34][48] = 262240; + tables[34][49] = 262238; + tables[34][50] = 262238; + tables[34][51] = 327879; + tables[34][52] = 262314; + tables[35] = tables[6]; + tables[35][8] = 336044; + tables[35][31] = 262208; + tables[35][32] = 262307; + tables[35][33] = 262311; + tables[35][36] = 262267; + tables[35][37] = 262235; + tables[35][38] = 262237; + tables[35][39] = 262269; + tables[35][47] = 262312; + tables[35][48] = 262237; + tables[35][136] = 262312; + tables[35][137] = 262312; + tables[35][138] = 262312; + tables[35][146] = 262312; + tables[35][147] = 262312; + tables[36] = tables[4]; + tables[36][4] = 328772; + tables[36][5] = 328760; + tables[36][6] = 328769; + tables[36][7] = 328754; + tables[36][8] = 328771; + tables[36][9] = 328752; + tables[36][10] = 328767; + tables[36][11] = 328768; + tables[36][12] = 328776; + tables[36][13] = 328766; + tables[36][14] = 328763; + tables[36][15] = 328756; + tables[36][16] = 328780; + tables[36][17] = 328770; + tables[36][18] = 328777; + tables[36][19] = 328759; + tables[36][20] = 328761; + tables[36][21] = 328762; + tables[36][22] = 328779; + tables[36][23] = 328757; + tables[36][24] = 328755; + tables[36][25] = 328764; + tables[36][26] = 328774; + tables[36][27] = 328775; + tables[36][28] = 328765; + tables[36][29] = 328783; + tables[36][47] = 328773; + tables[36][48] = 328778; + tables[36][51] = 328758; + tables[36][52] = 328781; + tables[36][53] = 328785; + tables[36][54] = 328753; + tables[36][55] = 328782; + tables[36][56] = 262190; + tables[37] = tables[5]; + tables[37][4] = 328740; + tables[37][5] = 328728; + tables[37][6] = 328737; + tables[37][7] = 328722; + tables[37][8] = 328739; + tables[37][9] = 328720; + tables[37][10] = 328735; + tables[37][11] = 328736; + tables[37][12] = 328744; + tables[37][13] = 328734; + tables[37][14] = 328731; + tables[37][15] = 328724; + tables[37][16] = 328748; + tables[37][17] = 328738; + tables[37][18] = 328745; + tables[37][19] = 328727; + tables[37][20] = 328729; + tables[37][21] = 328730; + tables[37][22] = 328747; + tables[37][23] = 328725; + tables[37][24] = 328723; + tables[37][25] = 328732; + tables[37][26] = 328742; + tables[37][27] = 328743; + tables[37][28] = 328733; + tables[37][29] = 328751; + tables[37][31] = 262178; + tables[37][32] = 270614; + tables[37][33] = 262203; + tables[37][35] = 262202; + tables[37][36] = 262207; + tables[37][47] = 328741; + tables[37][48] = 328746; + tables[37][49] = 262191; + tables[37][50] = 262191; + tables[37][51] = 328726; + tables[37][52] = 328749; + tables[37][53] = 328705; + tables[37][54] = 328721; + tables[37][55] = 328750; + tables[37][56] = 262188; + tables[37][100] = 262191; + tables[38] = tables[4]; + tables[38][49] = 262179; + tables[38][50] = 262179; + tables[39] = tables[5]; + tables[39][31] = 262178; + tables[39][32] = 262307; + tables[39][49] = 262270; + tables[39][50] = 262270; + tables[39][52] = 262208; + tables[39][53] = 262316; + tables[40][4] = 327905; + tables[40][8] = 327913; + tables[40][12] = 327917; + tables[40][18] = 327923; + tables[40][24] = 327930; + tables[40][33] = 270508; + tables[40][53] = 262310; + tables[41][4] = 327873; + tables[41][8] = 327881; + tables[41][12] = 327885; + tables[41][18] = 327891; + tables[41][24] = 327898; + tables[42] = tables[4]; + tables[42][4] = 332038; + tables[42][5] = 332146; + tables[42][6] = 332046; + tables[42][7] = 332043; + tables[42][8] = 332035; + tables[42][9] = 332037; + tables[42][10] = 332050; + tables[42][11] = 332137; + tables[42][12] = 332131; + tables[42][13] = 332133; + tables[42][14] = 332129; + tables[42][15] = 332149; + tables[42][16] = 332147; + tables[42][17] = 332142; + tables[42][18] = 332130; + tables[42][19] = 332134; + tables[42][20] = 332039; + tables[42][21] = 332032; + tables[42][22] = 332034; + tables[42][23] = 332041; + tables[42][24] = 332135; + tables[42][25] = 332049; + tables[42][26] = 332044; + tables[42][27] = 332048; + tables[42][28] = 332141; + tables[42][29] = 332047; + tables[42][50] = 262237; + tables[42][52] = 270360; + tables[43] = tables[5]; + tables[43][4] = 332038; + tables[43][5] = 332146; + tables[43][6] = 332046; + tables[43][7] = 332043; + tables[43][8] = 332036; + tables[43][9] = 332037; + tables[43][10] = 332050; + tables[43][11] = 332137; + tables[43][12] = 332131; + tables[43][13] = 332133; + tables[43][14] = 332129; + tables[43][15] = 332149; + tables[43][16] = 332147; + tables[43][17] = 332142; + tables[43][18] = 332132; + tables[43][19] = 332136; + tables[43][20] = 332040; + tables[43][21] = 332033; + tables[43][22] = 332034; + tables[43][23] = 332042; + tables[43][24] = 332135; + tables[43][25] = 332049; + tables[43][26] = 332045; + tables[43][27] = 332048; + tables[43][28] = 332141; + tables[43][29] = 332047; + tables[43][50] = 262269; + tables[43][52] = 270364; + tables[44] = tables[20]; + tables[44][45] = 262187; + tables[44][46] = 262236; + tables[44][47] = 327909; + tables[44][48] = 262312; + tables[44][49] = 262183; + tables[44][50] = 262183; + tables[44][51] = 327928; + tables[44][52] = 327910; + tables[44][53] = 262268; + tables[45] = tables[34]; + tables[45][33] = 262308; + tables[45][46] = 262240; + tables[45][47] = 327877; + tables[45][48] = 262238; + tables[45][49] = 262186; + tables[45][50] = 262186; + tables[45][51] = 327896; + tables[45][52] = 327878; + tables[45][53] = 262311; + tables[45][136] = 262240; + tables[45][137] = 262240; + tables[45][138] = 262240; + tables[45][146] = 262240; + tables[45][147] = 262240; + tables[46] = tables[35]; + tables[46][16] = 327861; + tables[46][33] = 262180; + tables[46][46] = 262324; + tables[46][47] = 0; + tables[46][48] = 262270; + tables[46][136] = 262324; + tables[46][137] = 262324; + tables[46][138] = 262324; + tables[46][146] = 262324; + tables[46][147] = 262324; + tables[47] = tables[44]; + tables[47][46] = 262324; + tables[47][51] = 327926; + tables[47][52] = 327908; + tables[47][53] = 262311; + tables[47][136] = 262312; + tables[47][137] = 262312; + tables[47][138] = 262312; + tables[47][146] = 262312; + tables[47][147] = 262312; + tables[48] = tables[45]; + tables[48][51] = 327894; + tables[48][52] = 327876; + tables[48][53] = 262333; + tables[48][136] = 262238; + tables[48][137] = 262238; + tables[48][138] = 262238; + tables[48][146] = 262238; + tables[48][147] = 262238; + tables[49] = tables[46]; + tables[49][45] = 262236; + tables[49][46] = 0; + tables[49][100] = 262268; + tables[49][136] = 262270; + tables[49][137] = 262270; + tables[49][138] = 262270; + tables[49][146] = 262270; + tables[49][147] = 262270; + tables[50] = tables[47]; + tables[50][51] = 327910; + tables[50][52] = 327928; + tables[50][53] = 262333; + tables[50][136] = 262324; + tables[50][137] = 262324; + tables[50][138] = 262324; + tables[50][146] = 262324; + tables[50][147] = 262324; + tables[51] = tables[45]; + tables[51][51] = 327878; + tables[51][52] = 327896; + tables[52] = tables[46]; + tables[52][34] = 262272; + tables[52][46] = 262268; + tables[52][100] = 262236; + tables[52][136] = 0; + tables[52][137] = 0; + tables[52][138] = 0; + tables[52][146] = 0; + tables[52][147] = 0; + tables[53] = tables[38]; + tables[53][4] = 340231; + tables[53][5] = 340246; + tables[53][6] = 340239; + tables[53][7] = 340238; + tables[53][8] = 340237; + tables[53][9] = 340241; + tables[53][10] = 340245; + tables[53][11] = 340248; + tables[53][12] = 340251; + tables[53][13] = 340264; + tables[53][14] = 340252; + tables[53][15] = 340256; + tables[53][16] = 340265; + tables[53][17] = 340249; + tables[53][18] = 340255; + tables[53][19] = 340259; + tables[53][20] = 340230; + tables[53][21] = 340240; + tables[53][22] = 340235; + tables[53][23] = 340244; + tables[53][24] = 340263; + tables[53][25] = 340242; + tables[53][26] = 340234; + tables[53][27] = 340236; + tables[53][28] = 340247; + tables[53][29] = 340232; + tables[53][30] = 274693; + tables[53][31] = 274697; + tables[53][32] = 262855; + tables[53][33] = 262859; + tables[53][34] = 274707; + tables[53][35] = 262858; + tables[53][36] = 262873; + tables[53][37] = 274714; + tables[53][38] = 274718; + tables[53][39] = 274722; + tables[53][45] = 274726; + tables[53][51] = 274724; + tables[53][52] = 270360; + tables[53][54] = 274717; + tables[53][55] = 274721; + tables[53][56] = 274725; + tables[54] = tables[43]; + tables[54][4] = 327777; + tables[54][5] = 327778; + tables[54][6] = 327779; + tables[54][7] = 327780; + tables[54][8] = 327781; + tables[54][9] = 327782; + tables[54][10] = 327783; + tables[54][11] = 327784; + tables[54][12] = 327785; + tables[54][13] = 327786; + tables[54][14] = 327787; + tables[54][15] = 327788; + tables[54][16] = 327789; + tables[54][17] = 327790; + tables[54][18] = 327791; + tables[54][19] = 327792; + tables[54][20] = 327793; + tables[54][21] = 327794; + tables[54][22] = 327795; + tables[54][23] = 327796; + tables[54][24] = 327797; + tables[54][25] = 327798; + tables[54][26] = 327799; + tables[54][27] = 327800; + tables[54][28] = 327801; + tables[54][29] = 327802; + tables[54][50] = 262179; + tables[54][100] = 262236; + tables[55] = tables[5]; + tables[55][52] = 270364; + tables[56] = tables[7]; + tables[56][46] = 327453; + tables[56][47] = 327483; + tables[56][48] = 327485; + tables[56][49] = 327484; + tables[56][50] = 327484; + tables[56][52] = 270360; + tables[56][53] = 270360; + tables[56][100] = 327653; + tables[56][135] = 0; + tables[57] = tables[8]; + tables[57][31] = 327456; + tables[57][35] = 327486; + tables[57][36] = 327430; + tables[57][37] = 327434; + tables[57][38] = 327432; + tables[57][39] = 327433; + tables[57][45] = 327487; + tables[57][46] = 327435; + tables[57][47] = 327515; + tables[57][48] = 327517; + tables[57][49] = 327516; + tables[57][50] = 327516; + tables[57][51] = 327450; + tables[57][52] = 270364; + tables[57][53] = 327518; + tables[57][100] = 327516; + tables[57][135] = 0; + tables[57][137] = 0; + tables[58] = tables[42]; + tables[58][4] = 353765; + tables[58][5] = 354056; + tables[58][6] = 365009; + tables[58][7] = 354088; + tables[58][8] = 355380; + tables[58][9] = 356459; + tables[58][10] = 349983; + tables[58][11] = 359161; + tables[58][12] = 352776; + tables[58][13] = 348993; + tables[58][14] = 350503; + tables[58][15] = 347693; + tables[58][16] = 347648; + tables[58][17] = 352019; + tables[58][18] = 347834; + tables[58][19] = 352195; + tables[58][20] = 352843; + tables[58][21] = 349155; + tables[58][22] = 351288; + tables[58][23] = 351999; + tables[58][24] = 351345; + tables[58][25] = 350579; + tables[58][26] = 357680; + tables[58][27] = 366307; + tables[58][28] = 349020; + tables[58][29] = 365005; + tables[58][50] = 262179; + tables[59] = tables[1]; + tables[59][47] = 262376; + tables[59][51] = 262377; + tables[59][52] = 262368; + tables[60] = tables[2]; + tables[60][47] = 262396; + tables[60][51] = 262390; + tables[60][52] = 262372; + tables[61] = tables[4]; + tables[61][47] = 262238; + tables[61][48] = 262328; + tables[61][49] = 262204; + tables[61][50] = 262204; + tables[61][52] = 262240; + tables[61][53] = 262179; + tables[61][56] = 327913; + tables[61][100] = 262315; + tables[61][136] = 262240; + tables[61][137] = 262240; + tables[61][138] = 262240; + tables[61][146] = 262240; + tables[61][147] = 262240; + tables[62] = tables[39]; + tables[62][32] = 262191; + tables[62][35] = 262207; + tables[62][47] = 262238; + tables[62][48] = 262312; + tables[62][49] = 262206; + tables[62][50] = 262206; + tables[62][52] = 262240; + tables[62][53] = 262268; + tables[62][54] = 262183; + tables[62][55] = 262190; + tables[62][56] = 327881; + tables[62][100] = 262331; + tables[62][136] = 262240; + tables[62][137] = 262240; + tables[62][138] = 262240; + tables[62][146] = 262240; + tables[62][147] = 262240; + tables[63] = tables[52]; + tables[63][8] = 0; + tables[63][18] = 327847; + tables[63][19] = 327862; + tables[63][30] = 262321; + tables[63][33] = 262306; + tables[63][34] = 262308; + tables[63][35] = 262316; + tables[63][36] = 262310; + tables[63][37] = 262322; + tables[63][38] = 262323; + tables[63][39] = 262332; + tables[63][45] = 262333; + tables[63][46] = 262334; + tables[63][47] = 262235; + tables[63][48] = 262237; + tables[63][49] = 262269; + tables[63][50] = 262269; + tables[63][51] = 262270; + tables[63][52] = 262267; + tables[63][53] = 262236; + tables[63][54] = 262319; + tables[63][55] = 262317; + tables[63][56] = 262324; + tables[63][100] = 262320; + tables[64] = tables[23]; + tables[64][35] = 327847; + tables[64][37] = 327713; + tables[64][46] = 327725; + tables[64][49] = 327861; + tables[64][50] = 327861; + tables[64][56] = 327741; + tables[65] = tables[24]; + tables[65][46] = 327775; + tables[65][48] = 327722; + tables[65][49] = 327843; + tables[65][50] = 327843; + tables[65][53] = 262323; + tables[65][56] = 327723; + tables[66][8] = 336044; + tables[66][30] = 327804; + tables[66][31] = 327744; + tables[66][32] = 327715; + tables[66][33] = 327803; + tables[66][34] = 327771; + tables[66][35] = 327774; + tables[66][38] = 327803; + tables[66][39] = 327805; + tables[66][47] = 327771; + tables[66][48] = 327773; + tables[66][49] = 262240; + tables[66][50] = 262240; + tables[66][52] = 262324; + tables[66][56] = 262270; + tables[66][100] = 262236; + tables[67][49] = 262240; + tables[67][50] = 262240; + tables[67][52] = 262324; + tables[67][56] = 262270; + tables[67][136] = 262240; + tables[67][137] = 262240; + tables[67][138] = 262240; + tables[67][146] = 262240; + tables[67][147] = 262240; + tables[68] = tables[4]; + tables[68][49] = 262179; + tables[68][100] = 0; + tables[69] = tables[5]; + tables[69][49] = 262270; + tables[69][100] = 0; + tables[70][4] = 327941; + tables[70][6] = 327943; + tables[70][8] = 327961; + tables[70][15] = 328002; + tables[70][17] = 328004; + tables[70][18] = 327923; + tables[70][22] = 328027; + tables[70][24] = 270508; + tables[70][27] = 328058; + tables[70][29] = 328060; + tables[71][4] = 327940; + tables[71][6] = 327942; + tables[71][8] = 327960; + tables[71][15] = 328001; + tables[71][17] = 328003; + tables[71][18] = 327891; + tables[71][22] = 328026; + tables[71][27] = 328057; + tables[71][29] = 328059; + tables[72] = tables[4]; + tables[72][47] = 262324; + tables[72][48] = 262235; + tables[72][49] = 0; + tables[72][50] = 262237; + tables[72][51] = 327911; + tables[72][52] = 262270; + tables[72][53] = 262183; + tables[72][56] = 262203; + tables[72][135] = 262191; + tables[73] = tables[5]; + tables[73][35] = 262312; + tables[73][47] = 262240; + tables[73][48] = 262267; + tables[73][49] = 0; + tables[73][50] = 262269; + tables[73][51] = 327879; + tables[73][52] = 262238; + tables[73][53] = 262178; + tables[73][56] = 262202; + tables[73][135] = 262207; + tables[74][8] = 262320; + tables[74][20] = 262191; + tables[74][26] = 262207; + tables[74][30] = 262329; + tables[74][31] = 262322; + tables[74][32] = 262323; + tables[74][33] = 262307; + tables[74][34] = 262306; + tables[74][35] = 262316; + tables[74][46] = 262311; + tables[74][48] = 262314; + tables[74][50] = 262330; + tables[74][135] = 262320; + tables[75] = tables[20]; + tables[75][12] = 327985; + tables[75][45] = 262186; + tables[75][46] = 262189; + tables[75][47] = 327967; + tables[75][48] = 327932; + tables[75][49] = 0; + tables[75][50] = 262188; + tables[75][51] = 328031; + tables[75][52] = 327785; + tables[75][53] = 262178; + tables[75][54] = 327926; + tables[75][55] = 327911; + tables[75][56] = 262190; + tables[76] = tables[21]; + tables[76][31] = 262183; + tables[76][32] = 262238; + tables[76][33] = 262187; + tables[76][46] = 262239; + tables[76][47] = 327966; + tables[76][48] = 262364; + tables[76][49] = 0; + tables[76][50] = 262203; + tables[76][51] = 328030; + tables[76][52] = 327984; + tables[76][53] = 262377; + tables[76][54] = 327894; + tables[76][55] = 327879; + tables[76][56] = 262202; + tables[77] = tables[52]; + tables[77][4] = 327910; + tables[77][8] = 270508; + tables[77][12] = 327785; + tables[77][16] = 0; + tables[77][20] = 262208; + tables[77][22] = 262367; + tables[77][30] = 262206; + tables[77][31] = 262307; + tables[77][32] = 262179; + tables[77][34] = 262333; + tables[77][45] = 262236; + tables[77][47] = 262312; + tables[77][50] = 262240; + tables[77][51] = 262324; + tables[77][53] = 262204; + tables[77][100] = 262268; + tables[78][4] = 327878; + tables[78][12] = 327984; + tables[79] = tables[20]; + tables[79][41] = 262268; + tables[79][46] = 262335; + tables[79][47] = 262324; + tables[79][50] = 327805; + tables[79][52] = 262267; + tables[79][53] = 262268; + tables[80] = tables[21]; + tables[80][32] = 262179; + tables[80][46] = 262305; + tables[80][47] = 262312; + tables[80][50] = 327773; + tables[80][52] = 262235; + tables[80][53] = 262320; + tables[81][20] = 262208; + tables[81][45] = 262236; + tables[81][48] = 262270; + tables[81][50] = 262240; + tables[81][52] = 262238; + tables[81][53] = 262316; + tables[82] = tables[4]; + tables[82][4] = 263732; + tables[82][5] = 263748; + tables[82][6] = 263716; + tables[82][7] = 263754; + tables[82][8] = 263723; + tables[82][9] = 263720; + tables[82][10] = 263748; + tables[82][11] = 263719; + tables[82][12] = 263751; + tables[82][13] = 263722; + tables[82][14] = 263750; + tables[82][15] = 263749; + tables[82][16] = 263721; + tables[82][17] = 263753; + tables[82][18] = 263726; + tables[82][19] = 263725; + tables[82][20] = 263734; + tables[82][21] = 263746; + tables[82][22] = 263731; + tables[82][23] = 263745; + tables[82][24] = 263737; + tables[82][25] = 263729; + tables[82][26] = 263733; + tables[82][27] = 263713; + tables[82][28] = 263738; + tables[82][29] = 263718; + tables[82][47] = 263724; + tables[82][48] = 263727; + tables[82][51] = 263747; + tables[82][52] = 263735; + tables[82][53] = 263728; + tables[82][54] = 263752; + tables[82][55] = 263730; + tables[82][56] = 263736; + tables[83] = tables[5]; + tables[83][4] = 263760; + tables[83][5] = 263748; + tables[83][6] = 262269; + tables[83][7] = 262237; + tables[83][8] = 263759; + tables[83][9] = 262235; + tables[83][10] = 263748; + tables[83][11] = 263715; + tables[83][12] = 262391; + tables[83][13] = 263744; + tables[83][14] = 263692; + tables[83][15] = 262191; + tables[83][16] = 270361; + tables[83][17] = 263714; + tables[83][18] = 262359; + tables[83][19] = 263707; + tables[83][20] = 263758; + tables[83][21] = 263756; + tables[83][22] = 263757; + tables[83][23] = 263748; + tables[83][24] = 270360; + tables[83][25] = 262267; + tables[83][26] = 263755; + tables[83][27] = 263762; + tables[83][28] = 263717; + tables[83][29] = 262270; + tables[83][38] = 262185; + tables[83][39] = 262184; + tables[83][47] = 262204; + tables[83][48] = 262206; + tables[83][53] = 263761; + tables[83][54] = 262188; + tables[83][55] = 262190; + tables[83][56] = 263711; + tables[84] = tables[1]; + tables[84][4] = 331295; + tables[84][5] = 331316; + tables[84][6] = 331329; + tables[84][7] = 331265; + tables[84][8] = 331315; + tables[84][9] = 331284; + tables[84][10] = 331328; + tables[84][11] = 331337; + tables[84][12] = 331299; + tables[84][13] = 331336; + tables[84][14] = 331314; + tables[84][15] = 331306; + tables[84][16] = 331287; + tables[84][17] = 331319; + tables[84][18] = 331289; + tables[84][19] = 331298; + tables[84][20] = 331334; + tables[84][21] = 331294; + tables[84][22] = 331307; + tables[84][23] = 331312; + tables[84][24] = 331317; + tables[84][25] = 331309; + tables[84][26] = 331332; + tables[84][27] = 331291; + tables[84][28] = 331313; + tables[84][29] = 331292; + tables[84][30] = 331333; + tables[84][31] = 327727; + tables[84][32] = 327725; + tables[84][33] = 331296; + tables[84][34] = 331286; + tables[84][35] = 331320; + tables[84][36] = 331318; + tables[84][37] = 331268; + tables[84][38] = 331285; + tables[84][39] = 331272; + tables[84][45] = 331266; + tables[84][46] = 331274; + tables[84][47] = 331290; + tables[84][48] = 331301; + tables[84][49] = 331267; + tables[84][50] = 331267; + tables[84][51] = 331303; + tables[84][52] = 331271; + tables[84][53] = 327775; + tables[84][54] = 331297; + tables[84][55] = 331331; + tables[84][56] = 331293; + tables[84][100] = 331267; + tables[84][103] = 0; + tables[84][133] = 0; + tables[84][134] = 0; + tables[84][140] = 0; + tables[85] = tables[2]; + tables[85][4] = 331300; + tables[85][5] = 331322; + tables[85][6] = 331273; + tables[85][7] = 331279; + tables[85][8] = 331278; + tables[85][9] = 331330; + tables[85][10] = 331276; + tables[85][11] = 331335; + tables[85][12] = 331283; + tables[85][13] = 331339; + tables[85][14] = 331305; + tables[85][15] = 331304; + tables[85][16] = 327743; + tables[85][17] = 331340; + tables[85][18] = 331311; + tables[85][19] = 331277; + tables[85][20] = 331344; + tables[85][21] = 331281; + tables[85][22] = 331270; + tables[85][23] = 331288; + tables[85][24] = 331338; + tables[85][25] = 331310; + tables[85][26] = 327714; + tables[85][27] = 327721; + tables[85][28] = 331341; + tables[85][29] = 327720; + tables[85][30] = 327723; + tables[85][31] = 331345; + tables[85][32] = 331346; + tables[85][33] = 331347; + tables[85][34] = 331348; + tables[85][35] = 331321; + tables[85][36] = 331327; + tables[85][37] = 331349; + tables[85][38] = 331350; + tables[85][39] = 331351; + tables[85][45] = 331352; + tables[85][46] = 331353; + tables[85][47] = 331280; + tables[85][48] = 327724; + tables[85][49] = 331269; + tables[85][50] = 331269; + tables[85][51] = 331275; + tables[85][52] = 327726; + tables[85][53] = 327717; + tables[85][54] = 331282; + tables[85][55] = 331308; + tables[85][56] = 331302; + tables[85][100] = 331269; + tables[85][103] = 0; + tables[85][133] = 0; + tables[85][134] = 0; + tables[85][140] = 0; + tables[86] = tables[1]; + tables[86][30] = 262187; + tables[86][31] = 327963; + tables[86][32] = 328033; + tables[86][33] = 327949; + tables[86][34] = 328025; + tables[86][35] = 328062; + tables[86][36] = 327933; + tables[86][37] = 327905; + tables[86][38] = 327917; + tables[86][39] = 327913; + tables[86][45] = 262205; + tables[86][46] = 262324; + tables[86][47] = 327930; + tables[86][48] = 262185; + tables[86][49] = 262312; + tables[86][50] = 262312; + tables[86][51] = 328047; + tables[86][52] = 262311; + tables[86][53] = 262203; + tables[86][100] = 262236; + tables[86][103] = 0; + tables[86][133] = 0; + tables[86][134] = 0; + tables[86][140] = 0; + tables[87] = tables[2]; + tables[87][30] = 262193; + tables[87][31] = 262194; + tables[87][32] = 262195; + tables[87][33] = 262196; + tables[87][34] = 262197; + tables[87][35] = 262198; + tables[87][36] = 262199; + tables[87][37] = 262200; + tables[87][38] = 262201; + tables[87][39] = 262192; + tables[87][45] = 262181; + tables[87][46] = 262855; + tables[87][47] = 262191; + tables[87][48] = 262184; + tables[87][49] = 262183; + tables[87][50] = 262183; + tables[87][51] = 262178; + tables[87][52] = 262177; + tables[87][54] = 262207; + tables[87][100] = 262268; + tables[87][103] = 0; + tables[87][133] = 0; + tables[87][134] = 0; + tables[87][140] = 0; + tables[88][5] = 262267; + tables[88][6] = 262182; + tables[88][7] = 262416; + tables[88][8] = 270508; + tables[88][9] = 262235; + tables[88][10] = 262237; + tables[88][14] = 262466; + tables[88][15] = 262465; + tables[88][17] = 262269; + tables[88][20] = 262236; + tables[88][22] = 262417; + tables[88][25] = 262208; + tables[88][26] = 262268; + tables[88][27] = 262179; + tables[88][30] = 262270; + tables[88][31] = 262855; + tables[88][32] = 262238; + tables[88][33] = 262872; + tables[88][34] = 262320; + tables[88][35] = 262875; + tables[88][36] = 262240; + tables[88][37] = 262873; + tables[88][38] = 262324; + tables[88][39] = 262877; + tables[88][45] = 262312; + tables[88][46] = 262328; + tables[88][47] = 262391; + tables[88][48] = 262359; + tables[88][49] = 262308; + tables[88][50] = 262308; + tables[88][51] = 262180; + tables[88][52] = 262367; + tables[88][54] = 262204; + tables[88][55] = 262206; + tables[88][56] = 262186; + tables[89] = tables[4]; + tables[89][4] = 328625; + tables[89][5] = 328626; + tables[89][6] = 328648; + tables[89][7] = 328628; + tables[89][8] = 328629; + tables[89][9] = 328646; + tables[89][10] = 328627; + tables[89][11] = 328631; + tables[89][12] = 328633; + tables[89][13] = 328638; + tables[89][14] = 328634; + tables[89][15] = 328635; + tables[89][16] = 328636; + tables[89][17] = 328637; + tables[89][18] = 328639; + tables[89][19] = 328640; + tables[89][20] = 262203; + tables[89][21] = 328641; + tables[89][22] = 328643; + tables[89][23] = 328644; + tables[89][24] = 328632; + tables[89][25] = 328649; + tables[89][26] = 263106; + tables[89][27] = 328647; + tables[89][28] = 328645; + tables[89][29] = 328630; + tables[89][51] = 263044; + tables[89][100] = 262204; + tables[89][103] = 0; + tables[89][133] = 0; + tables[89][134] = 0; + tables[89][140] = 0; + tables[90] = tables[5]; + tables[90][4] = 328593; + tables[90][5] = 328594; + tables[90][6] = 328616; + tables[90][7] = 328596; + tables[90][8] = 328597; + tables[90][9] = 328614; + tables[90][10] = 328595; + tables[90][11] = 328599; + tables[90][12] = 328601; + tables[90][13] = 328606; + tables[90][14] = 328602; + tables[90][15] = 328603; + tables[90][16] = 328604; + tables[90][17] = 328605; + tables[90][18] = 328607; + tables[90][19] = 328608; + tables[90][20] = 262202; + tables[90][21] = 328609; + tables[90][22] = 328611; + tables[90][23] = 328612; + tables[90][24] = 328600; + tables[90][25] = 328617; + tables[90][26] = 263045; + tables[90][27] = 328615; + tables[90][28] = 328613; + tables[90][29] = 328598; + tables[90][51] = 262312; + tables[90][100] = 262206; + tables[90][103] = 0; + tables[90][133] = 0; + tables[90][134] = 0; + tables[90][140] = 0; + tables[91][6] = 262313; + tables[91][8] = 270508; + tables[91][21] = 262318; + tables[91][28] = 262309; + tables[91][31] = 262322; + tables[91][32] = 262323; + tables[91][33] = 262307; + tables[91][34] = 262311; + tables[91][35] = 262326; + tables[91][37] = 262308; + tables[91][38] = 262310; + tables[91][39] = 262320; + tables[91][45] = 262321; + tables[91][46] = 262333; + tables[91][47] = 262315; + tables[91][48] = 262331; + tables[91][50] = 262316; + tables[91][51] = 263045; + tables[92] = tables[4]; + tables[92][103] = 0; + tables[92][133] = 0; + tables[92][134] = 0; + tables[92][140] = 0; + tables[93] = tables[5]; + tables[93][103] = 0; + tables[93][133] = 0; + tables[93][134] = 0; + tables[93][140] = 0; + tables[94] = tables[92]; + tables[94][30] = 327939; + tables[94][31] = 327906; + tables[94][32] = 327914; + tables[94][33] = 327924; + tables[94][34] = 262912; + tables[94][35] = 262921; + tables[94][36] = 262915; + tables[94][37] = 262913; + tables[94][38] = 262947; + tables[94][39] = 327953; + tables[94][46] = 270507; + tables[94][47] = 328112; + tables[94][48] = 328097; + tables[95] = tables[93]; + tables[95][30] = 327938; + tables[95][31] = 327874; + tables[95][32] = 327882; + tables[95][33] = 327892; + tables[95][34] = 262912; + tables[95][35] = 262921; + tables[95][36] = 262915; + tables[95][37] = 262913; + tables[95][38] = 262947; + tables[95][39] = 327952; + tables[95][47] = 328111; + tables[95][48] = 328096; + tables[96] = tables[92]; + tables[96][40] = 0; + tables[96][42] = 0; + tables[96][43] = 0; + tables[96][44] = 0; + tables[96][83] = 0; + tables[96][84] = 0; + tables[96][85] = 0; + tables[96][86] = 0; + tables[96][87] = 0; + tables[96][88] = 0; + tables[96][89] = 0; + tables[96][90] = 0; + tables[96][91] = 0; + tables[96][92] = 0; + tables[96][93] = 0; + tables[96][94] = 0; + tables[96][95] = 0; + tables[96][96] = 0; + tables[96][97] = 0; + tables[96][98] = 0; + tables[96][99] = 0; + tables[97] = tables[93]; + tables[97][40] = 0; + tables[97][42] = 0; + tables[97][43] = 0; + tables[97][44] = 0; + tables[97][83] = 0; + tables[97][84] = 0; + tables[97][85] = 0; + tables[97][86] = 0; + tables[97][87] = 0; + tables[97][88] = 0; + tables[97][89] = 0; + tables[97][90] = 0; + tables[97][91] = 0; + tables[97][92] = 0; + tables[97][93] = 0; + tables[97][94] = 0; + tables[97][95] = 0; + tables[97][96] = 0; + tables[97][97] = 0; + tables[97][98] = 0; + tables[97][99] = 0; + tables[98] = tables[92]; + tables[98][47] = 327939; + tables[98][48] = 327918; + tables[98][49] = 327906; + tables[98][50] = 327906; + tables[98][51] = 328217; + tables[98][52] = 328219; + tables[98][53] = 270366; + tables[99] = tables[93]; + tables[99][47] = 327938; + tables[99][48] = 327886; + tables[99][49] = 327874; + tables[99][50] = 327874; + tables[99][51] = 328216; + tables[99][52] = 328218; + tables[99][53] = 270365; + tables[99][54] = 262203; + tables[99][55] = 262202; + tables[100] = tables[88]; + tables[100][5] = 0; + tables[100][6] = 262313; + tables[100][7] = 327953; + tables[100][9] = 0; + tables[100][10] = 0; + tables[100][14] = 0; + tables[100][15] = 328002; + tables[100][17] = 0; + tables[100][19] = 262311; + tables[100][20] = 0; + tables[100][22] = 262367; + tables[100][25] = 0; + tables[100][26] = 0; + tables[100][27] = 0; + tables[100][47] = 262235; + tables[100][48] = 262237; + tables[100][49] = 262236; + tables[100][50] = 262236; + tables[100][51] = 262203; + tables[100][52] = 262183; + tables[100][53] = 262240; + tables[100][56] = 0; + tables[101] = tables[29]; + tables[101][7] = 327952; + tables[101][15] = 328001; + tables[101][45] = 270355; + tables[101][46] = 262321; + tables[101][49] = 262268; + tables[101][50] = 262268; + tables[101][51] = 262202; + tables[101][52] = 262178; + tables[101][53] = 262270; + tables[101][54] = 262315; + tables[101][55] = 262331; + tables[102] = tables[92]; + tables[102][28] = 327802; + tables[102][29] = 327801; + tables[102][39] = 327926; + tables[102][45] = 327932; + tables[102][46] = 327923; + tables[102][47] = 328017; + tables[102][48] = 327930; + tables[102][49] = 328049; + tables[102][50] = 328049; + tables[102][51] = 327913; + tables[102][52] = 327905; + tables[102][53] = 262192; + tables[102][56] = 262189; + tables[102][100] = 327917; + tables[103] = tables[87]; + tables[103][30] = 262183; + tables[103][31] = 262178; + tables[103][32] = 262187; + tables[103][33] = 262177; + tables[103][34] = 262181; + tables[103][35] = 262191; + tables[103][36] = 262205; + tables[103][37] = 262184; + tables[103][38] = 262185; + tables[103][39] = 327894; + tables[103][45] = 327900; + tables[103][46] = 327891; + tables[103][47] = 328016; + tables[103][48] = 327898; + tables[103][49] = 328048; + tables[103][50] = 328048; + tables[103][51] = 327881; + tables[103][52] = 327873; + tables[103][53] = 262311; + tables[103][100] = 327885; + tables[104] = tables[88]; + tables[104][4] = 262372; + tables[104][8] = 262340; + tables[104][12] = 262349; + tables[104][13] = 262381; + tables[104][16] = 262204; + tables[104][24] = 270508; + tables[104][29] = 262206; + tables[104][54] = 262203; + tables[104][100] = 262204; + tables[105] = tables[36]; + tables[105][22] = 328790; + tables[105][48] = 328791; + tables[105][52] = 328788; + tables[105][53] = 327719; + tables[105][83] = 0; + tables[105][100] = 263313; + tables[106] = tables[37]; + tables[106][22] = 328710; + tables[106][48] = 328711; + tables[106][52] = 328708; + tables[106][53] = 0; + tables[106][100] = 263312; + tables[107][24] = 263313; + tables[108][24] = 263312; + tables[109][40] = 262154; + tables[109][42] = 262152; + tables[109][43] = 262153; + tables[109][88] = 262154; + tables[109][186] = 262152; + tables[109][187] = 262153; + tables[110][31] = 327962; + tables[110][32] = 328032; + tables[110][33] = 327948; + tables[110][34] = 328024; + tables[110][35] = 328061; + tables[110][36] = 327901; + tables[110][37] = 327873; + tables[110][38] = 327885; + tables[110][39] = 327881; + tables[110][47] = 327898; + tables[110][51] = 328046; + return tables; +}(); -char16_t UsCharacter(std::uint16_t keycode) { - static constexpr char16_t kPunctuation[] = u"-=[]\\\0;'`,./"; - static constexpr char16_t kKeypad[] = u"/*-+\0" u"1234567890."; - if (keycode >= 0x04 && keycode <= 0x1D) return static_cast(u'a' + (keycode - 0x04)); - if (keycode >= 0x1E && keycode <= 0x26) return static_cast(u'1' + (keycode - 0x1E)); - if (keycode == 0x27) return u'0'; - if (keycode == 0x2C) return u' '; - if (keycode >= 0x2D && keycode <= 0x38) return kPunctuation[keycode - 0x2D]; - if (keycode == 0x64) return u'\\'; - if (keycode >= 0x54 && keycode <= 0x63) return kKeypad[keycode - 0x54]; - return 0; +constexpr std::array, 41> CharacterLayouts = {{ + {1, 2, 3, 0}, + {4, 5, 6, 0}, + {7, 8, 0, 0}, + {9, 10, 0, 0}, + {11, 12, 0, 0}, + {13, 14, 0, 0}, + {15, 16, 0, 0}, + {17, 18, 19, 0}, + {20, 21, 22, 0}, + {23, 24, 25, 0}, + {26, 27, 28, 29}, + {30, 31, 32, 0}, + {33, 34, 35, 0}, + {36, 37, 0, 0}, + {38, 39, 40, 41}, + {42, 43, 0, 0}, + {44, 45, 46, 0}, + {47, 48, 49, 0}, + {50, 51, 52, 0}, + {47, 48, 49, 0}, + {53, 54, 0, 55}, + {56, 57, 0, 0}, + {58, 54, 0, 55}, + {59, 60, 3, 0}, + {61, 62, 63, 0}, + {64, 65, 66, 67}, + {68, 69, 70, 71}, + {72, 73, 74, 0}, + {75, 76, 77, 78}, + {79, 80, 81, 0}, + {82, 83, 0, 0}, + {84, 85, 0, 0}, + {86, 87, 88, 0}, + {89, 90, 91, 0}, + {92, 93, 0, 0}, + {94, 95, 96, 97}, + {98, 99, 100, 101}, + {102, 103, 104, 0}, + {105, 106, 107, 108}, + {109, 109, 109, 109}, + {110, 110, 110, 110}, +}}; + +constexpr std::array KoreanInitial = { + 12593, 12594, 12596, 12599, 12600, 12601, 12609, 12610, 12611, 12613, 12614, 12615, 12616, 12617, 12618, 12619, 12620, 12621, 12622 +}; + +constexpr std::array KoreanFinal = { + 12593, 12594, 12595, 12596, 12597, 12598, 12599, 12601, 12602, 12603, 12604, 12605, 12606, 12607, 12608, 12609, 12610, 12612, 12613, 12614, 12615, 12616, 12618, 12619, 12620, 12621, 12622 +}; + + +std::size_t CharacterLayout(std::uint32_t type, std::uint32_t state) { + constexpr std::array layouts = {18, 7, 0, 1, 14, 8, 29, 17, 9, 25, 24, 23, 10, 11, 16, 26, 27, 12, 1, 19, 28, 3, 3, 1, 1, 1, 1, 1, 1, 1, 14, 32, 14, 34, 35, 36, 37, 1, 1}; + const bool imeMode = (state & 0x4000000u) != 0; + const bool alternateMode = (state & 0x10000000u) != 0; + switch (type) { + case 19: return imeMode ? 13 : 1; + case 22: return alternateMode ? 2 : 3; + case 23: + if (!imeMode) return alternateMode ? 2 : 3; + if ((state & 0x8000000u) == 0) return 6; + return alternateMode ? 4 : 5; + case 24: return imeMode ? 15 : 1; + case 26: + if (imeMode) return 20; + return alternateMode ? 21 : 1; + case 29: + case 39: + if (imeMode) return 22; + return alternateMode ? 21 : 1; + case 25: + case 27: + case 28: return alternateMode ? 21 : 1; + case 30: return imeMode ? 30 : 1; + case 31: return imeMode ? 31 : 14; + case 33: return imeMode ? 33 : 14; + case 38: return imeMode ? 38 : 1; + default: return layouts[type - 1]; + } +} + +std::uint32_t CharacterEntry(std::size_t layout, std::size_t layer, std::uint16_t keycode) { + if (keycode >= 256) return 0; + return CharacterTables[CharacterLayouts[layout][layer]][keycode]; +} + +char16_t ConvertCharacter(std::uint16_t keycode, std::uint32_t state, std::uint32_t type) { + if (keycode == 83 || (keycode & 0xFFF8u) == 224) return 0; + const auto layout = CharacterLayout(type, state); + const bool shifted = (state & 0x2200u) != 0; + const bool caps = (state & 0x20000u) != 0; + if (layout == 0 && caps) { + if (keycode == 47) return shifted ? 200 : 220; + if (keycode == 52) return shifted ? 192 : 196; + if (keycode == 51) return shifted ? 201 : 214; + } + const auto& layers = CharacterLayouts[layout]; + if ((state & 0x4000u) != 0 && (layers[2] != 0 || layers[3] != 0)) { + auto entry = CharacterEntry(layout, shifted ? 3 : 2, keycode); + if (entry == 0) return static_cast(CharacterEntry(39, 0, keycode)); + if (caps && (entry & 0x10000u) != 0) { + const auto opposite = CharacterEntry(layout, shifted ? 2 : 3, keycode); + if (opposite != 0) entry = opposite; + } + return static_cast(entry); + } + auto entry = CharacterEntry(layout, shifted ? 1 : 0, keycode); + if (entry == 0) return 0; + if (caps) { + if ((type == 26 || type == 29 || type == 39) && (state & 0x4000000u) != 0) { + entry = CharacterEntry(layout, shifted ? 3 : 0, keycode); + } else if (type == 32 && !shifted && ((keycode >= 31 && keycode <= 39) || keycode == 47 || keycode == 51)) { + entry = CharacterEntry(40, 0, keycode); + } else if ((entry & 0x10000u) != 0) { + entry = CharacterEntry(layout, shifted ? 0 : 1, keycode); + } + if (entry == 0) return 0; + } + if ((entry & 0x20000u) != 0) { + const bool numLock = (state & 0x10000u) != 0; + if (!numLock && keycode >= 89 && keycode <= 99) return 0; + entry = CharacterEntry(layout, numLock ? 1 : 0, keycode); + if (entry == 0) return 0; + } + auto character = static_cast(entry); + if ((state & 0x4000000u) != 0 && type == 24) { + if ((character >= 12592 && character < 12688) || (character >= 44032 && character < 55216) || (character & 0xFF00u) == 0x1100) { + if (character >= 4352 && character <= 4370) return KoreanInitial[character - 4352]; + if (character >= 4449 && character <= 4469) return static_cast(character + 8174); + if (character >= 4520 && character <= 4546) return KoreanFinal[character - 4520]; + return 0; + } + } + if ((state & 0x4000000u) != 0 && type == 30) { + if ((state & 0x80u) == 0) { + if (keycode == 5) character = shifted ? 1570 : 1575; + if (shifted && keycode == 10) character = 1571; + if (shifted && keycode == 23) character = 1573; + } + const auto modifiers = state & 0xFFF8FFFFu; + if (modifiers == 67117312 || modifiers == 67121152 || modifiers == 67109632 || modifiers == 67113472) { + constexpr std::array formatting = {0x200D, 0x200C, 0x200E, 0x200F}; + return keycode >= 30 && keycode <= 33 ? formatting[keycode - 30] : 0; + } + if ((state & 0x1100u) != 0 && shifted) return 0; + } + return character; } } // namespace @@ -241,8 +2400,7 @@ extern "C" { int APS5_VABI sceConvertKeycodeGetImeKeyboardType(int32_t userId, uint32_t* type) { if (type == nullptr) return kErrInvalidAddress; if (userId < 0) return kErrInvalidUserId; - *type = 0; - return 0; + throw std::runtime_error("sceConvertKeycodeGetImeKeyboardType: user keyboard language and variant are unavailable"); } int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint16_t keycode, std::uint32_t keyboardType, std::uint16_t* virtualKeycode) { if (virtualKeycode == nullptr) return kErrInvalidAddress; @@ -261,9 +2419,9 @@ int APS5_VABI sceConvertKeycodeGetCharacterFromKeyboardData(const KeyboardData* int APS5_VABI sceConvertKeycodeGetCharacter(std::uint16_t keycode, std::uint32_t state, std::uint32_t type, char16_t* character) { if (character == nullptr) return kErrInvalidAddress; - if ((state & ~kIgnoredStateBits) != 0) throw std::runtime_error("sceConvertKeycodeGetCharacter: unsupported state " + std::to_string(state)); - if (type != 0) throw std::runtime_error("sceConvertKeycodeGetCharacter: unsupported keyboard type " + std::to_string(type)); - *character = UsCharacter(keycode); + if (type == 0 || type > 39) return kErrInvalidKeyboardType; + if (type >= 31) throw std::runtime_error("sceConvertKeycodeGetCharacter: guest sdk version is unavailable"); + *character = ConvertCharacter(keycode, state, type); return 0; } diff --git a/core/libs/tests/GuestConvertKeycode.cpp b/core/libs/tests/GuestConvertKeycode.cpp index 1f2ec31789..cce08ee498 100644 --- a/core/libs/tests/GuestConvertKeycode.cpp +++ b/core/libs/tests/GuestConvertKeycode.cpp @@ -45,38 +45,48 @@ int main() { Require(sceConvertKeycodeGetImeKeyboardType(-1, &type) == invalidUserId); Require(type == 0xFFFFFFFFu); - Require(sceConvertKeycodeGetImeKeyboardType(0, &type) == 0); - Require(type == 0); + bool rejected = false; + try { + sceConvertKeycodeGetImeKeyboardType(0, &type); + } catch (const std::runtime_error&) { + rejected = true; + } + Require(rejected && type == 0xFFFFFFFFu); - type = 0xFFFFFFFFu; - Require(sceConvertKeycodeGetImeKeyboardType(1, &type) == 0); - Require(type == 0); - - Require(sceConvertKeycodeGetCharacter(0x04, 0, 0, nullptr) == invalidAddress); + Require(sceConvertKeycodeGetCharacter(4, 0, 0, nullptr) == invalidAddress); + char16_t character = u'?'; + Require(sceConvertKeycodeGetCharacter(4, 0, 0, &character) == static_cast(0x80BC001Du)); + Require(character == u'?'); + Require(sceConvertKeycodeGetCharacter(4, 0, 40, &character) == static_cast(0x80BC001Du)); for (std::uint32_t state : {0u, 0x4000000u}) { - char16_t character[3] = {u'?', 0, 0}; - Require(sceConvertKeycodeGetCharacter(0x04, state, 0, character) == 0); - Require(character[0] == u'a'); - Require(sceConvertKeycodeGetCharacter(0x1E, state, 0, character) == 0); - Require(character[0] == u'1'); - Require(sceConvertKeycodeGetCharacter(0x3A, state, 0, character) == 0); - Require(character[0] == 0); - Require(sceConvertKeycodeGetCharacter(0x64, state, 0, character) == 0); - Require(character[0] == u'\\'); - Require(sceConvertKeycodeGetCharacter(0x32, state, 0, character) == 0); - Require(character[0] == 0); - Require(sceConvertKeycodeGetCharacter(0x27, state, 0, character) == 0); - Require(character[0] == u'0'); - Require(sceConvertKeycodeGetCharacter(0x2C, state, 0, character) == 0); - Require(character[0] == u' '); - Require(sceConvertKeycodeGetCharacter(0x2D, state, 0, character) == 0); - Require(character[0] == u'-'); - Require(sceConvertKeycodeGetCharacter(0x54, state, 0, character) == 0); - Require(character[0] == u'/'); - Require(sceConvertKeycodeGetCharacter(0x59, state, 0, character) == 0); - Require(character[0] == u'1'); - Require(sceConvertKeycodeGetCharacter(0x63, state, 0, character) == 0); - Require(character[0] == u'.'); + Require(sceConvertKeycodeGetCharacter(4, state, 4, &character) == 0 && character == u'a'); + Require(sceConvertKeycodeGetCharacter(30, state, 4, &character) == 0 && character == u'1'); + Require(sceConvertKeycodeGetCharacter(58, state, 4, &character) == 0 && character == 0); + Require(sceConvertKeycodeGetCharacter(65535, state, 4, &character) == 0 && character == 0); + } + Require(sceConvertKeycodeGetCharacter(4, 0x200, 4, &character) == 0 && character == u'A'); + Require(sceConvertKeycodeGetCharacter(4, 0x20000, 4, &character) == 0 && character == u'A'); + Require(sceConvertKeycodeGetCharacter(4, 0x20200, 4, &character) == 0 && character == u'a'); + Require(sceConvertKeycodeGetCharacter(30, 0x200, 4, &character) == 0 && character == u'!'); + Require(sceConvertKeycodeGetCharacter(89, 0, 4, &character) == 0 && character == 0); + Require(sceConvertKeycodeGetCharacter(89, 0x10000, 4, &character) == 0 && character == u'1'); + Require(sceConvertKeycodeGetCharacter(89, 0x10200, 4, &character) == 0 && character == u'1'); + Require(sceConvertKeycodeGetCharacter(4, 0, 9, &character) == 0 && character == u'q'); + Require(sceConvertKeycodeGetCharacter(28, 0, 2, &character) == 0 && character == u'z'); + Require(sceConvertKeycodeGetCharacter(20, 0x4000, 2, &character) == 0 && character == u'@'); + Require(sceConvertKeycodeGetCharacter(47, 0x20000, 3, &character) == 0 && character == 220); + Require(sceConvertKeycodeGetCharacter(47, 0x20200, 3, &character) == 0 && character == 200); + Require(sceConvertKeycodeGetCharacter(4, 0x4000000, 24, &character) == 0 && character == 0x3141); + Require(sceConvertKeycodeGetCharacter(5, 0x4000000, 30, &character) == 0 && character == 1575); + Require(sceConvertKeycodeGetCharacter(5, 0x4000200, 30, &character) == 0 && character == 1570); + Require(sceConvertKeycodeGetCharacter(30, 0x4000300, 30, &character) == 0 && character == 0x200D); + Require(sceConvertKeycodeGetCharacter(31, 0x4000300, 30, &character) == 0 && character == 0x200C); + Require(sceConvertKeycodeGetCharacter(32, 0x4000300, 30, &character) == 0 && character == 0x200E); + Require(sceConvertKeycodeGetCharacter(33, 0x4000300, 30, &character) == 0 && character == 0x200F); + for (std::uint32_t layout = 1; layout <= 30; ++layout) { + for (std::uint16_t key : {83, 224, 225, 226, 227, 228, 229, 230, 231}) { + Require(sceConvertKeycodeGetCharacter(key, 0xFFFFFFFFu, layout, &character) == 0 && character == 0); + } } return 0; } diff --git a/docs/dev/debt/libSceConvertKeycode.md b/docs/dev/debt/libSceConvertKeycode.md index fbf5f9d6cd..d220da5136 100644 --- a/docs/dev/debt/libSceConvertKeycode.md +++ b/docs/dev/debt/libSceConvertKeycode.md @@ -1,11 +1,6 @@ # libSceConvertKeycode technical debt -### Unknown function info - -- [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) - the signature `(uint16_t keycode, uint32_t, uint32_t keyboardType, char16_t* character)` is inferred from PPSA12361, which passes a 16-bit keycode, 0, the type from `sceConvertKeycodeGetImeKeyboardType` and a pointer it reads one UTF-16 unit from when the result is 0. The meaning of the second argument is unknown. PPSA02530 (#5726) and PPSA27836 pass `0x4000000` as a constant: PPSA27836 calls it for every keycode from 0 to 0x8F to build a table of key names, upper-cases `a` to `z` itself and skips keycodes that fail or return 0, so that bit is ignored and the unshifted character is returned as for 0. Any other bit throws, and so does a keyboard type other than 0. The returned character for HID usage `0x64` has not been measured on a console; `\` is an unverified US-layout guess based on the Windows VK mapping, which does not establish the console result. - ### Functional -- [sceConvertKeycodeGetCharacter](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) (libSceConvertKeycode) takes the keycode as a USB HID keyboard usage and returns the unshifted character of the US layout for letters, digits, punctuation, space and the keypad. A keycode without a character returns 0 with the character 0; whether the console returns an error instead is not measured. - -- `sceConvertKeycodeGetVirtualKeycode`: HID-to-virtual-key mappings and the three-argument signature are confirmed. Layouts 31–39 and version-dependent Y/Z mappings for layouts 2, 3 and 12 throw until the guest platform and compiled SDK version are exposed; no version is guessed. +- `sceConvertKeycodeGetVirtualKeycode` and `sceConvertKeycodeGetCharacter`: layouts 31–39 throw until the guest platform and compiled SDK version are exposed. `GetVirtualKeycode` also throws for version-dependent Y/Z mappings in layouts 2, 3 and 12. Mapping tables and modifier behavior are recovered; no SDK version is guessed. +- `sceConvertKeycodeGetImeKeyboardType`: user keyboard language and Japanese/Chinese keyboard variants are not exposed by the current UserService implementation. Valid-user queries throw instead of returning a fabricated keyboard type. The confirmed language/variant mapping and SDK compatibility rules still need integration with those settings. From 286efdc9123fd93ddafadce5e5d1eae51fb8cea8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:14:30 +0300 Subject: [PATCH 249/523] fix(discid): restore output pointer arguments --- core/libs/prx/libSceDiscId/Export.cpp | 7 +++++-- docs/dev/debt/libSceDiscId.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceDiscId/Export.cpp b/core/libs/prx/libSceDiscId/Export.cpp index 055e970b29..ef0373a67b 100644 --- a/core/libs/prx/libSceDiscId/Export.cpp +++ b/core/libs/prx/libSceDiscId/Export.cpp @@ -3,14 +3,17 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct DiscId; +struct DiscId2; + extern "C" { -int APS5_VABI sceDiscIdGet() { +int APS5_VABI sceDiscIdGet(DiscId* discId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceDiscIdGet2() { +int APS5_VABI sceDiscIdGet2(DiscId2* discId, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceDiscId.md b/docs/dev/debt/libSceDiscId.md index fb26a7a344..92a0e6f465 100644 --- a/docs/dev/debt/libSceDiscId.md +++ b/docs/dev/debt/libSceDiscId.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceDiscIdGet and sceDiscIdGet2](../../../core/libs/prx/libSceDiscId/Export.cpp) (libSceDiscId) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceDiscId.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- `sceDiscIdGet` takes one output pointer; `sceDiscIdGet2` takes an output pointer and a 32-bit argument that must be zero. Both pass the pointer to a device ioctl without accessing its contents. Output structure sizes and fields require the device-side implementation or a caller and remain unresolved. From 5536d6175666cd74346ed1d657cc97aeabbf8b59 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:14:33 +0300 Subject: [PATCH 250/523] fix(contentdelete): restore lifecycle errors and list arguments --- core/libs/prx/libSceContentDelete/Export.cpp | 21 ++++++++++--------- core/libs/tests/GuestContentDelete.cpp | 22 ++++++++++++-------- docs/dev/debt/libSceContentDelete.md | 5 +++-- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceContentDelete/Export.cpp b/core/libs/prx/libSceContentDelete/Export.cpp index 3a9fe2a4c8..5f886b22a4 100644 --- a/core/libs/prx/libSceContentDelete/Export.cpp +++ b/core/libs/prx/libSceContentDelete/Export.cpp @@ -9,32 +9,33 @@ namespace { std::atomic g_initialized{false}; -void RequireInitialized(const char* function) { - if (!g_initialized.load()) throw std::logic_error(std::string(function) + ": not initialized"); -} +constexpr int InvalidArgument = static_cast(0x809D5001u); +constexpr int NotInitialized = static_cast(0x809D5002u); +constexpr int AlreadyInitialized = static_cast(0x809D5003u); } extern "C" { -int APS5_VABI sceContentDeleteInitialize(const ContentDeleteInitParam* init_param) { - if (init_param == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceContentDeleteInitialize(const ContentDeleteInitParam* initParam) { + if (initParam == nullptr || initParam->heap_size != 0x4000) return InvalidArgument; bool expected = false; - if (!g_initialized.compare_exchange_strong(expected, true)) throw std::logic_error(std::string(__func__) + ": already initialized"); + if (!g_initialized.compare_exchange_strong(expected, true)) return AlreadyInitialized; return 0; } int APS5_VABI sceContentDeleteByPath(const char* contentPath) { + if (!g_initialized.load()) return NotInitialized; if (contentPath == nullptr) APS5_INVALID_ARG_EX; - RequireInitialized(__func__); + if (*contentPath == 0) return InvalidArgument; throw std::logic_error(std::string(__func__) + ": " + contentPath + " is not in the host content library, which is empty, and the console's error for it is unknown"); } int APS5_VABI sceContentDeleteById(std::int64_t contentId) { - RequireInitialized(__func__); + if (!g_initialized.load()) return NotInitialized; throw std::logic_error(std::string(__func__) + ": content id " + std::to_string(contentId) + " is not in the host content library, which is empty, and the console's error for it is unknown"); } -int APS5_VABI sceContentDeleteByIdListForSf(void) { +int APS5_VABI sceContentDeleteByIdListForSf(const std::int64_t* contentIds, std::size_t count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -56,7 +57,7 @@ int APS5_VABI sceContentDeleteMemoryTerminate(void) { int APS5_VABI sceContentDeleteTerminate(void) { bool expected = true; - if (!g_initialized.compare_exchange_strong(expected, false)) throw std::logic_error(std::string(__func__) + ": not initialized"); + if (!g_initialized.compare_exchange_strong(expected, false)) return NotInitialized; return 0; } diff --git a/core/libs/tests/GuestContentDelete.cpp b/core/libs/tests/GuestContentDelete.cpp index 9353f3a578..06cebf5b38 100644 --- a/core/libs/tests/GuestContentDelete.cpp +++ b/core/libs/tests/GuestContentDelete.cpp @@ -4,7 +4,7 @@ #include #include -extern "C" int APS5_VABI sceContentDeleteInitialize(const ContentDeleteInitParam* init_param); +extern "C" int APS5_VABI sceContentDeleteInitialize(const ContentDeleteInitParam* initParam); extern "C" int APS5_VABI sceContentDeleteTerminate(void); extern "C" int APS5_VABI sceContentDeleteById(std::int64_t contentId); extern "C" int APS5_VABI sceContentDeleteByPath(const char* contentPath); @@ -30,16 +30,20 @@ bool Contains(const std::string& text, const std::string& part) { return text.fi } int main() { - Require(LogicErrorMessage([] { sceContentDeleteTerminate(); }) == "sceContentDeleteTerminate: not initialized"); - Require(LogicErrorMessage([] { sceContentDeleteInitialize(nullptr); }) == "invalid argument"); - Require(LogicErrorMessage([] { sceContentDeleteById(1); }) == "sceContentDeleteById: not initialized"); - Require(LogicErrorMessage([] { sceContentDeleteByPath("/user/av_contents/photo/a.png"); }) == "sceContentDeleteByPath: not initialized"); - Require(LogicErrorMessage([] { sceContentDeleteByPath(nullptr); }) == "invalid argument"); + Require(sceContentDeleteTerminate() == static_cast(0x809D5002u)); + Require(sceContentDeleteInitialize(nullptr) == static_cast(0x809D5001u)); + Require(sceContentDeleteById(1) == static_cast(0x809D5002u)); + Require(sceContentDeleteByPath("/user/av_contents/photo/a.png") == static_cast(0x809D5002u)); + Require(sceContentDeleteByPath(nullptr) == static_cast(0x809D5002u)); ContentDeleteInitParam param{}; param.heap_size = 0x10000; + Require(sceContentDeleteInitialize(¶m) == static_cast(0x809D5001u)); + param.heap_size = 0x4000; Require(sceContentDeleteInitialize(¶m) == 0); - Require(LogicErrorMessage([&] { sceContentDeleteInitialize(¶m); }) == "sceContentDeleteInitialize: already initialized"); + Require(sceContentDeleteInitialize(¶m) == static_cast(0x809D5003u)); + Require(sceContentDeleteInitialize(nullptr) == static_cast(0x809D5001u)); + Require(sceContentDeleteByPath("") == static_cast(0x809D5001u)); Require(LogicErrorMessage([] { sceContentDeleteByPath(nullptr); }) == "invalid argument"); Require(Contains(LogicErrorMessage([] { sceContentDeleteByPath("/user/av_contents/photo/a.png"); }), "sceContentDeleteByPath: /user/av_contents/photo/a.png is not in the host content library")); @@ -47,7 +51,7 @@ int main() { Require(Contains(LogicErrorMessage([] { sceContentDeleteById(-1); }), "sceContentDeleteById: content id -1 is not in the host content library")); Require(sceContentDeleteTerminate() == 0); - Require(LogicErrorMessage([] { sceContentDeleteTerminate(); }) == "sceContentDeleteTerminate: not initialized"); - Require(LogicErrorMessage([] { sceContentDeleteById(1); }) == "sceContentDeleteById: not initialized"); + Require(sceContentDeleteTerminate() == static_cast(0x809D5002u)); + Require(sceContentDeleteById(1) == static_cast(0x809D5002u)); return 0; } diff --git a/docs/dev/debt/libSceContentDelete.md b/docs/dev/debt/libSceContentDelete.md index a0dacf89d0..da7501c081 100644 --- a/docs/dev/debt/libSceContentDelete.md +++ b/docs/dev/debt/libSceContentDelete.md @@ -2,8 +2,9 @@ ### Functional -- [sceContentDeleteById and sceContentDeleteByPath](../../../core/libs/prx/libSceContentDelete/Export.cpp) (libSceContentDelete) delete from a content library that is empty on the host, as nothing is exported to it, so both throw for every content id and path once the arguments and the initialization are checked: no reference gives the console's error for content that is not in the library ([SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Content/ContentDelete.cs#L25-L42) names only `0x809D5006`, no permission). The signatures, a 64-bit content id and a UTF-8 path, follow [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json#L541-L542) and SharpProspero, which agree. A null path and a call outside `sceContentDeleteInitialize` and `sceContentDeleteTerminate` throw, as the console's errors for them are unknown, and the order of the two checks is unverified. +- `sceContentDeleteById` and `sceContentDeleteByPath`: content database access and deletion are not implemented. Calls with valid arguments after initialization throw; the backend result cannot be inferred from an empty host library. Null paths after initialization throw instead of reproducing the original unchecked string access. +- `sceContentDeleteInitialize`: validates the confirmed 16 KiB heap requirement and lifecycle errors, but does not create the original allocator, mutex and database client. Initialization-parameter fields other than the 64-bit heap size at offset 8 remain unresolved. ### Unknown function info -- [sceContentDeleteByIdListForSf, sceContentDeleteGlobalMstat, sceContentDeleteMemoryInitialize, sceContentDeleteMemoryTerminate](../../../core/libs/prx/libSceContentDelete/Export.cpp) (libSceContentDelete) - exported symbols from decrypted PS5 firmware 1.00–3.20 libSceContentDelete.sprx; parameter lists and return values are unknown/unverified and no reference (KytyPS5, shadPS4, fpPS4, SharpProspero) documents their signatures. Declared as stubs that throw via `NotImplemented_nid_no_patch` +- `sceContentDeleteGlobalMstat`, `sceContentDeleteMemoryInitialize` and `sceContentDeleteMemoryTerminate`: neither exports nor callers were found in the supplied decompiled library corpus; their signatures remain unresolved. From 0b09b0c26225341d4cb99f6e6961e3ef27b49120 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:14:48 +0300 Subject: [PATCH 251/523] docs: link remaining abi debt to library sources --- docs/dev/debt/libSceBgft.md | 6 +++--- docs/dev/debt/libSceContentDelete.md | 6 +++--- docs/dev/debt/libSceConvertKeycode.md | 4 ++-- docs/dev/debt/libSceDiscId.md | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/dev/debt/libSceBgft.md b/docs/dev/debt/libSceBgft.md index 3da7019e92..839539717c 100644 --- a/docs/dev/debt/libSceBgft.md +++ b/docs/dev/debt/libSceBgft.md @@ -2,6 +2,6 @@ ### Unknown function info -- `sceBgftServiceDownloadGetProgress`, `sceBgftServiceIntDownloadGetProgress`, `sceBgftServiceIntDownloadGetGameAndGameAcProgress` and `sceBgftServiceIntDownloadGetPlayGoProgress`: signatures and the shared 88-byte output are confirmed. The library forwards the output through IPC without accessing individual fields; their types remain unresolved. -- `sceBgftServiceIntDownloadRegisterTaskByStorageEx`: confirmed parameter pointer and 32-bit task ID output. The input contains 32-bit scalars at offsets 0, 4, 56 and 104, strings at offsets 8, 16, 32, 40, 48, 64, 72, 80 and 88, and a 64-bit scalar at offset 96. Scalar meanings and the field at offset 24 remain unresolved; the parameter type is forward-declared. -- `sceBgftServiceIntDownloadGetPatchProgress` and `sceBgftServiceIntDownloadGetTaskInfo`: neither export nor a caller was found in the supplied decompiled library corpus. Their signatures remain unresolved. +- [sceBgftServiceDownloadGetProgress](../../../core/libs/prx/libSceBgft/Export.cpp), `sceBgftServiceIntDownloadGetProgress`, `sceBgftServiceIntDownloadGetGameAndGameAcProgress` and `sceBgftServiceIntDownloadGetPlayGoProgress`: signatures and the shared 88-byte output are confirmed. The library forwards the output through IPC without accessing individual fields; their types remain unresolved. +- [sceBgftServiceIntDownloadRegisterTaskByStorageEx](../../../core/libs/prx/libSceBgft/Export.cpp): confirmed parameter pointer and 32-bit task ID output. The input contains 32-bit scalars at offsets 0, 4, 56 and 104, strings at offsets 8, 16, 32, 40, 48, 64, 72, 80 and 88, and a 64-bit scalar at offset 96. Scalar meanings and the field at offset 24 remain unresolved; the parameter type is forward-declared. +- [sceBgftServiceIntDownloadGetPatchProgress](../../../core/libs/prx/libSceBgft/Export.cpp) and `sceBgftServiceIntDownloadGetTaskInfo`: neither export nor a caller was found in the supplied decompiled library corpus. Their signatures remain unresolved. diff --git a/docs/dev/debt/libSceContentDelete.md b/docs/dev/debt/libSceContentDelete.md index da7501c081..f7365210d5 100644 --- a/docs/dev/debt/libSceContentDelete.md +++ b/docs/dev/debt/libSceContentDelete.md @@ -2,9 +2,9 @@ ### Functional -- `sceContentDeleteById` and `sceContentDeleteByPath`: content database access and deletion are not implemented. Calls with valid arguments after initialization throw; the backend result cannot be inferred from an empty host library. Null paths after initialization throw instead of reproducing the original unchecked string access. -- `sceContentDeleteInitialize`: validates the confirmed 16 KiB heap requirement and lifecycle errors, but does not create the original allocator, mutex and database client. Initialization-parameter fields other than the 64-bit heap size at offset 8 remain unresolved. +- [sceContentDeleteById](../../../core/libs/prx/libSceContentDelete/Export.cpp) and `sceContentDeleteByPath`: content database access and deletion are not implemented. Calls with valid arguments after initialization throw; the backend result cannot be inferred from an empty host library. Null paths after initialization throw instead of reproducing the original unchecked string access. +- [sceContentDeleteInitialize](../../../core/libs/prx/libSceContentDelete/Export.cpp): validates the confirmed 16 KiB heap requirement and lifecycle errors, but does not create the original allocator, mutex and database client. Initialization-parameter fields other than the 64-bit heap size at offset 8 remain unresolved. ### Unknown function info -- `sceContentDeleteGlobalMstat`, `sceContentDeleteMemoryInitialize` and `sceContentDeleteMemoryTerminate`: neither exports nor callers were found in the supplied decompiled library corpus; their signatures remain unresolved. +- [sceContentDeleteGlobalMstat](../../../core/libs/prx/libSceContentDelete/Export.cpp), `sceContentDeleteMemoryInitialize` and `sceContentDeleteMemoryTerminate`: neither exports nor callers were found in the supplied decompiled library corpus; their signatures remain unresolved. diff --git a/docs/dev/debt/libSceConvertKeycode.md b/docs/dev/debt/libSceConvertKeycode.md index d220da5136..5c01a3cabc 100644 --- a/docs/dev/debt/libSceConvertKeycode.md +++ b/docs/dev/debt/libSceConvertKeycode.md @@ -2,5 +2,5 @@ ### Functional -- `sceConvertKeycodeGetVirtualKeycode` and `sceConvertKeycodeGetCharacter`: layouts 31–39 throw until the guest platform and compiled SDK version are exposed. `GetVirtualKeycode` also throws for version-dependent Y/Z mappings in layouts 2, 3 and 12. Mapping tables and modifier behavior are recovered; no SDK version is guessed. -- `sceConvertKeycodeGetImeKeyboardType`: user keyboard language and Japanese/Chinese keyboard variants are not exposed by the current UserService implementation. Valid-user queries throw instead of returning a fabricated keyboard type. The confirmed language/variant mapping and SDK compatibility rules still need integration with those settings. +- [sceConvertKeycodeGetVirtualKeycode](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) and `sceConvertKeycodeGetCharacter`: layouts 31–39 throw until the guest platform and compiled SDK version are exposed. `GetVirtualKeycode` also throws for version-dependent Y/Z mappings in layouts 2, 3 and 12. Mapping tables and modifier behavior are recovered; no SDK version is guessed. +- [sceConvertKeycodeGetImeKeyboardType](../../../core/libs/prx/libSceConvertKeycode/Export.cpp): user keyboard language and Japanese/Chinese keyboard variants are not exposed by the current UserService implementation. Valid-user queries throw instead of returning a fabricated keyboard type. The confirmed language/variant mapping and SDK compatibility rules still need integration with those settings. diff --git a/docs/dev/debt/libSceDiscId.md b/docs/dev/debt/libSceDiscId.md index 92a0e6f465..17eb02c445 100644 --- a/docs/dev/debt/libSceDiscId.md +++ b/docs/dev/debt/libSceDiscId.md @@ -2,4 +2,4 @@ ### Unknown function info -- `sceDiscIdGet` takes one output pointer; `sceDiscIdGet2` takes an output pointer and a 32-bit argument that must be zero. Both pass the pointer to a device ioctl without accessing its contents. Output structure sizes and fields require the device-side implementation or a caller and remain unresolved. +- [sceDiscIdGet](../../../core/libs/prx/libSceDiscId/Export.cpp) takes one output pointer; `sceDiscIdGet2` takes an output pointer and a 32-bit argument that must be zero. Both pass the pointer to a device ioctl without accessing its contents. Output structure sizes and fields require the device-side implementation or a caller and remain unresolved. From dd1478f4af311e2bc7a34747731a01713dd14681 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:18:28 +0300 Subject: [PATCH 252/523] fix(httpcache): restore request and memory pool argument layouts --- core/libs/prx/libSceHttpCache/Export.cpp | 87 ++++++++++++++++++------ docs/dev/debt/libSceHttpCache.md | 3 +- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceHttpCache/Export.cpp b/core/libs/prx/libSceHttpCache/Export.cpp index 6f5d37ccb1..12a2497b87 100644 --- a/core/libs/prx/libSceHttpCache/Export.cpp +++ b/core/libs/prx/libSceHttpCache/Export.cpp @@ -3,94 +3,143 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct HttpCacheHeader { + const char* name; + const char* value; +}; + +struct HttpCacheRequest { + std::uint32_t method; + std::uint32_t reserved; + const char* url; + std::size_t headerCount; + const HttpCacheHeader* headers; +}; + +struct HttpCacheResponseHeader { + std::uint32_t statusCode; + std::uint32_t reserved; + std::size_t headerCount; + const HttpCacheHeader* headers; +}; + +struct HttpCacheResponse { + std::uint32_t statusCode; + std::uint32_t reserved; + std::size_t headerCount; + const HttpCacheHeader* headers; + std::uint64_t contentLength; +}; + +struct HttpCacheQuotaEntry { + const char* name; + std::uint64_t size; +}; + +struct HttpCacheQuota { + std::size_t count; + const HttpCacheQuotaEntry* entries; +}; + +static_assert(sizeof(HttpCacheHeader) == 16); +static_assert(sizeof(HttpCacheRequest) == 32); +static_assert(offsetof(HttpCacheRequest, url) == 8); +static_assert(offsetof(HttpCacheRequest, headers) == 24); +static_assert(sizeof(HttpCacheResponseHeader) == 24); +static_assert(offsetof(HttpCacheResponseHeader, headers) == 16); +static_assert(sizeof(HttpCacheResponse) == 32); +static_assert(offsetof(HttpCacheResponse, contentLength) == 24); +static_assert(sizeof(HttpCacheQuotaEntry) == 16); +static_assert(sizeof(HttpCacheQuota) == 16); + extern "C" { -int APS5_VABI sceHttpCacheClear() { +int APS5_VABI sceHttpCacheClear(std::int32_t cacheId, std::int32_t userId, const char* tag) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheClearAll() { +int APS5_VABI sceHttpCacheClearAll(std::int32_t cacheId, std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheCompleteRequest() { +int APS5_VABI sceHttpCacheCompleteRequest(std::int32_t requestId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheCreateRequest() { +int APS5_VABI sceHttpCacheCreateRequest(std::int32_t cacheId, std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheCreateRequestWithTag() { +int APS5_VABI sceHttpCacheCreateRequestWithTag(std::int32_t cacheId, std::int32_t userId, const char* tag) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheDeleteRequest() { +int APS5_VABI sceHttpCacheDeleteRequest(std::int32_t requestId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheInit() { +int APS5_VABI sceHttpCacheInit(std::size_t poolSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheReadData() { +int APS5_VABI sceHttpCacheReadData(std::int32_t requestId, std::size_t size, void* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheRetrieve() { +int APS5_VABI sceHttpCacheRetrieve(std::int32_t requestId, const HttpCacheRequest* request, HttpCacheResponse* response, HttpCacheRequest* nextRequest, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheRetrieveWithMemoryPool() { +int APS5_VABI sceHttpCacheRetrieveWithMemoryPool(std::int32_t requestId, const HttpCacheRequest* request, HttpCacheResponse* response, HttpCacheRequest* nextRequest, std::int32_t* result, void* pool, std::size_t* requiredSize, std::size_t poolSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheRevalidate() { +int APS5_VABI sceHttpCacheRevalidate(std::int32_t requestId, const HttpCacheResponseHeader* header, HttpCacheResponse* response, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheRevalidateWithMemoryPool() { +int APS5_VABI sceHttpCacheRevalidateWithMemoryPool(std::int32_t requestId, const HttpCacheResponseHeader* header, HttpCacheResponse* response, std::int32_t* result, void* pool, std::size_t* requiredSize, std::size_t poolSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheSetCacheSharing() { +int APS5_VABI sceHttpCacheSetCacheSharing(std::uint8_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheSetData() { +int APS5_VABI sceHttpCacheSetData(std::int32_t requestId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheSetQuota() { +int APS5_VABI sceHttpCacheSetQuota(std::int32_t cacheId, const HttpCacheQuota* quota) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheSetResponseHeader() { +int APS5_VABI sceHttpCacheSetResponseHeader(std::int32_t requestId, const HttpCacheResponseHeader* header) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheSystemClearAll() { +int APS5_VABI sceHttpCacheSystemClearAll(const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpCacheSystemInit() { +int APS5_VABI sceHttpCacheSystemInit(const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -108,7 +157,7 @@ int APS5_VABI sceHttpCacheSystemTerm() { return 0; } -int APS5_VABI sceHttpCacheTerm() { +int APS5_VABI sceHttpCacheTerm(std::int32_t cacheId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttpCache.md b/docs/dev/debt/libSceHttpCache.md index 9ee1af9ece..a2d7b748a6 100644 --- a/docs/dev/debt/libSceHttpCache.md +++ b/docs/dev/debt/libSceHttpCache.md @@ -2,6 +2,5 @@ ### Unknown function info -- [sceHttpCacheClear, sceHttpCacheClearAll, sceHttpCacheCompleteRequest, sceHttpCacheCreateRequest, sceHttpCacheCreateRequestWithTag, sceHttpCacheDeleteRequest, sceHttpCacheInit, sceHttpCacheReadData, sceHttpCacheRetrieve, sceHttpCacheRetrieveWithMemoryPool, sceHttpCacheRevalidate, sceHttpCacheRevalidateWithMemoryPool, sceHttpCacheSetCacheSharing, sceHttpCacheSetData, sceHttpCacheSetQuota, sceHttpCacheSetResponseHeader, sceHttpCacheSystemClearAll, sceHttpCacheSystemInit, sceHttpCacheSystemSendStatistics, sceHttpCacheSystemShutdown, sceHttpCacheSystemTerm and sceHttpCacheTerm](../../../core/libs/prx/libSceHttpCache/Export.cpp) (libSceHttpCache) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceHttpCache.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified - +- [sceHttpCacheSystemSendStatistics and sceHttpCacheSystemTerm](../../../core/libs/prx/libSceHttpCache/Export.cpp): the supplied implementations return zero without reading arguments. No callers have yet established whether their public prototypes include unused arguments. - The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`sceHttpCacheClearAllUserTags`](../../../core/libs/prx/libSceHttpCache/Export.cpp), [`sceHttpCacheClearCacheInfo`](../../../core/libs/prx/libSceHttpCache/Export.cpp), [`sceHttpCacheDebugSetUrl`](../../../core/libs/prx/libSceHttpCache/Export.cpp), [`sceHttpCacheRegisterCacheInfoUrl`](../../../core/libs/prx/libSceHttpCache/Export.cpp), [`sceHttpCacheRemoveTag`](../../../core/libs/prx/libSceHttpCache/Export.cpp). From aaf286859beb47dd0ccfe5a1718bb4a4f58d0c35 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:21:42 +0300 Subject: [PATCH 253/523] fix(bluetooth): restore hid callbacks and registration checks --- core/libs/prx/libSceBluetoothHid/Export.cpp | 98 +++++++++++++++++---- core/libs/tests/GuestBluetoothHid.cpp | 70 ++++++++++----- docs/dev/debt/libSceBluetoothHid.md | 6 +- 3 files changed, 136 insertions(+), 38 deletions(-) diff --git a/core/libs/prx/libSceBluetoothHid/Export.cpp b/core/libs/prx/libSceBluetoothHid/Export.cpp index 5b7a222827..63c76ddd55 100644 --- a/core/libs/prx/libSceBluetoothHid/Export.cpp +++ b/core/libs/prx/libSceBluetoothHid/Export.cpp @@ -4,47 +4,115 @@ #include "prx/libc/include/General.hpp" #include +#include + +struct BluetoothHidThreadParam { + std::int32_t priority; + std::uint32_t padding; + std::uint64_t affinityMask; + std::uint32_t reserved; + std::uint32_t tailPadding; +}; + +struct BluetoothHidParam { + const BluetoothHidThreadParam* threadParam; + std::uint32_t reserved; + std::uint32_t padding; +}; + +struct BluetoothHidEvent { + std::uint64_t deviceHandle; + std::uint16_t vendorId; + std::uint16_t productId; + std::uint8_t type; + std::uint8_t status; + std::uint16_t reportSize; + std::uint8_t reserved[8]; + std::uint8_t report[78]; + std::uint8_t padding[2]; +}; + +struct BluetoothHidDeviceInfo; +struct BluetoothHidDeviceName; +using BluetoothHidCallback = void (APS5_VABI *)(const BluetoothHidEvent*, std::int32_t, void*); + +static_assert(sizeof(BluetoothHidThreadParam) == 24); +static_assert(offsetof(BluetoothHidThreadParam, affinityMask) == 8); +static_assert(offsetof(BluetoothHidThreadParam, reserved) == 16); +static_assert(sizeof(BluetoothHidParam) == 16); +static_assert(sizeof(BluetoothHidEvent) == 104); +static_assert(offsetof(BluetoothHidEvent, reportSize) == 14); +static_assert(offsetof(BluetoothHidEvent, report) == 24); + +namespace { +std::mutex stateMutex; +bool initialized = false; +bool callbackRegistered = false; +constexpr std::int32_t InvalidArgument = static_cast(0x81540001u); +constexpr std::int32_t NotInitialized = static_cast(0x81540004u); +constexpr std::int32_t NoCallback = static_cast(0x81540006u); +} extern "C" { std::int32_t APS5_VABI sceBluetoothHidInit() { + std::lock_guard lock(stateMutex); + initialized = true; return 0; } -std::int32_t APS5_VABI sceBluetoothHidRegisterCallback(void* callback, std::int32_t flags, void* param) { - (void)flags; - (void)param; - if (callback == nullptr) throw std::invalid_argument(std::string(__func__) + ": null callback"); +std::int32_t APS5_VABI sceBluetoothHidRegisterCallback(BluetoothHidCallback callback, void* userData, const BluetoothHidParam* param) { + std::lock_guard lock(stateMutex); + if (!initialized) return NotInitialized; + if (callback == nullptr) return InvalidArgument; + if (param != nullptr) { + if (param->reserved != 0) return InvalidArgument; + if (param->threadParam != nullptr) { + if (param->threadParam->reserved != 0) return InvalidArgument; + throw std::runtime_error("sceBluetoothHidRegisterCallback: custom worker scheduling is not implemented"); + } + } + (void)userData; + callbackRegistered = true; return 0; } std::int32_t APS5_VABI sceBluetoothHidUnregisterCallback() { + std::lock_guard lock(stateMutex); + if (!initialized) return NotInitialized; + if (!callbackRegistered) return NoCallback; + callbackRegistered = false; return 0; } std::int32_t APS5_VABI sceBluetoothHidRegisterDevice(std::uint16_t vendorId, std::uint16_t productId) { + std::lock_guard lock(stateMutex); + if (!initialized) return NotInitialized; + if (!callbackRegistered) return NoCallback; (void)vendorId; (void)productId; return 0; } std::int32_t APS5_VABI sceBluetoothHidUnregisterDevice(std::uint16_t vendorId, std::uint16_t productId) { + std::lock_guard lock(stateMutex); + if (!initialized) return NotInitialized; (void)vendorId; (void)productId; return 0; } -int APS5_VABI sceBluetoothHidGetDeviceName() { +int APS5_VABI sceBluetoothHidGetDeviceName(std::uint64_t deviceHandle, BluetoothHidDeviceName* name) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidGetInputReport() { +int APS5_VABI sceBluetoothHidGetInputReport(std::uint64_t deviceHandle, std::uint8_t reportId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidSetOutputReport() { +int APS5_VABI sceBluetoothHidSetOutputReport(std::uint64_t deviceHandle, std::uint8_t reportId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -53,42 +121,42 @@ int APS5_VABI sceBluetoothHidDebugGetVersion() { return 0x1e; } -int APS5_VABI sceBluetoothHidDisconnectDevice() { +int APS5_VABI sceBluetoothHidDisconnectDevice(std::int32_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidGetDeviceInfo() { +int APS5_VABI sceBluetoothHidGetDeviceInfo(std::int32_t deviceId, BluetoothHidDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidGetFeatureReport() { +int APS5_VABI sceBluetoothHidGetFeatureReport(std::uint64_t deviceHandle, std::uint8_t reportId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidGetReportDescriptor() { +int APS5_VABI sceBluetoothHidGetReportDescriptor(std::uint64_t deviceHandle, void* descriptor, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidInterruptOutput() { +int APS5_VABI sceBluetoothHidInterruptOutput(std::uint64_t deviceHandle, std::uint8_t reportId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidParamInitialize() { +int APS5_VABI sceBluetoothHidParamInitialize(BluetoothHidParam* param, const BluetoothHidThreadParam* threadParam) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidSetFeatureReport() { +int APS5_VABI sceBluetoothHidSetFeatureReport(std::uint64_t deviceHandle, std::uint8_t reportId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceBluetoothHidThreadParamInitialize() { +int APS5_VABI sceBluetoothHidThreadParamInitialize(BluetoothHidThreadParam* param, std::int32_t priority, std::uint64_t affinityMask) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestBluetoothHid.cpp b/core/libs/tests/GuestBluetoothHid.cpp index e118532944..fb37aa3c23 100644 --- a/core/libs/tests/GuestBluetoothHid.cpp +++ b/core/libs/tests/GuestBluetoothHid.cpp @@ -1,29 +1,46 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include #include #include #include +struct BluetoothHidEvent; +struct BluetoothHidDeviceInfo; +struct BluetoothHidThreadParam { + std::int32_t priority; + std::uint32_t padding; + std::uint64_t affinityMask; + std::uint32_t reserved; + std::uint32_t tailPadding; +}; +struct BluetoothHidParam { + const BluetoothHidThreadParam* threadParam; + std::uint32_t reserved; + std::uint32_t padding; +}; +using BluetoothHidCallback = void (APS5_VABI *)(const BluetoothHidEvent*, std::int32_t, void*); + extern "C" { std::int32_t APS5_VABI sceBluetoothHidInit(); -std::int32_t APS5_VABI sceBluetoothHidRegisterCallback(void* callback, std::int32_t flags, void* param); +std::int32_t APS5_VABI sceBluetoothHidRegisterCallback(BluetoothHidCallback callback, void* userData, const BluetoothHidParam* param); std::int32_t APS5_VABI sceBluetoothHidUnregisterCallback(); std::int32_t APS5_VABI sceBluetoothHidRegisterDevice(std::uint16_t vendorId, std::uint16_t productId); std::int32_t APS5_VABI sceBluetoothHidUnregisterDevice(std::uint16_t vendorId, std::uint16_t productId); -int APS5_VABI sceBluetoothHidGetInputReport(); +int APS5_VABI sceBluetoothHidGetInputReport(std::uint64_t, std::uint8_t); int APS5_VABI sceBluetoothHidDebugGetVersion(); -int APS5_VABI sceBluetoothHidGetDeviceInfo(); -int APS5_VABI sceBluetoothHidDisconnectDevice(); -int APS5_VABI sceBluetoothHidGetFeatureReport(); -int APS5_VABI sceBluetoothHidSetFeatureReport(); +int APS5_VABI sceBluetoothHidGetDeviceInfo(std::int32_t, BluetoothHidDeviceInfo*); +int APS5_VABI sceBluetoothHidDisconnectDevice(std::int32_t); +int APS5_VABI sceBluetoothHidGetFeatureReport(std::uint64_t, std::uint8_t); +int APS5_VABI sceBluetoothHidSetFeatureReport(std::uint64_t, std::uint8_t, const void*, std::size_t); } namespace { int callbackCalls = 0; -void APS5_VABI Callback() { +void APS5_VABI Callback(const BluetoothHidEvent*, std::int32_t, void*) { ++callbackCalls; } @@ -37,21 +54,34 @@ void Require(bool condition, const char* message) { } int main() { + constexpr auto notInitialized = static_cast(0x81540004u); + constexpr auto invalidArgument = static_cast(0x81540001u); + constexpr auto noCallback = static_cast(0x81540006u); + Require(sceBluetoothHidRegisterCallback(nullptr, nullptr, nullptr) == notInitialized, "initialization check order"); + Require(sceBluetoothHidRegisterDevice(1, 2) == notInitialized, "registration before initialization"); + Require(sceBluetoothHidUnregisterDevice(1, 2) == notInitialized, "unregistration before initialization"); + Require(sceBluetoothHidUnregisterCallback() == notInitialized, "callback removal before initialization"); Require(sceBluetoothHidInit() == 0, "initialization failed"); - std::uint8_t param[16]{}; - Require(sceBluetoothHidRegisterCallback(reinterpret_cast(&Callback), 0, param) == 0, "callback registration failed"); + Require(sceBluetoothHidInit() == 0, "repeated initialization failed"); + Require(sceBluetoothHidRegisterDevice(1, 2) == noCallback, "registration without callback"); + Require(sceBluetoothHidUnregisterCallback() == noCallback, "removing absent callback"); + BluetoothHidParam param{}; + Require(sceBluetoothHidRegisterCallback(&Callback, &callbackCalls, ¶m) == 0, "callback registration failed"); Require(sceBluetoothHidRegisterDevice(0x44f, 0x1000) == 0, "device registration failed"); Require(callbackCalls == 0, "a device connection was reported"); + Require(sceBluetoothHidRegisterCallback(nullptr, nullptr, ¶m) == invalidArgument, "null callback validation"); + param.reserved = 1; + Require(sceBluetoothHidRegisterCallback(&Callback, nullptr, ¶m) == invalidArgument, "reserved field validation"); + param.reserved = 0; + BluetoothHidThreadParam threadParam{}; + threadParam.reserved = 1; + param.threadParam = &threadParam; + Require(sceBluetoothHidRegisterCallback(&Callback, nullptr, ¶m) == invalidArgument, "thread reserved field validation"); + Require(sceBluetoothHidRegisterCallback(&Callback, nullptr, nullptr) == 0, "optional parameters rejected"); bool threw = false; - try { - sceBluetoothHidRegisterCallback(nullptr, 0, param); - } catch (const std::invalid_argument&) { - threw = true; - } - Require(threw, "a null callback was accepted"); threw = false; try { - sceBluetoothHidGetInputReport(); + sceBluetoothHidGetInputReport(0, 0); } catch (const std::runtime_error&) { threw = true; } @@ -59,28 +89,28 @@ int main() { Require(sceBluetoothHidDebugGetVersion() == 0x1e, "debug get version did not return the firmware version"); threw = false; try { - sceBluetoothHidGetDeviceInfo(); + sceBluetoothHidGetDeviceInfo(0, nullptr); } catch (const std::runtime_error&) { threw = true; } Require(threw, "get device info did not throw"); threw = false; try { - sceBluetoothHidDisconnectDevice(); + sceBluetoothHidDisconnectDevice(0); } catch (const std::runtime_error&) { threw = true; } Require(threw, "disconnect device did not throw"); threw = false; try { - sceBluetoothHidGetFeatureReport(); + sceBluetoothHidGetFeatureReport(0, 0); } catch (const std::runtime_error&) { threw = true; } Require(threw, "get feature report did not throw"); threw = false; try { - sceBluetoothHidSetFeatureReport(); + sceBluetoothHidSetFeatureReport(0, 0, nullptr, 0); } catch (const std::runtime_error&) { threw = true; } diff --git a/docs/dev/debt/libSceBluetoothHid.md b/docs/dev/debt/libSceBluetoothHid.md index b8ec7bf2ab..faf41e09da 100644 --- a/docs/dev/debt/libSceBluetoothHid.md +++ b/docs/dev/debt/libSceBluetoothHid.md @@ -2,9 +2,9 @@ ### Silent stubs -- [libSceBluetoothHid](../../../core/libs/prx/libSceBluetoothHid/Export.cpp) implements only the no-device path: initialization and callback and device registration succeed, the callback is never called because no device connects, and calls that need a connected device throw +- [libSceBluetoothHid](../../../core/libs/prx/libSceBluetoothHid/Export.cpp): the host still has no device backend or event worker. Registration retains only lifecycle state and never delivers the recovered 104-byte callback events. Custom worker scheduling throws; device ioctl failures and resource exhaustion are not modeled. ### Unknown function info -- [libSceBluetoothHid](../../../core/libs/prx/libSceBluetoothHid/Export.cpp) (libSceBluetoothHid) - signatures taken from PPSA01317: `sceBluetoothHidInit()` and `sceBluetoothHidUnregisterCallback()` without arguments, `sceBluetoothHidRegisterCallback(callback, 0, param)` with a 16-byte param, and `sceBluetoothHidRegisterDevice` / `sceBluetoothHidUnregisterDevice(vendorId, productId)`; the report and device name exports are declared without parameters -- [sceBluetoothHidDebugGetVersion, sceBluetoothHidDisconnectDevice, sceBluetoothHidGetDeviceInfo, sceBluetoothHidGetFeatureReport, sceBluetoothHidGetReportDescriptor, sceBluetoothHidInterruptOutput, sceBluetoothHidParamInitialize, sceBluetoothHidSetFeatureReport, sceBluetoothHidThreadParamInitialize](../../../core/libs/prx/libSceBluetoothHid/Export.cpp) (libSceBluetoothHid) - throwing stubs declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware +- [sceBluetoothHidGetDeviceName and sceBluetoothHidGetDeviceInfo](../../../core/libs/prx/libSceBluetoothHid/Export.cpp): output pointers are forwarded to device ioctls without accessing the buffers. Their output sizes and layouts remain unresolved. Report APIs use 64-bit device handles; device-info and disconnect APIs use 32-bit device IDs. +- [sceBluetoothHidDebugGetVersion](../../../core/libs/prx/libSceBluetoothHid/Export.cpp): returns a constant without reading arguments, so the supplied implementation does not establish whether the public prototype has unused parameters. From 8c411544ebeb54109464977fca7bcf273feb10a1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:27:05 +0300 Subject: [PATCH 254/523] fix(keyboard): restore device arguments and verified key mappings --- core/libs/prx/libSceKeyboard/Export.cpp | 27 ++++++++++--------- .../prx/libSceKeyboard/src/keyboard_impl.cpp | 7 ++--- .../prx/libSceKeyboard/tests/Keyboard.cpp | 8 +++--- docs/dev/debt/libSceKeyboard.md | 6 +++-- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceKeyboard/Export.cpp b/core/libs/prx/libSceKeyboard/Export.cpp index d8d408237a..c7be6fe290 100644 --- a/core/libs/prx/libSceKeyboard/Export.cpp +++ b/core/libs/prx/libSceKeyboard/Export.cpp @@ -1,6 +1,9 @@ #include "prx/libc/include/General.hpp" #include "prx/libSceKeyboard/include/KeyboardState.hpp" +struct KeyboardDeviceInfo; +struct KeyboardProcessFocus; + extern "C" { int APS5_VABI sceKeyboardInit(void) { @@ -24,21 +27,21 @@ int APS5_VABI sceKeyboardReadState(int32_t handle, KeyboardData* data) { return Keyboard::ReadState(handle, data); } -int APS5_VABI sceKeyboardGetKey2Char(int32_t handle, int32_t arrange, uint32_t led, uint32_t modifier_key, uint16_t key_code, KeyboardCharData* char_data) { - return Keyboard::GetKey2Char(handle, arrange, led, modifier_key, key_code, char_data); +int APS5_VABI sceKeyboardGetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint8_t led, std::uint8_t modifierKey, std::uint16_t keyCode, KeyboardCharData* charData) { + return Keyboard::GetKey2Char(handle, arrange, led, modifierKey, keyCode, charData); } -int APS5_VABI sceKeyboardConnectPort(void) { +int APS5_VABI sceKeyboardConnectPort(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardDebugGetDeviceId(void) { +int APS5_VABI sceKeyboardDebugGetDeviceId(std::int32_t handle, std::int32_t* deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardDeviceOpen(void) { +int APS5_VABI sceKeyboardDeviceOpen(std::uint64_t deviceId, std::int32_t index, std::uint32_t options) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -48,27 +51,27 @@ int APS5_VABI sceKeyboardDisconnectAll(void) { return 0; } -int APS5_VABI sceKeyboardDisconnectDevice(void) { +int APS5_VABI sceKeyboardDisconnectDevice(std::int32_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardDisconnectPort(void) { +int APS5_VABI sceKeyboardDisconnectPort(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardGetConnection(void) { +int APS5_VABI sceKeyboardGetConnection(std::int32_t handle, bool* connected) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardGetDeviceInfo(void) { +int APS5_VABI sceKeyboardGetDeviceInfo(std::int32_t deviceId, KeyboardDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardGetHandle(void) { +int APS5_VABI sceKeyboardGetHandle(std::int32_t userId, std::int32_t type, std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -88,12 +91,12 @@ int APS5_VABI sceKeyboardPadEmulateOpen(void) { return 0; } -int APS5_VABI sceKeyboardSetProcessFocus(void) { +int APS5_VABI sceKeyboardSetProcessFocus(const KeyboardProcessFocus* focus) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceKeyboardSetProcessPrivilege(void) { +int APS5_VABI sceKeyboardSetProcessPrivilege(std::uint8_t privilege) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp b/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp index 0a16e2d75e..bd9cf7d8f8 100644 --- a/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp +++ b/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp @@ -19,8 +19,8 @@ std::uint32_t modifiers = 0; std::array keys{}; int keyCount = 0; -constexpr char US_NORMAL[] = "1234567890\n\0\b\t -=[]\\\0;'`,./"; -constexpr char US_SHIFTED[] = "!@#$%^&*()\n\0\b\t _+{}|\0:\"~<>?"; +constexpr char US_NORMAL[] = "1234567890\n\0\b\t -=[]\\\\;'`,./"; +constexpr char US_SHIFTED[] = "!@#$%^&*()\n\0\b\t _+{}||:\"~<>?"; constexpr char JIS_NORMAL[] = "1234567890\n\0\b\t -^@[]];:\0,./"; constexpr char JIS_SHIFTED[] = "!\"#$%&'()\0\n\0\b\t =~`{}}+*\0<>?"; constexpr char KEYPAD_DIGITS[] = "1234567890."; @@ -81,8 +81,9 @@ std::uint16_t character(bool jis, std::uint32_t ledState, std::uint32_t modifier const char* table = jis ? (shift ? JIS_SHIFTED : JIS_NORMAL) : (shift ? US_SHIFTED : US_NORMAL); return static_cast(table[keyCode - 0x1e]); } + if (!jis && keyCode == 0x64) return shift ? '|' : '\\'; if (jis && keyCode == 0x87) return shift ? '_' : '\\'; - if (jis && keyCode == 0x89) return shift ? '|' : '\\'; + if (jis && keyCode == 0x89) return shift ? '|' : 0x00A5; switch (keyCode) { case 0x54: return '/'; case 0x55: return '*'; diff --git a/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp b/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp index 97e1dd5129..7c97932bff 100644 --- a/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp +++ b/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp @@ -13,7 +13,7 @@ int APS5_VABI sceKeyboardOpen(int, std::int32_t, std::int32_t, const void*); int APS5_VABI sceKeyboardClose(std::int32_t); int APS5_VABI sceKeyboardRead(std::int32_t, KeyboardData*, std::int32_t); int APS5_VABI sceKeyboardReadState(std::int32_t, KeyboardData*); -int APS5_VABI sceKeyboardGetKey2Char(std::int32_t, std::int32_t, std::uint32_t, std::uint32_t, std::uint16_t, KeyboardCharData*); +int APS5_VABI sceKeyboardGetKey2Char(std::int32_t, std::int32_t, std::uint8_t, std::uint8_t, std::uint16_t, KeyboardCharData*); int APS5_VABI sceKeyboardConnectPort(void); int APS5_VABI sceKeyboardDisconnectAll(void); } @@ -115,13 +115,15 @@ int main() { Require(Char(KEYBOARD_LED_CAPS_LOCK, 0, 0x1e) == '1' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x1e) == '!'); Require(Char(0, 0, 0x27) == '0' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x27) == ')'); Require(Char(0, 0, 0x28) == '\n' && Char(0, 0, 0x2a) == '\b' && Char(0, 0, 0x2b) == '\t' && Char(0, 0, 0x2c) == ' '); - Require(Char(0, 0, 0x29) == 0 && Char(0, 0, 0x32) == 0 && Char(0, 0, 0x3a) == 0 && Char(0, 0, 0xe1) == 0); + Require(Char(0, 0, 0x29) == 0 && Char(0, 0, 0x3a) == 0 && Char(0, 0, 0xe1) == 0); Require(Char(0, 0, 0x31) == '\\' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x31) == '|'); Require(Char(0, 0, 0x34) == '\'' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x34) == '"'); Require(Char(0, 0, 0x38) == '/' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x38) == '?'); Require(Char(0, 0, 0x59) == 0 && Char(KEYBOARD_LED_NUM_LOCK, 0, 0x59) == '1'); Require(Char(KEYBOARD_LED_NUM_LOCK, 0, 0x62) == '0' && Char(KEYBOARD_LED_NUM_LOCK, 0, 0x63) == '.'); Require(Char(0, 0, 0x54) == '/' && Char(0, 0, 0x57) == '+' && Char(0, 0, 0x58) == '\n'); + Require(Char(0, 0, 0x32) == '\\' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x32) == '|'); + Require(Char(0, 0, 0x64) == '\\' && Char(0, KEYBOARD_MOD_LEFT_SHIFT, 0x64) == '|'); Require(Char(0, 0, 0x87) == 0 && Char(0, 0, 0x89) == 0); constexpr std::uint32_t shift = KEYBOARD_MOD_LEFT_SHIFT; @@ -134,7 +136,7 @@ int main() { Require(Jis(0, 0x31) == ']' && Jis(shift, 0x31) == '}' && Jis(0, 0x32) == ']' && Jis(shift, 0x32) == '}'); Require(Jis(0, 0x33) == ';' && Jis(shift, 0x33) == '+' && Jis(0, 0x34) == ':' && Jis(shift, 0x34) == '*'); Require(Jis(0, 0x35) == 0 && Jis(shift, 0x35) == 0 && Jis(0, 0x36) == ',' && Jis(shift, 0x37) == '>' && Jis(shift, 0x38) == '?'); - Require(Jis(0, 0x87) == '\\' && Jis(shift, 0x87) == '_' && Jis(0, 0x89) == '\\' && Jis(shift, 0x89) == '|'); + Require(Jis(0, 0x87) == '\\' && Jis(shift, 0x87) == '_' && Jis(0, 0x89) == 0x00A5 && Jis(shift, 0x89) == '|'); Require(Jis(0, 0x59, KEYBOARD_LED_NUM_LOCK) == '1' && Jis(0, 0x59) == 0 && Jis(0, 0x55) == '*' && Jis(0, 0xe1) == 0); Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, 0x04, nullptr) == KEYBOARD_ERROR_INVALID_ARG); Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_106, 0, 0, 0x04, nullptr) == KEYBOARD_ERROR_INVALID_ARG); diff --git a/docs/dev/debt/libSceKeyboard.md b/docs/dev/debt/libSceKeyboard.md index a0a894689e..60edc36b24 100644 --- a/docs/dev/debt/libSceKeyboard.md +++ b/docs/dev/debt/libSceKeyboard.md @@ -2,9 +2,11 @@ ### Functional -- [sceKeyboardGetKey2Char](../../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) (libSceKeyboard) translates the 101-key (US) arrangement and the alphanumeric layer of the 106-key (Japanese) one, which follows the JIS layout of the HID usages: the Yen (0x89) and Ro (0x87) keys return `\` (0x5C) unshifted, as JIS X 0201 places the yen sign there; whether the console returns U+00A5 instead is unverified. With the Kana LED (bit 4, the HID position) set the 106-key arrangement throws, as the kana the console returns are unknown. Ctrl and Alt do not change the character. +- [sceKeyboardGetKey2Char](../../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp): the original validates HID usages 4 through 231 before the handle and preserves unconverted HID usages in the output with `processed == 0`. The host instead accepts out-of-range usages and writes zero for unconverted keys. The original ignores the Kana LED, while the host throws for it. This partially implemented function contains `NotImplemented_nid_no_patch(__func__)`; its body is preserved under the requested restriction. ### Unknown function info - [sceDbgKeyboardClose, sceDbgKeyboardGetKey2Char, sceDbgKeyboardInit, sceDbgKeyboardOpen, sceDbgKeyboardRead, sceDbgKeyboardReadState](../../../core/libs/prx/libSceKeyboard/Export.cpp) (libSceKeyboard) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceKeyboard.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. -- [sceKeyboardConnectPort, sceKeyboardDebugGetDeviceId, sceKeyboardDeviceOpen, sceKeyboardDisconnectAll, sceKeyboardDisconnectDevice, sceKeyboardDisconnectPort, sceKeyboardGetConnection, sceKeyboardGetDeviceInfo, sceKeyboardGetHandle, sceKeyboardMbusInit, sceKeyboardPadEmulateClose, sceKeyboardPadEmulateOpen, sceKeyboardSetProcessFocus, sceKeyboardSetProcessPrivilege](../../../core/libs/prx/libSceKeyboard/Export.cpp) (libSceKeyboard) - throwing stubs declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware +- [sceKeyboardGetDeviceInfo and sceKeyboardSetProcessFocus](../../../core/libs/prx/libSceKeyboard/Export.cpp): pointer arguments are passed to device ioctls. Device-info storage is 168 bytes in the connection-query caller, but its fields and the process-focus layout remain unresolved. +- [sceKeyboardConnectPort, sceKeyboardDisconnectPort and sceKeyboardDeviceOpen](../../../core/libs/prx/libSceKeyboard/Export.cpp): argument widths are confirmed. The meaning/order of the two 32-bit port arguments and the final 32-bit device-open argument still require the device-service backend. +- [sceKeyboardPadEmulateClose and sceKeyboardPadEmulateOpen](../../../core/libs/prx/libSceKeyboard/Export.cpp): the supplied keyboard library has neither export; their signatures remain unresolved. From 7b44b3c8d171afec9d395a01b72be18abe4c7dc4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:29:36 +0300 Subject: [PATCH 255/523] fix(mbus): restore device service parameter pointers --- core/libs/prx/libSceMbus/Export.cpp | 42 ++++++++++++++++++--- core/libs/prx/libSceMbus/PendingExports.cpp | 4 +- core/libs/tests/GuestMbus.cpp | 22 +++++++++-- docs/dev/debt/libSceMbus.md | 6 ++- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceMbus/Export.cpp b/core/libs/prx/libSceMbus/Export.cpp index 10a101f22c..6d67b46184 100644 --- a/core/libs/prx/libSceMbus/Export.cpp +++ b/core/libs/prx/libSceMbus/Export.cpp @@ -5,11 +5,41 @@ #include +struct DeviceServiceThreadParam { + std::uint64_t affinityMask; + std::int32_t priority; + std::uint32_t reserved; +}; + +struct DeviceServiceInitParam { + std::uint32_t size; + std::uint32_t reserved; + const DeviceServiceThreadParam* threadParam; +}; + +struct DeviceServiceUsageFilter { + std::uint16_t usagePage; + std::uint16_t minimumUsage; + std::uint16_t maximumUsage; +}; + +static_assert(sizeof(DeviceServiceThreadParam) == 16); +static_assert(offsetof(DeviceServiceThreadParam, priority) == 8); +static_assert(sizeof(DeviceServiceInitParam) == 16); +static_assert(offsetof(DeviceServiceInitParam, threadParam) == 8); +static_assert(sizeof(DeviceServiceUsageFilter) == 6); + extern "C" { -std::int32_t APS5_VABI sceDeviceServiceInitialize(std::int32_t mode, std::int32_t flags) { +std::int32_t APS5_VABI sceDeviceServiceInitialize(std::int32_t mode, const DeviceServiceInitParam* param) { (void)mode; - (void)flags; + if (param != nullptr) { + if (param->size != sizeof(DeviceServiceInitParam) || param->reserved != 0) return static_cast(0x809B0080u); + if (param->threadParam != nullptr) { + if (param->threadParam->reserved != 0) return static_cast(0x809B0080u); + throw std::runtime_error("sceDeviceServiceInitialize: custom worker scheduling is not implemented"); + } + } return 0; } @@ -17,14 +47,14 @@ std::int32_t APS5_VABI sceDeviceServiceTerminate() { return 0; } -std::int32_t APS5_VABI sceDeviceServiceGetEventState(std::int32_t deviceType) { - (void)deviceType; +std::int32_t APS5_VABI sceDeviceServiceGetEventState(std::int32_t clear) { + (void)clear; return 0; } -std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass, std::int32_t arg1, std::int32_t arg2, void* infos, std::int32_t maxInfos, std::int32_t* count, std::int32_t* reserved, std::uint64_t infoSize) { +std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass, const DeviceServiceUsageFilter* filter, std::int32_t arg2, void* infos, std::int32_t maxInfos, std::int32_t* count, std::int32_t* reserved, std::uint32_t infoSize) { (void)deviceClass; - (void)arg1; + (void)filter; (void)arg2; (void)infos; (void)reserved; diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index 907eeb12c0..deb64a2463 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -31,7 +31,7 @@ int APS5_VABI sceMbusAddHandle() { return 0; } -int APS5_VABI sceMbusAddHandleByDeviceId() { +int APS5_VABI sceMbusAddHandleByDeviceId(std::uint32_t deviceClass, std::int32_t handle, std::uint64_t deviceId, std::uint32_t first, std::uint32_t second, std::uint32_t third) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -516,7 +516,7 @@ int APS5_VABI sceMbusSetTestFlag() { return 0; } -int APS5_VABI sceMbusSetThreadParam() { +int APS5_VABI sceMbusSetThreadParam(std::int32_t priority, std::uint64_t affinityMask, std::uint64_t reserved) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestMbus.cpp b/core/libs/tests/GuestMbus.cpp index c4f97b1141..d1c7c85258 100644 --- a/core/libs/tests/GuestMbus.cpp +++ b/core/libs/tests/GuestMbus.cpp @@ -6,12 +6,20 @@ #include #include +struct DeviceServiceThreadParam; +struct DeviceServiceInitParam { + std::uint32_t size; + std::uint32_t reserved; + const DeviceServiceThreadParam* threadParam; +}; +struct DeviceServiceUsageFilter; + extern "C" { -std::int32_t APS5_VABI sceDeviceServiceInitialize(std::int32_t mode, std::int32_t flags); +std::int32_t APS5_VABI sceDeviceServiceInitialize(std::int32_t mode, const DeviceServiceInitParam* param); std::int32_t APS5_VABI sceDeviceServiceTerminate(); -std::int32_t APS5_VABI sceDeviceServiceGetEventState(std::int32_t deviceType); +std::int32_t APS5_VABI sceDeviceServiceGetEventState(std::int32_t clear); +std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass, const DeviceServiceUsageFilter* filter, std::int32_t arg2, void* infos, std::int32_t maxInfos, std::int32_t* count, std::int32_t* reserved, std::uint32_t infoSize); std::int32_t APS5_VABI sceDeviceServiceGetGeneration(); -std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass, std::int32_t arg1, std::int32_t arg2, void* infos, std::int32_t maxInfos, std::int32_t* count, std::int32_t* reserved, std::uint64_t infoSize); } namespace { @@ -26,6 +34,14 @@ void Require(bool condition, const char* message) { } int main() { + DeviceServiceInitParam param{}; + constexpr auto invalidArgument = static_cast(0x809B0080u); + Require(sceDeviceServiceInitialize(3, ¶m) == invalidArgument, "invalid parameter size accepted"); + param.size = 16; + param.reserved = 1; + Require(sceDeviceServiceInitialize(3, ¶m) == invalidArgument, "reserved field accepted"); + param.reserved = 0; + Require(sceDeviceServiceInitialize(3, ¶m) == 0, "default parameter initialization failed"); Require(sceDeviceServiceInitialize(3, 0) == 0, "initialization failed"); Require(sceDeviceServiceGetGeneration() == 0, "the device set started at another generation"); Require(sceDeviceServiceGetEventState(1) == 0, "a device event was reported"); diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index 1ae13da251..c19904c941 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -6,6 +6,8 @@ ### Unknown function info -- [libSceMbus](../../../core/libs/prx/libSceMbus/Export.cpp) (`libSceDeviceService`) - signatures taken from PPSA01317, which calls `sceDeviceServiceInitialize(3, 0)`, polls `sceDeviceServiceGetEventState(1)` and queries again when bit 0 or 1 is set, and calls `sceDeviceServiceQueryDeviceInfo_(0x7001, 0, 0, infos, 1, &count, &unknown, 0x70)`, treating a negative result as an error and `count` as the number of entries. The host has no devices: the event state is 0 and a query returns 0 with `count` 0 +- [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 115 symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 113 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. + +- [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. From 223cc1c45695569e55c7668c7fbf159db7b0d315 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:32:50 +0300 Subject: [PATCH 256/523] fix(opuscelt): restore encoder and control signatures --- core/libs/prx/libSceOpusCeltEnc/Export.cpp | 24 ++++++++++++---------- docs/dev/debt/libSceOpusCeltEnc.md | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceOpusCeltEnc/Export.cpp b/core/libs/prx/libSceOpusCeltEnc/Export.cpp index df4e7d1854..bcbe6e9029 100644 --- a/core/libs/prx/libSceOpusCeltEnc/Export.cpp +++ b/core/libs/prx/libSceOpusCeltEnc/Export.cpp @@ -3,59 +3,61 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct OpusCeltEncoder; + extern "C" { -int APS5_VABI sceOpusCeltEncCreate() { +int APS5_VABI sceOpusCeltEncCreate(OpusCeltEncoder* encoder, std::int32_t sampleRate, std::int32_t channels, std::int32_t application) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncCreateEx() { +int APS5_VABI sceOpusCeltEncCreateEx(const std::uint32_t* contextId, OpusCeltEncoder* encoder, std::int32_t sampleRate, std::int32_t channels, std::int32_t application) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncCtl() { +int APS5_VABI sceOpusCeltEncCtl(OpusCeltEncoder* encoder, std::int32_t request, ...) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncDestroy() { +int APS5_VABI sceOpusCeltEncDestroy(OpusCeltEncoder* encoder) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncEncode() { +int APS5_VABI sceOpusCeltEncEncode(OpusCeltEncoder* encoder, const std::int16_t* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncEncodeFloat() { +int APS5_VABI sceOpusCeltEncEncodeFloat(OpusCeltEncoder* encoder, const float* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncEncodeFloatWithPriority() { +int APS5_VABI sceOpusCeltEncEncodeFloatWithPriority(OpusCeltEncoder* encoder, const float* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes, std::uint32_t priority) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncEncodeWithPriority() { +int APS5_VABI sceOpusCeltEncEncodeWithPriority(OpusCeltEncoder* encoder, const std::int16_t* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes, std::uint32_t priority) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncGetSize() { +int APS5_VABI sceOpusCeltEncGetSize(std::int32_t channels) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncInitialize() { +int APS5_VABI sceOpusCeltEncInitialize(std::uint32_t* contextId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusCeltEncTerminate() { +int APS5_VABI sceOpusCeltEncTerminate(const std::uint32_t* contextId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceOpusCeltEnc.md b/docs/dev/debt/libSceOpusCeltEnc.md index 6f232c9cee..6ef2fceb48 100644 --- a/docs/dev/debt/libSceOpusCeltEnc.md +++ b/docs/dev/debt/libSceOpusCeltEnc.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceOpusCeltEncCreate, sceOpusCeltEncCreateEx, sceOpusCeltEncCtl, sceOpusCeltEncDestroy, sceOpusCeltEncEncode, sceOpusCeltEncEncodeFloat, sceOpusCeltEncEncodeFloatWithPriority, sceOpusCeltEncEncodeWithPriority, sceOpusCeltEncGetSize, sceOpusCeltEncInitialize and sceOpusCeltEncTerminate](../../../core/libs/prx/libSceOpusCeltEnc/Export.cpp) (libSceOpusCeltEnc) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceOpusCeltEnc.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [sceOpusCeltEncTerminate](../../../core/libs/prx/libSceOpusCeltEnc/Export.cpp): the context-ID pointer is confirmed. The null-input path does not define the return register, so an integer return contract is not established. The current integer return declaration is retained because changing it to `void` would require altering the protected stub body, which contains `return 0`. From 45555f165f08272906c410a0b51e71bf23941c87 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:32:51 +0300 Subject: [PATCH 257/523] fix(opussilk): restore encoding and batch control signatures --- core/libs/prx/libSceOpusSilkEnc/Export.cpp | 30 ++++++++++++---------- docs/dev/debt/libSceOpusSilkEnc.md | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceOpusSilkEnc/Export.cpp b/core/libs/prx/libSceOpusSilkEnc/Export.cpp index 201dab2e4b..4e741a3bf9 100644 --- a/core/libs/prx/libSceOpusSilkEnc/Export.cpp +++ b/core/libs/prx/libSceOpusSilkEnc/Export.cpp @@ -3,74 +3,76 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct OpusSilkEncoder; + extern "C" { -int APS5_VABI sceOpusSilkEncCancel() { +int APS5_VABI sceOpusSilkEncCancel(OpusSilkEncoder* encoder, std::uint32_t batchId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncCreate() { +int APS5_VABI sceOpusSilkEncCreate(OpusSilkEncoder* encoder, std::int32_t sampleRate, std::int32_t channels, std::int32_t application) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncCreateEx() { +int APS5_VABI sceOpusSilkEncCreateEx(const std::uint32_t* contextId, OpusSilkEncoder* encoder, std::int32_t sampleRate, std::int32_t channels, std::int32_t application) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncCtl() { +int APS5_VABI sceOpusSilkEncCtl(OpusSilkEncoder* encoder, std::int32_t request, ...) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncDestroy() { +int APS5_VABI sceOpusSilkEncDestroy(OpusSilkEncoder* encoder) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncEncode() { +int APS5_VABI sceOpusSilkEncEncode(OpusSilkEncoder* encoder, const std::int16_t* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncEncodeFloat() { +int APS5_VABI sceOpusSilkEncEncodeFloat(OpusSilkEncoder* encoder, const float* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncEncodeFloatWithPriority() { +int APS5_VABI sceOpusSilkEncEncodeFloatWithPriority(OpusSilkEncoder* encoder, const float* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes, std::uint32_t priority) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncEncodeFloatWithPriorityAndTimeout() { +int APS5_VABI sceOpusSilkEncEncodeFloatWithPriorityAndTimeout(OpusSilkEncoder* encoder, const float* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes, std::uint32_t priority, std::uint32_t timeout, std::uint32_t* batchId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncEncodeWithPriority() { +int APS5_VABI sceOpusSilkEncEncodeWithPriority(OpusSilkEncoder* encoder, const std::int16_t* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes, std::uint32_t priority) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncEncodeWithPriorityAndTimeout() { +int APS5_VABI sceOpusSilkEncEncodeWithPriorityAndTimeout(OpusSilkEncoder* encoder, const std::int16_t* samples, std::int32_t frameSize, std::uint8_t* data, std::int32_t maxDataBytes, std::uint32_t priority, std::uint32_t timeout, std::uint32_t* batchId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncGetSize() { +int APS5_VABI sceOpusSilkEncGetSize(std::int32_t channels) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncInitialize() { +int APS5_VABI sceOpusSilkEncInitialize(std::uint32_t* contextId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceOpusSilkEncTerminate() { +int APS5_VABI sceOpusSilkEncTerminate(const std::uint32_t* contextId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceOpusSilkEnc.md b/docs/dev/debt/libSceOpusSilkEnc.md index 3e129c1e1f..266dfe90fd 100644 --- a/docs/dev/debt/libSceOpusSilkEnc.md +++ b/docs/dev/debt/libSceOpusSilkEnc.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceOpusSilkEncCancel, sceOpusSilkEncCreate, sceOpusSilkEncCreateEx, sceOpusSilkEncCtl, sceOpusSilkEncDestroy, sceOpusSilkEncEncode, sceOpusSilkEncEncodeFloat, sceOpusSilkEncEncodeFloatWithPriority, sceOpusSilkEncEncodeFloatWithPriorityAndTimeout, sceOpusSilkEncEncodeWithPriority, sceOpusSilkEncEncodeWithPriorityAndTimeout, sceOpusSilkEncGetSize, sceOpusSilkEncInitialize and sceOpusSilkEncTerminate](../../../core/libs/prx/libSceOpusSilkEnc/Export.cpp) (libSceOpusSilkEnc) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceOpusSilkEnc.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [sceOpusSilkEncTerminate](../../../core/libs/prx/libSceOpusSilkEnc/Export.cpp): the context-ID pointer is confirmed. The null-input path does not define the return register, so an integer return contract is not established. The current integer return declaration is retained because changing it to `void` would require altering the protected stub body, which contains `return 0`. From d356e6535b2a8bbf3a93431f4cd8270ee9967430 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:40:33 +0300 Subject: [PATCH 258/523] fix(automounter): restore usb and thread attribute arguments --- core/libs/prx/libSceAutoMounterClient/Export.cpp | 8 +++++--- docs/dev/debt/libSceAutoMounterClient.md | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAutoMounterClient/Export.cpp b/core/libs/prx/libSceAutoMounterClient/Export.cpp index 3b3e49adb8..e4845005e9 100644 --- a/core/libs/prx/libSceAutoMounterClient/Export.cpp +++ b/core/libs/prx/libSceAutoMounterClient/Export.cpp @@ -3,17 +3,19 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct AutoMounterUsbDeviceInfo; + extern "C" { -int APS5_VABI sceAutoMounterClientGetUsbDeviceInfo() { +int APS5_VABI sceAutoMounterClientGetUsbDeviceInfo(const char* deviceName, AutoMounterUsbDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAutoMounterClientGetUsbDeviceList() { +int APS5_VABI sceAutoMounterClientGetUsbDeviceList(char (*deviceNames)[64], std::int32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAutoMounterClientInit() { +int APS5_VABI sceAutoMounterClientInit(PthreadAttr* threadAttr) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAutoMounterClient.md b/docs/dev/debt/libSceAutoMounterClient.md index b7200004e3..1517732495 100644 --- a/docs/dev/debt/libSceAutoMounterClient.md +++ b/docs/dev/debt/libSceAutoMounterClient.md @@ -3,3 +3,4 @@ ### Unknown function info - [sceAutoMounterClientRegisterCallback, sceAutoMounterClientTerm and sceAutoMounterClientUnregisterCallback](../../../core/libs/prx/libSceAutoMounterClient/Export.cpp) (libSceAutoMounterClient) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAutoMounterClient.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. +- [sceAutoMounterClientGetUsbDeviceInfo](../../../core/libs/prx/libSceAutoMounterClient/Export.cpp): the name string and output pointer are confirmed. The output occupies 944 bytes, including a 128-byte device name and three variable-length byte arrays with 64-bit lengths, but the metadata field meanings remain unresolved. From 7149edc821b28a42b699a978ad62084d19ee388c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:40:39 +0300 Subject: [PATCH 259/523] fix(razorcpu): restore job event arguments and flush output behavior --- core/libs/prx/libSceRazorCpu/Export.cpp | 30 +++++++++++++++++-------- core/libs/tests/GuestRazorCpu.cpp | 16 ++++++------- docs/dev/debt/libSceRazorCpu.md | 12 +++------- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/core/libs/prx/libSceRazorCpu/Export.cpp b/core/libs/prx/libSceRazorCpu/Export.cpp index 751f920cd1..defd9d798b 100644 --- a/core/libs/prx/libSceRazorCpu/Export.cpp +++ b/core/libs/prx/libSceRazorCpu/Export.cpp @@ -9,18 +9,30 @@ uint32_t APS5_VABI sceRazorCpuIsCapturing(void) { return 0; } -int APS5_VABI sceRazorCpuJobManagerDispatch(const void* args) { - (void)args; +int APS5_VABI sceRazorCpuJobManagerDispatch(const char* label, uint16_t firstId, uint16_t secondId, uint16_t firstCount, uint16_t secondCount, uint64_t firstValue, uint64_t secondValue) { + (void)label; + (void)firstId; + (void)secondId; + (void)firstCount; + (void)secondCount; + (void)firstValue; + (void)secondValue; return 0; } -int APS5_VABI sceRazorCpuJobManagerJob(const void* args) { - (void)args; +int APS5_VABI sceRazorCpuJobManagerJob(uint16_t firstId, uint16_t secondId, uint16_t thirdId, uint16_t event) { + (void)firstId; + (void)secondId; + (void)thirdId; + (void)event; return 0; } -int APS5_VABI sceRazorCpuJobManagerSequence(const void* args) { - (void)args; +int APS5_VABI sceRazorCpuJobManagerSequence(const char* label, uint16_t firstId, uint16_t secondId, uint16_t event) { + (void)label; + (void)firstId; + (void)secondId; + (void)event; return 0; } @@ -47,9 +59,9 @@ int APS5_VABI sceRazorCpuPopMarker(void) { return 0; } -int APS5_VABI sceRazorCpuFlushOccurred(uint64_t* timeSpentInFlush) { - if (timeSpentInFlush != nullptr) *timeSpentInFlush = 0; - return 0; +bool APS5_VABI sceRazorCpuFlushOccurred(uint64_t* timeSpentInFlush) { + (void)timeSpentInFlush; + return false; } int APS5_VABI sceRazorCpuPlotValue(const char* series, float value) { diff --git a/core/libs/tests/GuestRazorCpu.cpp b/core/libs/tests/GuestRazorCpu.cpp index 13572d5feb..69f35dbfa0 100644 --- a/core/libs/tests/GuestRazorCpu.cpp +++ b/core/libs/tests/GuestRazorCpu.cpp @@ -7,14 +7,14 @@ extern "C" { std::uint32_t APS5_VABI sceRazorCpuIsCapturing(void); -int APS5_VABI sceRazorCpuJobManagerDispatch(const void* args); -int APS5_VABI sceRazorCpuJobManagerJob(const void* args); -int APS5_VABI sceRazorCpuJobManagerSequence(const void* args); +int APS5_VABI sceRazorCpuJobManagerDispatch(const char* label, std::uint16_t firstId, std::uint16_t secondId, std::uint16_t firstCount, std::uint16_t secondCount, std::uint64_t firstValue, std::uint64_t secondValue); +int APS5_VABI sceRazorCpuJobManagerJob(std::uint16_t firstId, std::uint16_t secondId, std::uint16_t thirdId, std::uint16_t event); +int APS5_VABI sceRazorCpuJobManagerSequence(const char* label, std::uint16_t firstId, std::uint16_t secondId, std::uint16_t event); int APS5_VABI sceRazorCpuPushMarker(const char* label, std::uint32_t color, std::uint32_t flags); int APS5_VABI sceRazorCpuNamedSync(const char* label); int APS5_VABI sceRazorCpuPushMarkerStatic(const char* name, std::uint32_t color, std::uint32_t flags); int APS5_VABI sceRazorCpuPopMarker(void); -int APS5_VABI sceRazorCpuFlushOccurred(std::uint64_t* timeSpentInFlush); +bool APS5_VABI sceRazorCpuFlushOccurred(std::uint64_t* timeSpentInFlush); int APS5_VABI sceRazorCpuPlotValue(const char* series, float value); int APS5_VABI sceRazorCpuWriteBookmark(const char* label, const char* description); int APS5_VABI sceRazorCpuSync(void); @@ -38,9 +38,9 @@ void Require(bool value) { if (!value) std::abort(); } int main() { Require(sceRazorCpuIsCapturing() == 0); - Require(sceRazorCpuJobManagerDispatch(nullptr) == 0); - Require(sceRazorCpuJobManagerJob(nullptr) == 0); - Require(sceRazorCpuJobManagerSequence(nullptr) == 0); + Require(sceRazorCpuJobManagerDispatch(nullptr, 1, 2, 3, 4, 5, 6) == 0); + Require(sceRazorCpuJobManagerJob(1, 2, 3, 16) == 0); + Require(sceRazorCpuJobManagerSequence(nullptr, 1, 2, 3) == 0); Require(sceRazorCpuNamedSync("frame") == 0); Require(sceRazorCpuPushMarkerStatic("outer", 0x80ffffffu, 2) == 0); Require(sceRazorCpuPushMarkerStatic("inner", 0x80ffffffu, 2) == 0); @@ -52,7 +52,7 @@ int main() { Require(sceRazorCpuIsCapturing() == 0); std::uint64_t timeSpentInFlush = 0x123456789abcdef0ull; Require(sceRazorCpuFlushOccurred(&timeSpentInFlush) == 0); - Require(timeSpentInFlush == 0); + Require(timeSpentInFlush == 0x123456789abcdef0ull); Require(sceRazorCpuFlushOccurred(nullptr) == 0); Require(sceRazorCpuPlotValue("read time (ms)", 1.5f) == 0); Require(sceRazorCpuWriteBookmark("read timeout", "lba=0x10") == 0); diff --git a/docs/dev/debt/libSceRazorCpu.md b/docs/dev/debt/libSceRazorCpu.md index f6f5690d75..fed20752ee 100644 --- a/docs/dev/debt/libSceRazorCpu.md +++ b/docs/dev/debt/libSceRazorCpu.md @@ -1,18 +1,12 @@ # libSceRazorCpu technical debt -### Silent stubs +### Functional -- [sceRazorCpuPushMarker](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signature (label, color, flags) from [SharpProspero](https://github.com/SvenGDK/SharpProspero), the same as sceRazorCpuPushMarkerStatic's except that the label is copied; it does nothing without a capture and returns 0 -- [sceRazorCpuSync](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - the `int(void)` signature and its meaning, an unnamed frame-boundary event, come from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Debug/RazorCpu.cs#L119-L122). Without a capture there is no event to record, so it does nothing and returns 0, like the other `sceRazorCpu*` markers here and the [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/razor_cpu/razor_cpu.cpp#L143-L146) stub. PRAGMATA (PPSA02530) continues on 0; the console's result with no capture attached is unverified -- [sceRazorCpuDisableFiberUserMarkers, sceRazorCpuBeginLogicalFileAccess and sceRazorCpuEndLogicalFileAccess](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signatures and meanings from SharpProspero ([fiber markers](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Debug/RazorCpu.cs#L73-L79), [logical file access](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Debug/RazorCpu.cs#L189-L209)): the first makes later markers carry the thread instead of the fiber, the other two attribute the thread's file reads and writes to a named logical file. Both only change what a capture records, so without one they do nothing, and the two file-access calls return 0 for any arguments. Whether the console checks the operation (0 read, 1 write) or an unmatched end without a capture is unverified -- [sceRazorCpuInitDataTags, sceRazorCpuShutdownDataTags, sceRazorCpuTagBuffer, sceRazorCpuTagArray, sceRazorCpuResizeTaggedBuffer and sceRazorCpuUnTagBuffer](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signatures from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Debug/RazorCpu.cs#L130-L187), which documents them as the data sampler's buffer-tag table and the tags in it. Without a capture there is no data sampler, so they neither read the caller's storage nor keep the tags, and return 0 for any arguments; the console's errors for a tag before `sceRazorCpuInitDataTags`, an unknown buffer or a full table are unverified +- [libSceRazorCpu](../../../core/libs/prx/libSceRazorCpu/Export.cpp): capture-controller registration, thread-local capture storage, marker serialization and flushing are not implemented. The host implements only the confirmed uninitialized-profiler path: markers return zero without reading labels, and `sceRazorCpuFlushOccurred` returns false without writing its optional output. ### Unknown function info - [sceRazorCpuFiberLogNameChange, sceRazorCpuFiberSwitch, sceRazorCpuGpuMarkerSync, sceRazorCpuInitializeGpuMarkerContext, sceRazorCpuInitializeInternal, sceRazorCpuSetPopMarkerCallback](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - the list is the intersection of the export table of the console's [libSceRazorCpu](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRazorCpu.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. - [sceRazorCpuSetPushMarkerCallback, sceRazorCpuSetPushMarkerStaticCallback, sceRazorCpuStartCaptureInternal, sceRazorCpuStopCaptureInternal, sceRazorCpuWorkloadRunBegin, sceRazorCpuWorkloadRunEnd, sceRazorCpuWorkloadSubmit](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - the list is the intersection of the export table of the console's [libSceRazorCpu](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRazorCpu.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. -- [sceRazorCpuFlushOccurred](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signature and return values (1 = flush since the last call, 0 = none) from [SharpProspero](https://github.com/SvenGDK/SharpProspero); without a capture nothing is flushed, so it returns 0 and writes 0 cycles to the optional out pointer (whether the real library writes it when nothing was flushed is unknown) -- [sceRazorCpuPushMarkerStatic, sceRazorCpuPopMarker](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signatures from PPSA26344's calls; (name, color, flags) assumed for the push; both do nothing without a capture -- [sceRazorCpuPlotValue, sceRazorCpuWriteBookmark](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - signatures (series, float value) and (label, optional description) from [SharpProspero](https://github.com/SvenGDK/SharpProspero), matching PPSA06320's calls; both do nothing without a capture and return 0 -- [sceRazorCpuNamedSync](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - NID `XCuZoBSVFG8` is named by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/razor_cpu/razor_cpu.cpp); the NID and a stub returning 0 come from there, but its stub declares no arguments; the `const char* label` parameter (NUL-terminated, up to 16384 bytes) comes from [SharpProspero](https://github.com/SvenGDK/SharpProspero), which documents it as the frame-boundary label. Without a capture it does nothing and returns 0; the label is not read, and the capture behavior and the real return values are unverified +- [sceRazorCpuJobManagerDispatch, sceRazorCpuJobManagerJob and sceRazorCpuJobManagerSequence](../../../core/libs/prx/libSceRazorCpu/Export.cpp): argument positions and serialized widths are recovered from machine instructions. Dispatch takes seven arguments, including two 64-bit payloads whose upper 16 bits are replaced by the count fields. The payloads are not dereferenced, so their original pointer-versus-integer types and the semantic names of the event identifiers remain unresolved. Job and Sequence serialize 16-bit identifiers and an event selector; their source enum names are unavailable. - [sceRazorCpuGetDataTagStorageSize](../../../core/libs/prx/libSceRazorCpu/Export.cpp) (libSceRazorCpu) - throws: [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Debug/RazorCpu.cs#L142-L144) documents it as the byte size a tag table needs for a count of entries, but not the size of an entry From 57ee1e839e9971146232d142b67f58b02b277e15 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:43:56 +0300 Subject: [PATCH 260/523] fix(opuscelt): verify decoder abi and const context inputs --- core/libs/prx/libSceOpusCeltDec/Export.cpp | 4 ++-- core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp | 4 ++-- core/libs/tests/GuestOpusCeltDec.cpp | 4 ++-- docs/dev/debt/libSceOpusCeltDec.md | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceOpusCeltDec/Export.cpp b/core/libs/prx/libSceOpusCeltDec/Export.cpp index 2b04cca7be..5f40c01b3c 100644 --- a/core/libs/prx/libSceOpusCeltDec/Export.cpp +++ b/core/libs/prx/libSceOpusCeltDec/Export.cpp @@ -13,11 +13,11 @@ extern "C" { int APS5_VABI sceOpusCeltDecInitialize(std::uint32_t* context) { return library.Initialize(context); } -int APS5_VABI sceOpusCeltDecTerminate(std::uint32_t* context) { return library.Terminate(context); } +int APS5_VABI sceOpusCeltDecTerminate(const std::uint32_t* context) { return library.Terminate(context); } int APS5_VABI sceOpusCeltDecGetSize(int channels) { return library.GetSize(channels); } -int APS5_VABI sceOpusCeltDecCreateEx(std::uint32_t* context, void* state, int sampleRate, int channels) { +int APS5_VABI sceOpusCeltDecCreateEx(const std::uint32_t* context, void* state, int sampleRate, int channels) { return library.CreateEx(context, state, sampleRate, channels); } diff --git a/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp b/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp index eef8d86d52..818381ea7c 100644 --- a/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp +++ b/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp @@ -38,7 +38,7 @@ public: return 0; } - int Terminate(std::uint32_t* context) { + int Terminate(const std::uint32_t* context) { if (!context) fail("null context"); std::lock_guard lock(mutex); if (!contexts.contains(*context)) fail("unknown context"); @@ -53,7 +53,7 @@ public: return 640; } - int CreateEx(std::uint32_t* context, void* state, int sampleRate, int channels) { + int CreateEx(const std::uint32_t* context, void* state, int sampleRate, int channels) { if (!context || !state) fail("null context or state"); GetSize(channels); if (sampleRate != 8000 && sampleRate != 12000 && sampleRate != 16000 && sampleRate != 24000 && sampleRate != 48000) diff --git a/core/libs/tests/GuestOpusCeltDec.cpp b/core/libs/tests/GuestOpusCeltDec.cpp index aa3572b7e1..d6422e910b 100644 --- a/core/libs/tests/GuestOpusCeltDec.cpp +++ b/core/libs/tests/GuestOpusCeltDec.cpp @@ -7,9 +7,9 @@ extern "C" { int APS5_VABI sceOpusCeltDecInitialize(std::uint32_t*); -int APS5_VABI sceOpusCeltDecTerminate(std::uint32_t*); +int APS5_VABI sceOpusCeltDecTerminate(const std::uint32_t*); int APS5_VABI sceOpusCeltDecGetSize(int); -int APS5_VABI sceOpusCeltDecCreateEx(std::uint32_t*, void*, int, int); +int APS5_VABI sceOpusCeltDecCreateEx(const std::uint32_t*, void*, int, int); int APS5_VABI sceOpusCeltDecDecode(void*, const std::uint8_t*, int, std::int16_t*, int); int APS5_VABI sceOpusCeltDecDestroy(void*); } diff --git a/docs/dev/debt/libSceOpusCeltDec.md b/docs/dev/debt/libSceOpusCeltDec.md index 66de7596b2..0c94aeb226 100644 --- a/docs/dev/debt/libSceOpusCeltDec.md +++ b/docs/dev/debt/libSceOpusCeltDec.md @@ -3,8 +3,9 @@ ### Unknown function info - [sceOpusCeltDecCreate, sceOpusCeltDecCtl, sceOpusCeltDecDecodeFloat, sceOpusCeltDecDecodeFloatWithPriority, sceOpusCeltDecDecodeWithPriority](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp) (libSceOpusCeltDec) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceOpusCeltDec.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. -- [sceOpusCeltDecCreateEx, sceOpusCeltDecDecode, sceOpusCeltDecDestroy, sceOpusCeltDecGetSize, sceOpusCeltDecInitialize, sceOpusCeltDecTerminate](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp) (libSceOpusCeltDec) - imported by PPSA99001. Their signatures are assumed to match libSceOpusDec's in [ProsperoRadio](https://github.com/blackbearreloaded/ProsperoRadio/blob/2c4029c3a8f43e9e1dfef98374d94d99b14d745d/src/opus_decoder.cpp), and the 640-byte state size, context handling and byte-count return are taken from [libSceOpusDec](../../../core/libs/prx/libSceOpusDec/Export.cpp); none of them was measured on a console. PPSA99001 opens this decoder only as a fallback when libSceOpusDec reports a retryable error for a CELT packet, which libSceOpusDec here never returns, so the fallback is not reached +- [sceOpusCeltDecTerminate](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp): the non-null path forwards the AJM finalization result, but the null path returns without setting the return register. Its source return type cannot be established from this callee alone. The existing integer return is retained. ### Functional -- [sceOpusCeltDecDecode](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp) (libSceOpusCeltDec) decodes with the FFmpeg decoder of [libSceOpusDec](../../../core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp) and shares its limits. Packets whose TOC configuration is below 16 (SILK and hybrid) throw: whether the console decodes them or returns an error code is unknown. Contexts and decoder states are separate from libSceOpusDec's +- [libSceOpusCeltDec](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp): the supplied module confirms the six exported argument lists, read-only context inputs, 640-byte state allocation, mono/stereo channels, five sample rates, byte-sized decode capacity and byte-count return. The implementation still keeps decoder state outside guest memory and uses host contexts instead of AJM contexts. Original error codes, AJM resource ownership and termination with live decoders are not implemented. +- [sceOpusCeltDecDecode](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp): the original forwards packets to AJM codec 16 without a local CELT-only check. The backend response to SILK/hybrid packets is unresolved; these inputs still throw. Packet loss concealment, asynchronous AJM execution, exact PCM conversion and resampler buffering remain outside the verified behavior of the FFmpeg implementation in [OpusDecoderLibrary](../../../core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp). From 31935cc859f8de8794706898dd40b3b13fd8056b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:43:59 +0300 Subject: [PATCH 261/523] fix(discmap): restore bitmap output widths --- core/libs/prx/libSceDiscMap/Export.cpp | 2 +- core/libs/tests/GuestDiscMap.cpp | 2 +- docs/dev/debt/libSceDiscMap.md | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceDiscMap/Export.cpp b/core/libs/prx/libSceDiscMap/Export.cpp index 80f2a899dc..8fedde6365 100644 --- a/core/libs/prx/libSceDiscMap/Export.cpp +++ b/core/libs/prx/libSceDiscMap/Export.cpp @@ -19,7 +19,7 @@ std::int32_t APS5_VABI sceDiscMapIsRequestOnHDD(const char* path, std::int64_t o } APS5_EXPORT("ioKMruft1ek", sceDiscMapUnknown_ioKMruft1ek); -std::int32_t APS5_VABI sceDiscMapUnknown_ioKMruft1ek(const char* path, std::int64_t offset, std::int64_t size, void* flags, void* out1, void* out2) { +std::int32_t APS5_VABI sceDiscMapUnknown_ioKMruft1ek(const char* path, std::int64_t offset, std::int64_t size, std::uint64_t* flags, std::uint64_t* out1, std::uint64_t* out2) { (void)path; (void)offset; (void)size; diff --git a/core/libs/tests/GuestDiscMap.cpp b/core/libs/tests/GuestDiscMap.cpp index 41f711baff..498dc7c443 100644 --- a/core/libs/tests/GuestDiscMap.cpp +++ b/core/libs/tests/GuestDiscMap.cpp @@ -6,7 +6,7 @@ extern "C" { std::int32_t APS5_VABI sceDiscMapIsRequestOnHDD(const char* path, std::int64_t offset, std::int64_t size, std::int32_t* result); -std::int32_t APS5_VABI sceDiscMapUnknown_ioKMruft1ek(const char* path, std::int64_t offset, std::int64_t size, void* flags, void* out1, void* out2); +std::int32_t APS5_VABI sceDiscMapUnknown_ioKMruft1ek(const char* path, std::int64_t offset, std::int64_t size, std::uint64_t* flags, std::uint64_t* out1, std::uint64_t* out2); } namespace { diff --git a/docs/dev/debt/libSceDiscMap.md b/docs/dev/debt/libSceDiscMap.md index 4fcd7700ec..b058be4747 100644 --- a/docs/dev/debt/libSceDiscMap.md +++ b/docs/dev/debt/libSceDiscMap.md @@ -1,10 +1,9 @@ # libSceDiscMap technical debt -### Silent stubs +### Functional -- [libSceDiscMap](../../../core/libs/prx/libSceDiscMap/Export.cpp) implements only the state with no disc bitmap: `sceDiscMapIsRequestOnHDD` and `ioKMruft1ek` return `SCE_DISC_MAP_ERROR_NO_BITMAP_INFO` (0x81100004) for every request and write nothing. CUSA02636's bundled `libSceFios2.prx` imports both; what it does with the error is not known (shadPS4 returns the same, and [shadps4-game-compatibility #617](https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/617) lists CUSA02636 as in-game) +- [libSceDiscMap](../../../core/libs/prx/libSceDiscMap/Export.cpp): loading disc bitmap metadata and resolving file byte ranges to bitmap blocks are not implemented. Both exports implement the confirmed no-bitmap path: return `SCE_DISC_MAP_ERROR_NO_BITMAP_INFO` before validating any argument and leave every output unchanged. The supplied module confirms signed 64-bit byte offset/size, a 32-bit `IsRequestOnHDD` result and three 64-bit outputs for `ioKMruft1ek`. The latter outputs describe the bitmap selector, first block and block count. Its exported source name remains unavailable. ### Unknown function info - [sceDiscMapGetPackageSize](../../../core/libs/prx/libSceDiscMap/Export.cpp) (libSceDiscMap) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceDiscMap.c); the `(int64_t, int*, int*)` prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/disc_map/disc_map.cpp#L12), is not measured on a console, and every call throws -- [sceDiscMapIsRequestOnHDD, sceDiscMapUnknown_ioKMruft1ek](../../../core/libs/prx/libSceDiscMap/Export.cpp) (libSceDiscMap, NIDs `lbQKqsERhtE` and `ioKMruft1ek`) - `ioKMruft1ek` has no known name. The `(path, offset, size, result)` and `(path, offset, size, flags, out1, out2)` signatures follow [shadPS4 disc_map.cpp](https://github.com/shadps4-emu/shadPS4/blob/b2d7ec7d31f9ad9426db93afe079db8679b3e1e0/src/core/libraries/disc_map/disc_map.cpp); the widths of the `ioKMruft1ek` outputs are unknown (shadPS4 `int*`, [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/src/ps4_libscediscmap.pas) `PInt64`), so they are `void*`. The error code is the one both return; [shadPS4 #2686](https://github.com/shadps4-emu/shadPS4/pull/2686) reads the console library as checking an initialisation global first in each call and returning it while the global is clear. Not measured on a console From 41035b4a0eeb9ac4255c21cb73e9544ff9f5a25d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:45:27 +0300 Subject: [PATCH 262/523] fix(gameupdate): reject unsupported service operations explicitly --- core/libs/prx/libSceGameUpdate/Export.cpp | 54 ++++++++++------------- core/libs/tests/GuestGameUpdate.cpp | 32 +++++++++----- docs/dev/debt/libSceGameUpdate.md | 7 +-- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/core/libs/prx/libSceGameUpdate/Export.cpp b/core/libs/prx/libSceGameUpdate/Export.cpp index 37c0901309..f8527db000 100644 --- a/core/libs/prx/libSceGameUpdate/Export.cpp +++ b/core/libs/prx/libSceGameUpdate/Export.cpp @@ -1,11 +1,10 @@ #include #include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" -// Update checks need the network, which is not available; requests fail so the game skips the check. -static constexpr int SCE_GAME_UPDATE_ERROR_OFFLINE = static_cast(0x80D50001); static constexpr int SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED = static_cast(0x80412801); static constexpr int SCE_GAME_UPDATE_ERROR_ALREADY_INITIALIZED = static_cast(0x80412802); static constexpr int SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT = static_cast(0x80412803); @@ -15,19 +14,19 @@ static constexpr int SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND = static_cast( static_assert(sizeof(GameUpdateCheckParam) == 0x30); static_assert(sizeof(GameUpdateCheckResult) == 0x30); -static std::atomic g_initialized{false}; +static std::atomic initialized{false}; -static int CheckRequestId(int request_id) { - if (request_id <= 0) { +static int checkRequestId(int requestId) { + if (requestId <= 0) { return SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND; } - if (!g_initialized.load()) { + if (!initialized.load()) { return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED; } return SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND; } -static bool HasNonZeroReserved(const GameUpdateCheckParam* param) { +static bool hasNonZeroReserved(const GameUpdateCheckParam* param) { for (std::uint32_t word : param->reserved) { if (word != 0) { return true; @@ -38,12 +37,12 @@ static bool HasNonZeroReserved(const GameUpdateCheckParam* param) { extern "C" { -int APS5_VABI sceGameUpdateAbortRequest(int request_id) { - return CheckRequestId(request_id); +int APS5_VABI sceGameUpdateAbortRequest(int requestId) { + return checkRequestId(requestId); } -int APS5_VABI sceGameUpdateCheck(int request_id, const GameUpdateCheckParam* param, GameUpdateCheckResult* result) { - if (request_id <= 0) { +int APS5_VABI sceGameUpdateCheck(int requestId, const GameUpdateCheckParam* param, GameUpdateCheckResult* result) { + if (requestId <= 0) { return SCE_GAME_UPDATE_ERROR_REQUEST_NOT_FOUND; } if (param == nullptr || result == nullptr) { @@ -52,53 +51,46 @@ int APS5_VABI sceGameUpdateCheck(int request_id, const GameUpdateCheckParam* par if (param->size != sizeof(GameUpdateCheckParam) || result->size != sizeof(GameUpdateCheckResult)) { return SCE_GAME_UPDATE_ERROR_INVALID_SIZE; } - if (param->option != 0 || HasNonZeroReserved(param)) { + if (param->option != 0 || hasNonZeroReserved(param)) { return SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT; } - return CheckRequestId(request_id); + return checkRequestId(requestId); } int APS5_VABI sceGameUpdateCreateRequest(void) { - if (!g_initialized.load()) { + if (!initialized.load()) { return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED; } - return SCE_GAME_UPDATE_ERROR_OFFLINE; + throw std::runtime_error("sceGameUpdateCreateRequest: patch service request creation is not implemented"); } -int APS5_VABI sceGameUpdateDeleteRequest(int request_id) { - return CheckRequestId(request_id); +int APS5_VABI sceGameUpdateDeleteRequest(int requestId) { + return checkRequestId(requestId); } -int APS5_VABI sceGameUpdateGetAddcontLatestVersion(uint32_t service_label, const void* entitlement_label, GameUpdateAddcontVersionInfo* info) { - (void)service_label; +int APS5_VABI sceGameUpdateGetAddcontLatestVersion(uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, GameUpdateAddcontVersionInfo* info) { + (void)serviceLabel; if (info == nullptr) { return SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT; } - if (entitlement_label == nullptr) { + if (entitlementLabel == nullptr) { return SCE_GAME_UPDATE_ERROR_INVALID_ARGUMENT; } - if (!g_initialized.load()) { + if (!initialized.load()) { return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED; } - if (info->size < sizeof(GameUpdateAddcontVersionInfo)) { - return SCE_GAME_UPDATE_ERROR_INVALID_SIZE; - } - const std::size_t size = info->size; - *info = {}; - info->size = size; - info->found = false; - return 0; + throw std::runtime_error("sceGameUpdateGetAddcontLatestVersion: entitlement version lookup is not implemented"); } int APS5_VABI sceGameUpdateInitialize(void) { - if (g_initialized.exchange(true)) { + if (initialized.exchange(true)) { return SCE_GAME_UPDATE_ERROR_ALREADY_INITIALIZED; } return 0; } int APS5_VABI sceGameUpdateTerminate(void) { - if (!g_initialized.exchange(false)) { + if (!initialized.exchange(false)) { return SCE_GAME_UPDATE_ERROR_NOT_INITIALIZED; } return 0; diff --git a/core/libs/tests/GuestGameUpdate.cpp b/core/libs/tests/GuestGameUpdate.cpp index 13fcac85c2..c46d451c83 100644 --- a/core/libs/tests/GuestGameUpdate.cpp +++ b/core/libs/tests/GuestGameUpdate.cpp @@ -9,7 +9,7 @@ extern "C" { int APS5_VABI sceGameUpdateInitialize(void); int APS5_VABI sceGameUpdateTerminate(void); -int APS5_VABI sceGameUpdateGetAddcontLatestVersion(std::uint32_t, const void*, GameUpdateAddcontVersionInfo*); +int APS5_VABI sceGameUpdateGetAddcontLatestVersion(std::uint32_t, const NpUnifiedEntitlementLabel*, GameUpdateAddcontVersionInfo*); int APS5_VABI sceGameUpdateCreateRequest(void); int APS5_VABI sceGameUpdateCheck(int, const GameUpdateCheckParam*, GameUpdateCheckResult*); int APS5_VABI sceGameUpdateAbortRequest(int); @@ -18,6 +18,16 @@ int APS5_VABI sceGameUpdateDeleteRequest(int); static void Require(bool value) { if (!value) std::abort(); } +template +static void RequireThrows(TFunction function) { + try { + function(); + } catch (const std::runtime_error&) { + return; + } + std::abort(); +} + static void TestRequestArguments(int notInitialized, int invalidArgument, int invalidSize, int notFound) { GameUpdateCheckParam param{}; param.size = sizeof(param); @@ -63,7 +73,7 @@ int main() { constexpr int invalidSize = static_cast(0x80412804); static_assert(sizeof(GameUpdateAddcontVersionInfo) == 0x30); - const std::uint8_t label[16] = {}; + const NpUnifiedEntitlementLabel label{}; GameUpdateAddcontVersionInfo info{}; info.size = sizeof(info); @@ -73,27 +83,27 @@ int main() { Require(sceGameUpdateTerminate() == notInitialized); TestRequestArguments(notInitialized, invalidArgument, invalidSize, notFound); - Require(sceGameUpdateGetAddcontLatestVersion(0, label, &info) == notInitialized); - Require(sceGameUpdateGetAddcontLatestVersion(0, label, nullptr) == invalidArgument); + Require(sceGameUpdateGetAddcontLatestVersion(0, &label, &info) == notInitialized); + Require(sceGameUpdateGetAddcontLatestVersion(0, &label, nullptr) == invalidArgument); Require(sceGameUpdateGetAddcontLatestVersion(0, nullptr, &info) == invalidArgument); Require(sceGameUpdateInitialize() == 0); Require(sceGameUpdateInitialize() == alreadyInitialized); - Require(sceGameUpdateGetAddcontLatestVersion(0, label, nullptr) == invalidArgument); + Require(sceGameUpdateGetAddcontLatestVersion(0, &label, nullptr) == invalidArgument); Require(sceGameUpdateGetAddcontLatestVersion(0, nullptr, &info) == invalidArgument); info.size = sizeof(info) - 1; - Require(sceGameUpdateGetAddcontLatestVersion(0, label, &info) == invalidSize); + RequireThrows([&] { sceGameUpdateGetAddcontLatestVersion(0, &label, &info); }); std::memset(&info, 0xAB, sizeof(info)); info.size = sizeof(info) + 8; - Require(sceGameUpdateGetAddcontLatestVersion(0, label, &info) == 0); - Require(info.size == sizeof(info) + 8 && !info.found); - const auto* bytes = reinterpret_cast(&info); - for (std::size_t i = sizeof(info.size); i < sizeof(info); ++i) Require(bytes[i] == 0); + const auto original = info; + RequireThrows([&] { sceGameUpdateGetAddcontLatestVersion(0, &label, &info); }); + Require(std::memcmp(&info, &original, sizeof(info)) == 0); + RequireThrows([] { sceGameUpdateCreateRequest(); }); Require(sceGameUpdateTerminate() == 0); Require(sceGameUpdateTerminate() == notInitialized); info.size = sizeof(info); - Require(sceGameUpdateGetAddcontLatestVersion(0, label, &info) == notInitialized); + Require(sceGameUpdateGetAddcontLatestVersion(0, &label, &info) == notInitialized); } diff --git a/docs/dev/debt/libSceGameUpdate.md b/docs/dev/debt/libSceGameUpdate.md index c67a4a14a3..fd8f0f341a 100644 --- a/docs/dev/debt/libSceGameUpdate.md +++ b/docs/dev/debt/libSceGameUpdate.md @@ -1,6 +1,7 @@ # libSceGameUpdate technical debt -### Unknown function info +### Functional -- [sceGameUpdateGetAddcontLatestVersion](../../../core/libs/prx/libSceGameUpdate/Export.cpp) (libSceGameUpdate) - a null `entitlement_label` returns `0x80412803` as in prosper; KytyPS5 accepts it instead, and neither is confirmed on a console. Bytes after `found` are zeroed as in KytyPS5; prosper leaves them untouched -- [sceGameUpdateCreateRequest, sceGameUpdateCheck, sceGameUpdateAbortRequest, sceGameUpdateDeleteRequest](../../../core/libs/prx/libSceGameUpdate/Export.cpp) (libSceGameUpdate) - an initialized `sceGameUpdateCreateRequest` returns `0x80D50001` and creates no request, so `sceGameUpdateCheck`, `sceGameUpdateAbortRequest` and `sceGameUpdateDeleteRequest` answer `0x80412805` for every id that passes their argument checks. The PS5 3.20 module, as read by prosper, keeps 8 requests with ids from `0x20000000`, and answers "no update" when its patch-check service reports `0x80412883`; what that service returns on an offline console is unknown. Closes when: measured on a PS5 +- [libSceGameUpdate](../../../core/libs/prx/libSceGameUpdate/Export.cpp): initialization tracks host state but does not connect the patch service. Request creation and entitlement version lookup throw instead of fabricating an offline error or a successful no-update result. The original maintains eight service-backed requests with IDs starting at `0x20000000`; the service's offline response is still unknown. Check, Abort and Delete implement argument/state checks but have no live requests to operate on. +- [sceGameUpdateCheck](../../../core/libs/prx/libSceGameUpdate/Export.cpp): option zero is supported for argument validation. The original additionally permits option one when a kernel platform predicate succeeds; that platform mode is not modeled. +- [sceGameUpdateGetAddcontLatestVersion](../../../core/libs/prx/libSceGameUpdate/Export.cpp): the supplied module confirms a 20-byte entitlement-label input and a 48-byte service output. Null pointers are rejected before initialization is checked. There is no local output-size validation or blanket clearing of the output; the host leaves it untouched when the missing service causes an exception. From b276137d2a05c53216b9eae3e008e1ccf79f9796 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:45:36 +0300 Subject: [PATCH 263/523] docs: classify razor cpu uninitialized capture stubs --- docs/dev/debt/libSceRazorCpu.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceRazorCpu.md b/docs/dev/debt/libSceRazorCpu.md index fed20752ee..092aa68738 100644 --- a/docs/dev/debt/libSceRazorCpu.md +++ b/docs/dev/debt/libSceRazorCpu.md @@ -1,6 +1,6 @@ # libSceRazorCpu technical debt -### Functional +### Silent stubs - [libSceRazorCpu](../../../core/libs/prx/libSceRazorCpu/Export.cpp): capture-controller registration, thread-local capture storage, marker serialization and flushing are not implemented. The host implements only the confirmed uninitialized-profiler path: markers return zero without reading labels, and `sceRazorCpuFlushOccurred` returns false without writing its optional output. From 0996b7523827c625383119070f43b7ceb68e0b03 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:48:32 +0300 Subject: [PATCH 264/523] fix(voiceqos): restore endpoint slots and attribute validation --- core/libs/prx/libSceVoiceQoS/Export.cpp | 133 ++++++++++++------------ core/libs/tests/GuestVoiceQoS.cpp | 53 +++++----- docs/dev/debt/libSceVoiceQoS.md | 9 +- 3 files changed, 98 insertions(+), 97 deletions(-) diff --git a/core/libs/prx/libSceVoiceQoS/Export.cpp b/core/libs/prx/libSceVoiceQoS/Export.cpp index 3e02a10932..6c52605b5e 100644 --- a/core/libs/prx/libSceVoiceQoS/Export.cpp +++ b/core/libs/prx/libSceVoiceQoS/Export.cpp @@ -1,12 +1,10 @@ #include #include #include -#include -#include +#include #include #include #include -#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" @@ -17,9 +15,10 @@ constexpr std::int32_t VOICE_QOS_APP_TYPE_10000000 = 0x10000000; constexpr std::uint32_t VOICE_QOS_MEMORY_SIZE = 0x40000; constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_NOT_INIT = static_cast(0x804E0901); constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID = static_cast(0x804E0902); +constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT = static_cast(0x804E0907); constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_INITIALIZED = static_cast(0x804E0905); -using Endpoint = std::map>; +struct Endpoint {}; struct Connection { std::int32_t local; @@ -29,7 +28,7 @@ struct Connection { struct QoS { std::mutex mutex; bool initialized = false; - std::int32_t nextId = 1; + std::int32_t endpointLimit = 0; std::map locals; std::map remotes; std::map connections; @@ -40,53 +39,25 @@ QoS& State() { return qos; } -std::int32_t NewId(QoS& qos) { - const std::int32_t id = qos.nextId; - qos.nextId = qos.nextId == std::numeric_limits::max() ? 1 : qos.nextId + 1; - return id; -} - -int CreateEndpoint(std::int32_t* id, std::map& endpoints, QoS& qos) { +int CreateEndpoint(std::int32_t* id, std::map& endpoints, std::int32_t limit) { if (!id) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; - *id = NewId(qos); - endpoints.emplace(*id, Endpoint{}); - return 0; + for (std::int32_t candidate = 0; candidate < limit; ++candidate) { + if (!endpoints.contains(candidate)) { + endpoints.emplace(candidate, Endpoint{}); + *id = candidate; + return 0; + } + } + *id = -1; + return SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT; } int DeleteEndpoint(std::int32_t id, std::map& endpoints) { return endpoints.erase(id) != 0 ? 0 : SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; } -int SetAttribute(std::map& endpoints, std::int32_t id, std::int32_t attribute, const void* value, std::int32_t size) { - const auto endpoint = endpoints.find(id); - if (endpoint == endpoints.end() || !value) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; - if (size > 0) { - const auto* bytes = static_cast(value); - endpoint->second[attribute].assign(bytes, bytes + size); - } else { - endpoint->second[attribute].clear(); - } - return 0; -} - -int GetAttribute(std::map& endpoints, std::int32_t id, std::int32_t attribute, void* value, std::int32_t size) { - const auto endpoint = endpoints.find(id); - if (endpoint == endpoints.end() || !value || size <= 0) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; - const auto attr = endpoint->second.find(attribute); - if (attr != endpoint->second.end() && !attr->second.empty()) { - const auto toCopy = std::min(static_cast(size), attr->second.size()); - std::memcpy(value, attr->second.data(), toCopy); - if (static_cast(size) > toCopy) { - std::memset(static_cast(value) + toCopy, 0, static_cast(size) - toCopy); - } - } else { - std::memset(value, 0, static_cast(size)); - } - return 0; -} - -template -int Locked(Body&& body) { +template +int Locked(TBody&& body) { auto& qos = State(); std::lock_guard lock(qos.mutex); if (!qos.initialized) return SCE_VOICE_ERROR_LIBVOICEQOS_NOT_INIT; @@ -102,6 +73,7 @@ int APS5_VABI sceVoiceQoSInit(void* memBlock, uint32_t memSize, int32_t appType) std::lock_guard lock(qos.mutex); if (qos.initialized) return SCE_VOICE_ERROR_LIBVOICEQOS_INITIALIZED; if (!memBlock || memSize != VOICE_QOS_MEMORY_SIZE || (appType != VOICE_QOS_APP_TYPE_GAME && appType != VOICE_QOS_APP_TYPE_10000000)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + qos.endpointLimit = appType == VOICE_QOS_APP_TYPE_10000000 ? 15 : 7; qos.initialized = true; return 0; } @@ -117,10 +89,15 @@ int APS5_VABI sceVoiceQoSEnd() { } int APS5_VABI sceVoiceQoSCreateLocalEndpoint(int32_t* localId, int32_t userId, int32_t deviceInId, int32_t deviceOutId) { - (void)userId; (void)deviceInId; (void)deviceOutId; - return Locked([&](QoS& qos) { return CreateEndpoint(localId, qos.locals, qos); }); + return Locked([&](QoS& qos) { + if (localId && userId == -1) { + *localId = -1; + return SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT; + } + return CreateEndpoint(localId, qos.locals, 1); + }); } int APS5_VABI sceVoiceQoSDeleteLocalEndpoint(int32_t localId) { @@ -128,24 +105,45 @@ int APS5_VABI sceVoiceQoSDeleteLocalEndpoint(int32_t localId) { } int APS5_VABI sceVoiceQoSCreateRemoteEndpoint(int32_t* remoteId) { - return Locked([&](QoS& qos) { return CreateEndpoint(remoteId, qos.remotes, qos); }); + return Locked([&](QoS& qos) { return CreateEndpoint(remoteId, qos.remotes, qos.endpointLimit); }); } int APS5_VABI sceVoiceQoSDeleteRemoteEndpoint(int32_t remoteId) { - return Locked([&](QoS& qos) { return DeleteEndpoint(remoteId, qos.remotes); }); -} - -int APS5_VABI sceVoiceQoSConnect(int32_t* connectionId, int32_t localId, int32_t remoteId) { return Locked([&](QoS& qos) { - if (!connectionId || !qos.locals.contains(localId) || !qos.remotes.contains(remoteId)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; - *connectionId = NewId(qos); - qos.connections.emplace(*connectionId, Connection{localId, remoteId}); + const int result = DeleteEndpoint(remoteId, qos.remotes); + if (result != 0) return result; + for (auto it = qos.connections.begin(); it != qos.connections.end(); ++it) { + if (it->second.remote == remoteId) { + qos.connections.erase(it); + break; + } + } return 0; }); } +int APS5_VABI sceVoiceQoSConnect(int32_t* connectionId, int32_t localId, int32_t remoteId) { + return Locked([&](QoS& qos) { + if (!connectionId || localId != 0 || remoteId < 0 || remoteId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + if (!qos.locals.contains(localId) || !qos.remotes.contains(remoteId)) throw std::runtime_error("sceVoiceQoSConnect: missing voice port error behavior is not implemented"); + for (std::int32_t candidate = 0; candidate < qos.endpointLimit; ++candidate) { + if (!qos.connections.contains(candidate)) { + qos.connections.emplace(candidate, Connection{localId, remoteId}); + *connectionId = candidate; + return 0; + } + } + *connectionId = -1; + return SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT; + }); +} + int APS5_VABI sceVoiceQoSDisconnect(int32_t connectionId) { - return Locked([&](QoS& qos) { return qos.connections.erase(connectionId) != 0 ? 0 : SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; }); + return Locked([&](QoS& qos) { + if (connectionId < 0 || connectionId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + qos.connections.erase(connectionId); + return 0; + }); } int APS5_VABI sceVoiceQoSSetConnectionAttribute(std::int32_t connectionId, std::int32_t attributeId, const void* value, std::int32_t size) { @@ -154,11 +152,17 @@ int APS5_VABI sceVoiceQoSSetConnectionAttribute(std::int32_t connectionId, std:: } int APS5_VABI sceVoiceQoSSetLocalEndpointAttribute(int32_t localId, int32_t attributeId, const void* value, int32_t size) { - return Locked([&](QoS& qos) { return SetAttribute(qos.locals, localId, attributeId, value, size); }); + return Locked([&](QoS&) -> int { + if (localId != 0 || !value || attributeId < 0 || attributeId > 3 || size != (attributeId % 2 == 0 ? 4 : 1)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + throw std::runtime_error("sceVoiceQoSSetLocalEndpointAttribute: voice port attributes are not implemented"); + }); } int APS5_VABI sceVoiceQoSSetRemoteEndpointAttribute(int32_t remoteId, int32_t attributeId, const void* value, int32_t size) { - return Locked([&](QoS& qos) { return SetAttribute(qos.remotes, remoteId, attributeId, value, size); }); + return Locked([&](QoS& qos) -> int { + if (remoteId < 0 || remoteId >= qos.endpointLimit || !value || attributeId != 7 || size != 1) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + throw std::runtime_error("sceVoiceQoSSetRemoteEndpointAttribute: voice port attributes are not implemented"); + }); } int APS5_VABI sceVoiceQoSReadPacket(int32_t connectionId, void* buffer, uint32_t* size) { @@ -177,7 +181,8 @@ int APS5_VABI sceVoiceQoSWritePacket(int32_t connectionId, const void* buffer, u } int APS5_VABI sceVoiceQoSGetLocalEndpointAttribute(int32_t localId, int32_t attributeId, void* value, int32_t size) { - return Locked([&](QoS& qos) { return GetAttribute(qos.locals, localId, attributeId, value, size); }); + NotImplemented_nid_no_patch(__func__); + return 0; } int APS5_VABI sceVoiceQoSGetRemoteEndpointAttribute(int32_t remoteId, int32_t attributeId, void* value, int32_t size) { @@ -200,20 +205,18 @@ int APS5_VABI sceVoiceQoSGetConnectionAttribute(int32_t connectionId, int32_t at int APS5_VABI sceVoiceQoSGetLocalEndpoint(int32_t connectionId, int32_t* localId) { return Locked([&](QoS& qos) { - if (!localId) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + if (!localId || connectionId < 0 || connectionId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; const auto it = qos.connections.find(connectionId); - if (it == qos.connections.end()) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; - *localId = it->second.local; + *localId = it == qos.connections.end() ? -1 : it->second.local; return 0; }); } int APS5_VABI sceVoiceQoSGetRemoteEndpoint(int32_t connectionId, int32_t* remoteId) { return Locked([&](QoS& qos) { - if (!remoteId) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; + if (!remoteId || connectionId < 0 || connectionId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; const auto it = qos.connections.find(connectionId); - if (it == qos.connections.end()) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID; - *remoteId = it->second.remote; + *remoteId = it == qos.connections.end() ? -1 : it->second.remote; return 0; }); } diff --git a/core/libs/tests/GuestVoiceQoS.cpp b/core/libs/tests/GuestVoiceQoS.cpp index 9be6bbae28..8db920a44f 100644 --- a/core/libs/tests/GuestVoiceQoS.cpp +++ b/core/libs/tests/GuestVoiceQoS.cpp @@ -70,10 +70,10 @@ int main(int argc, char** argv) { std::int32_t remote = -1; std::int32_t connection = -1; Require(sceVoiceQoSCreateLocalEndpoint(nullptr, 1, 0, 0) == argumentInvalid); - Require(sceVoiceQoSCreateLocalEndpoint(&local, 1, 0, 0) == 0 && local > 0); - Require(sceVoiceQoSCreateRemoteEndpoint(&remote) == 0 && remote > 0 && remote != local); - Require(sceVoiceQoSConnect(&connection, remote, local) == argumentInvalid); - Require(sceVoiceQoSConnect(&connection, local, remote) == 0 && connection > 0); + Require(sceVoiceQoSCreateLocalEndpoint(&local, 1, 0, 0) == 0 && local == 0); + Require(sceVoiceQoSCreateRemoteEndpoint(&remote) == 0 && remote == 0); + Require(sceVoiceQoSConnect(&connection, 1, remote) == argumentInvalid); + Require(sceVoiceQoSConnect(&connection, local, remote) == 0 && connection == 0); std::int32_t queriedLocal = -1; std::int32_t queriedRemote = -1; Require(sceVoiceQoSGetLocalEndpoint(connection, nullptr) == argumentInvalid); @@ -86,32 +86,22 @@ int main(int argc, char** argv) { Require(sceVoiceQoSConnect(&duplicate, local, remote) == 0 && duplicate > 0 && duplicate != connection); Require(sceVoiceQoSDisconnect(duplicate) == 0); - std::int32_t otherLocal = -1; - std::int32_t otherConnection = -1; - Require(sceVoiceQoSCreateLocalEndpoint(&otherLocal, 1, 0, 0) == 0); - Require(sceVoiceQoSConnect(&otherConnection, otherLocal, remote) == 0); - Require(sceVoiceQoSDeleteLocalEndpoint(otherLocal) == 0); - Require(sceVoiceQoSDeleteLocalEndpoint(otherLocal) == argumentInvalid); - Require(sceVoiceQoSDisconnect(otherConnection) == 0); + constexpr int limitReached = static_cast(0x804E0907); + std::int32_t otherLocal = 12; + Require(sceVoiceQoSCreateLocalEndpoint(&otherLocal, 1, 0, 0) == limitReached && otherLocal == -1); + Require(sceVoiceQoSCreateLocalEndpoint(&otherLocal, 2, 0, 0) == limitReached && otherLocal == -1); const std::uint32_t level = 0x1234; - std::uint32_t readLevel = 0; - Require(sceVoiceQoSGetLocalEndpointAttribute(local, 3, &readLevel, sizeof(readLevel)) == 0 && readLevel == 0); - Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, &level, sizeof(level)) == 0); - Require(sceVoiceQoSGetLocalEndpointAttribute(local, 3, &readLevel, sizeof(readLevel)) == 0 && readLevel == level); - Require(sceVoiceQoSGetLocalEndpointAttribute(local, 3, nullptr, sizeof(readLevel)) == argumentInvalid); - Require(sceVoiceQoSGetLocalEndpointAttribute(local, 3, &readLevel, 0) == argumentInvalid); - Require(sceVoiceQoSGetLocalEndpointAttribute(local, 3, &readLevel, -1) == argumentInvalid); - Require(sceVoiceQoSGetLocalEndpointAttribute(local + 100, 3, &readLevel, sizeof(readLevel)) == argumentInvalid); + Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, &level, sizeof(level)) == argumentInvalid); Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, nullptr, sizeof(level)) == argumentInvalid); - Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, &level, 0) == 0); - Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, &level, -1) == 0); + Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, &level, 0) == argumentInvalid); + Require(sceVoiceQoSSetLocalEndpointAttribute(local, 3, &level, -1) == argumentInvalid); Require(sceVoiceQoSSetLocalEndpointAttribute(local + 100, 3, &level, sizeof(level)) == argumentInvalid); - Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, sizeof(level)) == 0); + Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, sizeof(level)) == argumentInvalid); Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, nullptr, sizeof(level)) == argumentInvalid); - Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, 0) == 0); - Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, -1) == 0); + Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, 0) == argumentInvalid); + Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, -1) == argumentInvalid); Require(sceVoiceQoSSetRemoteEndpointAttribute(remote + 100, 3, &level, sizeof(level)) == argumentInvalid); std::vector packet(1024, 0); @@ -127,15 +117,21 @@ int main(int argc, char** argv) { Require(sceVoiceQoSWritePacket(connection, packet.data(), nullptr) == argumentInvalid); Require(sceVoiceQoSWritePacket(connection + 100, packet.data(), &packetSize) == argumentInvalid); + Require(NotImplemented([&] { sceVoiceQoSSetLocalEndpointAttribute(local, 0, &level, 4); })); + Require(NotImplemented([&] { sceVoiceQoSSetRemoteEndpointAttribute(remote, 7, &level, 1); })); Require(sceVoiceQoSDisconnect(connection) == 0); - Require(sceVoiceQoSDisconnect(connection) == argumentInvalid); - Require(sceVoiceQoSGetLocalEndpoint(connection, &queriedLocal) == argumentInvalid); - Require(sceVoiceQoSGetRemoteEndpoint(connection, &queriedRemote) == argumentInvalid); + Require(sceVoiceQoSDisconnect(connection) == 0); + Require(sceVoiceQoSGetLocalEndpoint(connection, &queriedLocal) == 0 && queriedLocal == -1); + Require(sceVoiceQoSGetRemoteEndpoint(connection, &queriedRemote) == 0 && queriedRemote == -1); Require(sceVoiceQoSDeleteLocalEndpoint(local) == 0); Require(sceVoiceQoSDeleteLocalEndpoint(local) == argumentInvalid); Require(sceVoiceQoSDeleteRemoteEndpoint(remote) == 0); - Require(sceVoiceQoSCreateRemoteEndpoint(&remote) == 0); + const int endpointLimit = appType == 0x10000000 ? 15 : 7; + for (int index = 0; index < endpointLimit; ++index) Require(sceVoiceQoSCreateRemoteEndpoint(&remote) == 0 && remote == index); + Require(sceVoiceQoSCreateRemoteEndpoint(&remote) == limitReached && remote == -1); + Require(sceVoiceQoSDeleteRemoteEndpoint(2) == 0); + Require(sceVoiceQoSCreateRemoteEndpoint(&remote) == 0 && remote == 2); Require(sceVoiceQoSEnd() == 0); Require(sceVoiceQoSEnd() == notInitialized); Require(sceVoiceQoSGetLocalEndpoint(connection, &queriedLocal) == notInitialized); @@ -152,6 +148,7 @@ int main(int argc, char** argv) { Require(sceVoiceQoSEnd() == notInitialized); Require(sceVoiceQoSInit(nullptr, 0x40000, appType) == argumentInvalid); + Require(NotImplemented([] { sceVoiceQoSGetLocalEndpointAttribute(0, 0, nullptr, 0); })); Require(NotImplemented([] { sceVoiceQoSGetRemoteEndpointAttribute(1, 1, nullptr, 0); })); Require(NotImplemented([] { sceVoiceQoSGetConnectionAttribute(1, 1, nullptr, 0); })); Require(NotImplemented([] { sceVoiceQoSEnableChat(); })); diff --git a/docs/dev/debt/libSceVoiceQoS.md b/docs/dev/debt/libSceVoiceQoS.md index ba43885880..9b322456c0 100644 --- a/docs/dev/debt/libSceVoiceQoS.md +++ b/docs/dev/debt/libSceVoiceQoS.md @@ -7,10 +7,11 @@ ### Unknown function info -- [sceVoiceQoSInit](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `0x804E0905` for a second initialization comes from [prosper](https://github.com/mattias800/prosper)'s reading of the module and was not measured: on a PS5 a valid call blocks inside a homebrew payload, so it never returns there. PPSA26344's two call sites (`0xE307BF`, `0x151C45D`) pass an allocated pointer, a 0x40000-byte block and app type 0x10000000; the app type and block are not used. `sceVoiceQoSCreateLocalEndpoint` takes four arguments (`int* id, userId, deviceInId, deviceOutId`) as declared in fpPS4 (`src/ps4_libscevoice.pas`, `ps4_sceVoiceQoSCreateLocalEndpoint`); signatures for `DeleteLocalEndpoint(id)`, `CreateRemoteEndpoint(int* id)`, `DeleteRemoteEndpoint(id)`, `Connect(int* id, local, remote)`, `Disconnect(connection)`, `SetLocalEndpointAttribute(id, attribute, value, size)`, `SetRemoteEndpointAttribute(id, attribute, value, size)`, and `GetLocalEndpointAttribute(id, attribute, value, size)` were verified against call sites in Aliens: Fireteam Elite (PPSA03393, e.g. `0x76D132`, `0x76D16E`, `0x76CECB`). Endpoints and connections are tracked by allocated id; duplicate connections and deleting an endpoint while referenced in a connection are permitted as the console is not known to reject them; endpoint attributes are stored as given (including empty or non-positive sizes), as their ids and defaults are unknown. The not-initialized error 0x804E0901 is assumed from the 0x804E09xx numbering, and unknown ids return `ARGUMENT_INVALID`. Aliens: Fireteam Elite (PPSA03393) uses all implemented calls on its load path; it stops with `not implemented` when any unimplemented call throws -- [libSceVoiceQoS firmware exports](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `sceVoiceQoSGetRemoteEndpointAttribute`, `sceVoiceQoSGetConnectionAttribute` and `sceVoiceQoSGetStatus` follow public PS4 SDK prototypes in [Orbital](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json#L2436-L2441) and remain throwing stubs as endpoint and connection attribute storage is unverified on hardware. `sceVoiceQoSSetMode` and `sceVoiceQoSTerminate` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws +- [sceVoiceQoSSetMode and sceVoiceQoSTerminate](../../../core/libs/prx/libSceVoiceQoS/Export.cpp): these exports are absent from the supplied module. Their argument declarations still require another firmware version or callers. ### Functional -- [sceVoiceQoSEnd](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `0x804E0901` (not initialized) before any initialization was measured on a PS5 (firmware 13.60, #1407); ending an initialized library was not, because a valid `sceVoiceQoSInit` never returns inside a homebrew payload. PPSA14632 calls it at shutdown and PPSA26344 after a nonzero `sceVoiceQoSInit`. After an initialization it returns 0 and clears the init flag, so the library can be initialized again. Whether the console also ends the libSceVoice instance that its `sceVoiceQoSInit` starts ([prosper](https://github.com/mattias800/prosper)'s reading of the module), and what it answers while endpoints exist, is unknown; here it drops every endpoint and connection -- [sceVoiceQoSGetLocalEndpoint, sceVoiceQoSGetRemoteEndpoint](../../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - signatures follow [Orbital](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json#L2437-L2439) (`GetLocalEndpoint(connectionId, int* localId)` and `GetRemoteEndpoint(connectionId, int* remoteId)`). Operations look up the connection by allocated id and return the connected local or remote endpoint id; an unknown connection id or null output pointer returns `ARGUMENT_INVALID` (0x804E0902), and calling before initialization returns `0x804E0901` +- [libSceVoiceQoS](../../../core/libs/prx/libSceVoiceQoS/Export.cpp): initialization arguments, initialization errors, endpoint and connection argument lists, and the no-argument End path are confirmed by the supplied module. The host still tracks endpoints without initializing, starting or stopping libSceVoice or creating its ports. Device-dependent port creation, deletion and connection errors remain unimplemented; connecting an uncreated port throws. End clears host state but does not execute the confirmed voice stop/end sequence or propagate its result. +- [sceVoiceQoSCreateLocalEndpoint, sceVoiceQoSCreateRemoteEndpoint and sceVoiceQoSConnect](../../../core/libs/prx/libSceVoiceQoS/Export.cpp): local ID zero and its single-slot limit, remote/connection slot allocation from zero, seven/fifteen slot capacities, exhaustion outputs, and reuse of released slots are recovered. Remote endpoint deletion clears the first matching connection. Shared encoder ownership, codec parameters and packet accounting are still missing. +- [sceVoiceQoSSetLocalEndpointAttribute and sceVoiceQoSSetRemoteEndpointAttribute](../../../core/libs/prx/libSceVoiceQoS/Export.cpp): local selectors 0/2 accept four-byte values and 1/3 one-byte values; remote selector 7 accepts one byte. Unsupported selectors and sizes return the confirmed invalid-argument error. Valid requests throw until voice-port volume/mute forwarding is implemented; they no longer silently store arbitrary bytes. +- [sceVoiceQoSGetLocalEndpoint and sceVoiceQoSGetRemoteEndpoint](../../../core/libs/prx/libSceVoiceQoS/Export.cpp): all in-range connection slots are queryable, including unused slots returning endpoint ID -1. Disconnecting an unused in-range slot succeeds. These state rules are confirmed; the packet, status and attribute getter bodies containing `NotImplemented_nid_no_patch` remain unchanged. From 555fd66a5cf6f15647a7325f68af0c4aba82c0fc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 14:50:18 +0300 Subject: [PATCH 265/523] fix(pngdec): restore work memory layout and validation errors --- core/libs/prx/libScePngDec/Export.cpp | 50 +++++++++++++++++---------- core/libs/tests/GuestPngDec.cpp | 24 +++++++------ docs/dev/debt/libScePngDec.md | 6 +++- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/core/libs/prx/libScePngDec/Export.cpp b/core/libs/prx/libScePngDec/Export.cpp index 419c7966f0..8ff5827de0 100644 --- a/core/libs/prx/libScePngDec/Export.cpp +++ b/core/libs/prx/libScePngDec/Export.cpp @@ -28,8 +28,8 @@ constexpr std::int32_t SCE_PNG_DEC_ERROR_DECODE_ERROR = static_castthis_size != sizeof(PngDecCreateParam)) return SCE_PNG_DEC_ERROR_INVALID_SIZE; if (param->attribute != ATTRIBUTE_NONE && param->attribute != ATTRIBUTE_BIT_DEPTH_16) return SCE_PNG_DEC_ERROR_INVALID_PARAM; if (param->max_image_width - 1 >= MAX_IMAGE_WIDTH) return SCE_PNG_DEC_ERROR_INVALID_PARAM; @@ -102,38 +108,42 @@ std::span toBytes(const void* data, std::uint32_t size) { return {static_cast(data), size}; } -} // namespace +} // namespace extern "C" { -int32_t APS5_VABI scePngDecCreate(const PngDecCreateParam* param, void* memory_address, uint32_t memory_size, void** handle) { +int32_t APS5_VABI scePngDecCreate(const PngDecCreateParam* param, void* memoryAddress, uint32_t memorySize, void** handle) { const std::int32_t result = validateCreateParam(param); if (result != 0) return result; - if (!memory_address || !handle) return SCE_PNG_DEC_ERROR_INVALID_ADDR; - if (memory_size < MEMORY_SIZE) return SCE_PNG_DEC_ERROR_INVALID_WORK_MEMORY; - const auto address = reinterpret_cast(memory_address); + if (!memoryAddress) return SCE_PNG_DEC_ERROR_INVALID_ADDR; + if (memorySize < requiredMemory(*param)) return SCE_PNG_DEC_ERROR_INVALID_SIZE; + if (!handle) return SCE_PNG_DEC_ERROR_INVALID_ADDR; + const auto address = reinterpret_cast(memoryAddress); const std::uintptr_t aligned = (address + HANDLE_ALIGNMENT - 1) & ~(HANDLE_ALIGNMENT - 1); - auto* context = new (reinterpret_cast(aligned)) Context{}; + auto* context = new (reinterpret_cast(aligned)) Context; context->self = context; + context->size = sizeof(PngDecCreateParam); context->attribute = param->attribute; - context->maxImageWidth = param->max_image_width; + context->maxImageWidth = (param->max_image_width + 7u) & ~7u; + context->memorySize = requiredMemory(*param) - 144u; *handle = context; return 0; } -int32_t APS5_VABI scePngDecDecode(void* handle, const PngDecDecodeParam* param, PngDecImageInfo* image_info) { +int32_t APS5_VABI scePngDecDecode(void* handle, const PngDecDecodeParam* param, PngDecImageInfo* imageInfo) { const Context* context = toContext(handle); if (!context) return SCE_PNG_DEC_ERROR_INVALID_HANDLE; - if (!param) return SCE_PNG_DEC_ERROR_INVALID_PARAM; + if (!param) return SCE_PNG_DEC_ERROR_INVALID_ADDR; if (!param->png_mem_addr || !param->image_mem_addr || (reinterpret_cast(param->image_mem_addr) & 3u) != 0) return SCE_PNG_DEC_ERROR_INVALID_ADDR; if (param->png_mem_size == 0 || param->image_mem_size == 0) return SCE_PNG_DEC_ERROR_INVALID_SIZE; + if (param->pixel_format == 132) throw std::runtime_error("scePngDecDecode: pixel format 132 is not implemented"); if (param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) return SCE_PNG_DEC_ERROR_INVALID_PARAM; if (param->alpha_value > 0xFF) return SCE_PNG_DEC_ERROR_INVALID_PARAM; const std::span png = toBytes(param->png_mem_addr, param->png_mem_size); const std::optional header = Decoder::Png::ParseHeader(png); if (!header) return SCE_PNG_DEC_ERROR_INVALID_DATA; - if (image_info) fillImageInfo(*header, image_info); + if (imageInfo) fillImageInfo(*header, imageInfo); if (header->bitDepth == 16 && context->attribute == ATTRIBUTE_BIT_DEPTH_16) { throw std::runtime_error("scePngDecDecode: 16-bit output is not implemented"); } @@ -179,20 +189,22 @@ int32_t APS5_VABI scePngDecDelete(void* handle) { return 0; } -int32_t APS5_VABI scePngDecParseHeader(const PngDecParseParam* param, PngDecImageInfo* image_info) { - if (!param) return SCE_PNG_DEC_ERROR_INVALID_PARAM; - if (!param->png_mem_addr || !image_info) return SCE_PNG_DEC_ERROR_INVALID_ADDR; +int32_t APS5_VABI scePngDecParseHeader(const PngDecParseParam* param, PngDecImageInfo* imageInfo) { + if (!param) return SCE_PNG_DEC_ERROR_INVALID_ADDR; + if (!param->png_mem_addr) return SCE_PNG_DEC_ERROR_INVALID_ADDR; if (param->png_mem_size == 0) return SCE_PNG_DEC_ERROR_INVALID_SIZE; + if (param->reserved0 != 0) return SCE_PNG_DEC_ERROR_INVALID_PARAM; + if (!imageInfo) return SCE_PNG_DEC_ERROR_INVALID_ADDR; const std::optional header = Decoder::Png::ParseHeader(toBytes(param->png_mem_addr, param->png_mem_size)); if (!header) return SCE_PNG_DEC_ERROR_INVALID_DATA; - fillImageInfo(*header, image_info); + fillImageInfo(*header, imageInfo); return 0; } int32_t APS5_VABI scePngDecQueryMemorySize(const PngDecCreateParam* param) { const std::int32_t result = validateCreateParam(param); if (result != 0) return result; - return static_cast(MEMORY_SIZE); + return static_cast(requiredMemory(*param)); } int APS5_VABI scePngDecDecodeWithInputControl() { diff --git a/core/libs/tests/GuestPngDec.cpp b/core/libs/tests/GuestPngDec.cpp index 83281c1c1c..4ee4031ad2 100644 --- a/core/libs/tests/GuestPngDec.cpp +++ b/core/libs/tests/GuestPngDec.cpp @@ -81,8 +81,8 @@ int main() { const PngDecCreateParam param{sizeof(PngDecCreateParam), 0, 1920}; const std::int32_t memorySize = scePngDecQueryMemorySize(¶m); - Require(memorySize > 0); - Require(scePngDecQueryMemorySize(nullptr) == invalidParam); + Require(memorySize == 69264); + Require(scePngDecQueryMemorySize(nullptr) == invalidAddr); const PngDecCreateParam badSize{sizeof(PngDecCreateParam) - 1, 0, 1920}; Require(scePngDecQueryMemorySize(&badSize) == invalidSize); const PngDecCreateParam badAttribute{sizeof(PngDecCreateParam), 2, 1920}; @@ -94,21 +94,21 @@ int main() { const PngDecCreateParam overWidth{sizeof(PngDecCreateParam), 1, 1000001}; Require(scePngDecQueryMemorySize(&overWidth) == invalidParam); const PngDecCreateParam maxWidth{sizeof(PngDecCreateParam), 1, 1000000}; - Require(scePngDecQueryMemorySize(&maxWidth) == memorySize); + Require(scePngDecQueryMemorySize(&maxWidth) == 16054416); std::vector memory(static_cast(memorySize) + 1); unsigned char* unaligned = memory.data() + 1; void* handle = nullptr; - Require(scePngDecCreate(nullptr, unaligned, static_cast(memorySize), &handle) == invalidParam); + Require(scePngDecCreate(nullptr, unaligned, static_cast(memorySize), &handle) == invalidAddr); Require(scePngDecCreate(&badSize, unaligned, static_cast(memorySize), &handle) == invalidSize); Require(scePngDecCreate(&overWidth, unaligned, static_cast(memorySize), &handle) == invalidParam); Require(scePngDecCreate(¶m, nullptr, static_cast(memorySize), &handle) == invalidAddr); Require(scePngDecCreate(¶m, unaligned, static_cast(memorySize), nullptr) == invalidAddr); - Require(scePngDecCreate(¶m, unaligned, static_cast(memorySize) - 1, &handle) == invalidWorkMemory); + Require(scePngDecCreate(¶m, unaligned, static_cast(memorySize) - 1, &handle) == invalidSize); Require(handle == nullptr); Require(scePngDecCreate(¶m, unaligned, static_cast(memorySize), &handle) == 0); - Require(reinterpret_cast(handle) % 8 == 0); - Require(static_cast(handle) >= unaligned && static_cast(handle) < unaligned + 8); + Require(reinterpret_cast(handle) % 16 == 0); + Require(static_cast(handle) >= unaligned && static_cast(handle) < unaligned + 16); const std::vector rgbPng = RgbPng(); PngDecImageInfo info{}; @@ -125,7 +125,7 @@ int main() { } const PngDecParseParam completeTransparency{PALETTE_TRNS, 73, 0}; Require(scePngDecParseHeader(&completeTransparency, &info) == 0 && info.image_flag == 2); - Require(scePngDecParseHeader(nullptr, &info) == invalidParam); + Require(scePngDecParseHeader(nullptr, &info) == invalidAddr); Require(scePngDecParseHeader(&parse, nullptr) == invalidAddr); const PngDecParseParam parseNull{nullptr, 16, 0}; Require(scePngDecParseHeader(&parseNull, &info) == invalidAddr); @@ -213,7 +213,7 @@ int main() { decode = DecodeParam(rgbPng, image); Require(scePngDecDecode(nullptr, &decode, &info) == invalidHandle); - Require(scePngDecDecode(handle, nullptr, &info) == invalidParam); + Require(scePngDecDecode(handle, nullptr, &info) == invalidAddr); decode.png_mem_addr = nullptr; Require(scePngDecDecode(handle, &decode, &info) == invalidAddr); decode = DecodeParam(rgbPng, image); @@ -253,9 +253,11 @@ int main() { Require(scePngDecDecode(handle, &decode, &info) == decodeError); void* deepHandle = nullptr; - std::vector deepMemory(static_cast(memorySize)); const PngDecCreateParam deepParam{sizeof(PngDecCreateParam), 1, 1920}; - Require(scePngDecCreate(&deepParam, deepMemory.data(), static_cast(memorySize), &deepHandle) == 0); + const auto deepSize = scePngDecQueryMemorySize(&deepParam); + Require(deepSize == 85136); + std::vector deepMemory(static_cast(deepSize)); + Require(scePngDecCreate(&deepParam, deepMemory.data(), static_cast(deepSize), &deepHandle) == 0); decode = DecodeParam(gray16Png, gray16Image); Require(ThrowsRuntimeError([&] { scePngDecDecode(deepHandle, &decode, &info); })); decode = DecodeParam(rgbPng, image); diff --git a/docs/dev/debt/libScePngDec.md b/docs/dev/debt/libScePngDec.md index 2eca53adb5..9449f5a2e9 100644 --- a/docs/dev/debt/libScePngDec.md +++ b/docs/dev/debt/libScePngDec.md @@ -1,6 +1,10 @@ # libScePngDec technical debt +### Functional + +- [libScePngDec](../../../core/libs/prx/libScePngDec/Export.cpp): the supplied module confirms the work-memory size formula, 16-byte handle alignment, the first 24 bytes of its context, and Create/QueryMemorySize validation order. The host now exposes these values but still allocates decoder working storage on the host instead of using the guest work region. +- [scePngDecDecode](../../../core/libs/prx/libScePngDec/Export.cpp): 16-bit output and the original's accepted pixel format 132 remain unsupported and throw. Malformed-image diagnostics and decoder scratch-memory behavior are not fully matched to the supplied module. + ### Unknown function info -- [scePngDecQueryMemorySize, scePngDecCreate](../../../core/libs/prx/libScePngDec/Export.cpp) (libScePngDec) - the work memory is a fixed 0x20 bytes, because decoding uses host memory; the PS5 module asks for `((max_image_width + 0x47) & ~7) * (8 << attribute)` plus a constant, as read from the 3.20 module by prosper. A null parameter returns `0x80690003`; prosper returns `0x80690001` when it cannot read the parameter, which was not measured. Closes when: measured on a PS5 - [scePngDecDecodeWithInputControl](../../../core/libs/prx/libScePngDec/Export.cpp) (libScePngDec) - its name and NID are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePngDec.c); the no-parameter prototype follows [shadPS4's pngdec.h](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/libpng/pngdec.h#L76) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim. From 7742fd5831fd05887c52b3e0d7aca020577d8902 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:13:28 +0300 Subject: [PATCH 266/523] fix(mbus): restore device registration and control arguments --- core/libs/prx/libSceMbus/PendingExports.cpp | 28 ++++++++++----------- docs/dev/debt/libSceMbus.md | 4 ++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index deb64a2463..33c86a808b 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -2,7 +2,7 @@ extern "C" { -int APS5_VABI sceMbusAcquireControl() { +int APS5_VABI sceMbusAcquireControl(std::uint64_t control, std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -11,22 +11,22 @@ int APS5_VABI sceMbusAcquireControlList() { return static_cast(0x809B00FF); } -int APS5_VABI sceMbusAcquireControlWithState() { +int APS5_VABI sceMbusAcquireControlWithState(std::uint32_t deviceClass, std::int32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusAcquireControlWithState2() { +int APS5_VABI sceMbusAcquireControlWithState2(std::uint32_t deviceClass, std::int32_t moduleId, std::int32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusAcquireControlWithStateFlag() { +int APS5_VABI sceMbusAcquireControlWithStateFlag(std::uint32_t deviceClass, std::uint32_t moduleId, std::uint32_t state, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusAddHandle() { +int APS5_VABI sceMbusAddHandle(std::uint32_t deviceClass, std::int32_t handle, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -36,32 +36,32 @@ int APS5_VABI sceMbusAddHandleByDeviceId(std::uint32_t deviceClass, std::int32_t return 0; } -int APS5_VABI sceMbusAddHandleByUserId() { +int APS5_VABI sceMbusAddHandleByUserId(std::uint32_t deviceClass, std::int32_t handle, std::int32_t userId, std::int32_t first, std::int32_t second, std::int32_t third) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusAddHandleByVidPid() { +int APS5_VABI sceMbusAddHandleByVidPid(std::uint32_t deviceClass, std::int32_t handle, std::int32_t first, std::uint16_t vendorId, std::uint16_t productId, std::int32_t second, std::int32_t third) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusAddHandleWithModuleId() { +int APS5_VABI sceMbusAddHandleWithModuleId(std::uint32_t deviceClass, std::int32_t handle, std::uint32_t flags, std::uint32_t moduleId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusAttachToApplicationByPid() { +int APS5_VABI sceMbusAttachToApplicationByPid(std::int32_t processId, std::int32_t deviceClass, std::int32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusBindDeviceWithUserId() { +int APS5_VABI sceMbusBindDeviceWithUserId(std::uint64_t deviceId, std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusChangeAppModuleState() { +int APS5_VABI sceMbusChangeAppModuleState(std::uint32_t deviceClass, std::int32_t moduleId, std::uint32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -71,17 +71,17 @@ int APS5_VABI sceMbusCheckDriverCallback() { return 0; } -int APS5_VABI sceMbusConvertToLocalDeviceId() { +int APS5_VABI sceMbusConvertToLocalDeviceId(std::uint64_t deviceId, std::uint32_t* localId, std::uint32_t* deviceClass) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusConvertToLocalDeviceId2() { +int APS5_VABI sceMbusConvertToLocalDeviceId2(std::uint64_t deviceId, std::uint32_t deviceClass, std::uint64_t* localId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusConvertToMbusDeviceId() { +int APS5_VABI sceMbusConvertToMbusDeviceId(std::uint64_t localId, std::uint32_t deviceClass, std::uint64_t* deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index c19904c941..335491f9c7 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,6 +8,8 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 113 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 99 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. + +- [sceMbus control and handle registration](../../../core/libs/prx/libSceMbus/PendingExports.cpp): argument positions and transmitted widths are recovered for control acquisition, handle registration, process attachment, user binding and device-ID conversion. Several scalar field meanings still depend on the service protocol; the 64-bit control value and unnamed registration fields must not be interpreted as pointers. AcquireControlList returns a constant error without reading its incoming arguments, so its source signature remains unresolved. From f5e1929f8b8657ecf7f0c8187aa2edec9b93f0b7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:14:33 +0300 Subject: [PATCH 267/523] fix(mbus): restore debug process and device query signatures --- core/libs/prx/libSceMbus/PendingExports.cpp | 54 ++++++++++++--------- docs/dev/debt/libSceMbus.md | 4 +- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index 33c86a808b..9822cc0453 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -1,5 +1,13 @@ #include "prx/libc/include/General.hpp" +struct MbusProcessInfo; +struct MbusStartupInfo; +struct MbusStartupParam; +struct MbusControlStatus; +struct MbusDebugDeviceInfo; +struct MbusInternalInfo; +struct MbusPriorityInfo; + extern "C" { int APS5_VABI sceMbusAcquireControl(std::uint64_t control, std::int32_t first, std::int32_t second) { @@ -86,117 +94,117 @@ int APS5_VABI sceMbusConvertToMbusDeviceId(std::uint64_t localId, std::uint32_t return 0; } -int APS5_VABI sceMbusDebugAcquireControl() { +int APS5_VABI sceMbusDebugAcquireControl(std::uint64_t control, std::int32_t first, std::int32_t second, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugAcquireControlWithState() { +int APS5_VABI sceMbusDebugAcquireControlWithState(std::uint32_t deviceClass, std::int32_t state, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugAcquireControlWithState2() { +int APS5_VABI sceMbusDebugAcquireControlWithState2(std::uint32_t deviceClass, std::int32_t moduleId, std::int32_t state, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugAcquireControlWithStateFlag() { +int APS5_VABI sceMbusDebugAcquireControlWithStateFlag(std::uint32_t deviceClass, std::uint32_t moduleId, std::uint32_t state, std::uint32_t flags, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugAddProcess() { +int APS5_VABI sceMbusDebugAddProcess(std::int32_t processId, std::int32_t flags, const MbusProcessInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugCheckProcessResume() { +int APS5_VABI sceMbusDebugCheckProcessResume(MbusProcessInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugDisableBgmForShellUi() { +int APS5_VABI sceMbusDebugDisableBgmForShellUi(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugGetApplicationStartupInfo() { +int APS5_VABI sceMbusDebugGetApplicationStartupInfo(std::uint32_t appId, const MbusProcessInfo* info, MbusStartupInfo* startupInfo) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugGetControlStatus() { +int APS5_VABI sceMbusDebugGetControlStatus(std::int32_t deviceClass, std::int32_t processId, MbusControlStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugGetDeviceInfo() { +int APS5_VABI sceMbusDebugGetDeviceInfo(std::uint64_t deviceClass, MbusDebugDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugGetInternalInfo() { +int APS5_VABI sceMbusDebugGetInternalInfo(MbusInternalInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugGetPriorityInfo() { +int APS5_VABI sceMbusDebugGetPriorityInfo(std::uint32_t deviceClass, MbusPriorityInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugReenableBgmForShellUi() { +int APS5_VABI sceMbusDebugReenableBgmForShellUi(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugReleaseControl() { +int APS5_VABI sceMbusDebugReleaseControl(std::uint64_t control, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugRemoveCameraAppModuleFocus() { +int APS5_VABI sceMbusDebugRemoveCameraAppModuleFocus(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugResumeApplication() { +int APS5_VABI sceMbusDebugResumeApplication(std::int32_t appId, std::uint8_t firstFlag, std::uint8_t secondFlag, MbusProcessInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugSetApplicationFocusByAppId() { +int APS5_VABI sceMbusDebugSetApplicationFocusByAppId(std::uint32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugSetCameraAppModuleFocus() { +int APS5_VABI sceMbusDebugSetCameraAppModuleFocus(std::int32_t processId, std::int32_t moduleId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugSetControllerFocusByAppId() { +int APS5_VABI sceMbusDebugSetControllerFocusByAppId(std::uint32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugSetOtherProcessExcludedAction() { +int APS5_VABI sceMbusDebugSetOtherProcessExcludedAction(std::uint64_t control, std::uint32_t action, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugSetPriority() { +int APS5_VABI sceMbusDebugSetPriority(std::uint64_t control, std::uint32_t priority, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugStartApplication() { +int APS5_VABI sceMbusDebugStartApplication(std::int32_t appId, std::int32_t first, std::int32_t second, std::uint8_t firstFlag, std::uint8_t secondFlag, const MbusStartupParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugStartApplication2() { +int APS5_VABI sceMbusDebugStartApplication2(std::int32_t appId, std::int32_t first, std::int32_t second, std::uint8_t firstFlag, std::uint8_t secondFlag, const MbusStartupParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index 335491f9c7..457bd494bb 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,8 +8,10 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 99 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 76 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. - [sceMbus control and handle registration](../../../core/libs/prx/libSceMbus/PendingExports.cpp): argument positions and transmitted widths are recovered for control acquisition, handle registration, process attachment, user binding and device-ID conversion. Several scalar field meanings still depend on the service protocol; the 64-bit control value and unnamed registration fields must not be interpreted as pointers. AcquireControlList returns a constant error without reading its incoming arguments, so its source signature remains unresolved. + +- [sceMbus debug process and device queries](../../../core/libs/prx/libSceMbus/PendingExports.cpp): process information is transmitted as 16 bytes, startup input/output as eight bytes, control status as 24 bytes, internal information as 2832 bytes and priority information as 3088 bytes. Device enumeration writes a 32-bit count followed by 396-byte entries. These pointee fields and the semantics of unnamed process flags remain unresolved; declarations use distinct incomplete types instead of guessing their fields. From e6115d30f2f0fca61a7aaaefd2193317fd2cd8a6 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:15:42 +0300 Subject: [PATCH 268/523] fix(mbus): restore event queue and diagnostic arguments --- core/libs/prx/libSceMbus/PendingExports.cpp | 34 +++++++++++---------- docs/dev/debt/libSceMbus.md | 4 ++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index 9822cc0453..c1edb5c31a 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -1,5 +1,7 @@ #include "prx/libc/include/General.hpp" +struct MbusEvent; +struct MbusEventCallbacks; struct MbusProcessInfo; struct MbusStartupInfo; struct MbusStartupParam; @@ -209,27 +211,27 @@ int APS5_VABI sceMbusDebugStartApplication2(std::int32_t appId, std::int32_t fir return 0; } -int APS5_VABI sceMbusDebugStartApplicationNull() { +int APS5_VABI sceMbusDebugStartApplicationNull(std::int32_t appId, std::int32_t first, std::int32_t second, std::uint8_t firstFlag, std::uint8_t secondFlag) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugSuspendApplication() { +int APS5_VABI sceMbusDebugSuspendApplication(std::uint32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugTerminateApplication() { +int APS5_VABI sceMbusDebugTerminateApplication(std::uint32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDebugTerminateProcess() { +int APS5_VABI sceMbusDebugTerminateProcess(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDetachFromApplicationByPid() { +int APS5_VABI sceMbusDetachFromApplicationByPid(std::int32_t processId, std::int32_t deviceClass) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -239,12 +241,12 @@ int APS5_VABI sceMbusDisableBgmForShellUi() { return 0; } -int APS5_VABI sceMbusDisableChat() { +int APS5_VABI sceMbusDisableChat(std::uint32_t deviceClass) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDisconnectDevice() { +int APS5_VABI sceMbusDisconnectDevice(std::uint64_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -259,17 +261,17 @@ int APS5_VABI sceMbusDumpDeviceInfo() { return 0; } -int APS5_VABI sceMbusDumpHandleStatusInfo() { +int APS5_VABI sceMbusDumpHandleStatusInfo(std::uint64_t control, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDumpHandleStatusInfoAll() { +int APS5_VABI sceMbusDumpHandleStatusInfoAll(std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusDumpModuleStateInfo() { +int APS5_VABI sceMbusDumpModuleStateInfo(std::uint64_t control) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -289,7 +291,7 @@ int APS5_VABI sceMbusDumpPortHandleInfo() { return 0; } -int APS5_VABI sceMbusDumpProcMediaInfo() { +int APS5_VABI sceMbusDumpProcMediaInfo(std::uint64_t control) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -304,17 +306,17 @@ int APS5_VABI sceMbusDumpUserMediaInfo() { return 0; } -int APS5_VABI sceMbusEnableChat() { +int APS5_VABI sceMbusEnableChat(std::uint32_t deviceClass) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusEnterAudioOutAutoAttenuation() { +int APS5_VABI sceMbusEnterAudioOutAutoAttenuation(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusEventBusStatusChangeSubscribe() { +int APS5_VABI sceMbusEventBusStatusChangeSubscribe(std::int32_t eventHandle, std::uint32_t first, std::uint32_t second, std::uint64_t firstValue, std::uint64_t secondValue) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -324,12 +326,12 @@ int APS5_VABI sceMbusEventBusStatusChangeUnsubscribe() { return 0; } -int APS5_VABI sceMbusEventCallbackFuncsInit_() { +int APS5_VABI sceMbusEventCallbackFuncsInit_(MbusEventCallbacks* callbacks, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusEventCreate_() { +int APS5_VABI sceMbusEventCreate_(std::uint32_t flags, MbusEvent* buffer, std::uint32_t capacity, const MbusEventCallbacks* callbacks, void* userData) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index 457bd494bb..5286392e2b 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,10 +8,12 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 76 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 52 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. - [sceMbus control and handle registration](../../../core/libs/prx/libSceMbus/PendingExports.cpp): argument positions and transmitted widths are recovered for control acquisition, handle registration, process attachment, user binding and device-ID conversion. Several scalar field meanings still depend on the service protocol; the 64-bit control value and unnamed registration fields must not be interpreted as pointers. AcquireControlList returns a constant error without reading its incoming arguments, so its source signature remains unresolved. - [sceMbus debug process and device queries](../../../core/libs/prx/libSceMbus/PendingExports.cpp): process information is transmitted as 16 bytes, startup input/output as eight bytes, control status as 24 bytes, internal information as 2832 bytes and priority information as 3088 bytes. Device enumeration writes a 32-bit count followed by 396-byte entries. These pointee fields and the semantics of unnamed process flags remain unresolved; declarations use distinct incomplete types instead of guessing their fields. + +- [sceMbus event subscriptions](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the subscription transports two 32-bit and two 64-bit fields whose service-side meanings remain unresolved. EventCreate uses 80-byte queue records and copies a 16-byte callback table; callback signatures and event fields still require the dispatch path. EventCallbackFuncsInit only clears memory and has no defined return value; its protected stub retains the existing integer return. Eight dump/BGM wrappers have confirmed zero-argument ABIs. EventBusStatusChangeUnsubscribe ignores its first incoming argument and consumes a 32-bit second argument; its first parameter type remains unresolved. From 952b8ab0382fcb26ef599d64bf6add18e1e35c76 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:16:52 +0300 Subject: [PATCH 269/523] fix(mbus): restore device query and driver event signatures --- core/libs/prx/libSceMbus/PendingExports.cpp | 43 ++++++++++++--------- docs/dev/debt/libSceMbus.md | 4 +- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index c1edb5c31a..b78144821c 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -1,5 +1,10 @@ #include "prx/libc/include/General.hpp" +struct MbusDeviceDescription; +struct MbusDeviceInfo; +struct MbusSimulatedHandleStatus; +struct MbusSparkState; +struct MbusDriverCallbacks; struct MbusEvent; struct MbusEventCallbacks; struct MbusProcessInfo; @@ -336,27 +341,27 @@ int APS5_VABI sceMbusEventCreate_(std::uint32_t flags, MbusEvent* buffer, std::u return 0; } -int APS5_VABI sceMbusEventDestroy() { +int APS5_VABI sceMbusEventDestroy(std::int32_t eventHandle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusEventReceive() { +int APS5_VABI sceMbusEventReceive(std::int32_t eventHandle, MbusEvent* events, std::int32_t capacity, std::int32_t wait) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetControlStatus() { +int APS5_VABI sceMbusGetControlStatus(std::uint64_t control, MbusControlStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetDeviceDescription() { +int APS5_VABI sceMbusGetDeviceDescription(std::uint64_t deviceId, std::uint32_t selector, MbusDeviceDescription* description) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetDeviceInfoByBusId_() { +int APS5_VABI sceMbusGetDeviceInfoByBusId_(std::int32_t busId, MbusDeviceInfo* info, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -371,27 +376,27 @@ int APS5_VABI sceMbusGetDeviceInfoByCondition_() { return 0; } -int APS5_VABI sceMbusGetDeviceInfo_() { +int APS5_VABI sceMbusGetDeviceInfo_(std::uint64_t deviceId, MbusDeviceInfo* info, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetSimulatedBusUsableStatusByBusType() { +int APS5_VABI sceMbusGetSimulatedBusUsableStatusByBusType(std::uint32_t busType, std::int32_t first, std::uint64_t deviceId, std::uint32_t second, std::uint32_t third) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetSimulatedHandleStatusInfo() { +int APS5_VABI sceMbusGetSimulatedHandleStatusInfo(std::int32_t deviceClass, std::int32_t first, std::int32_t second, MbusSimulatedHandleStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetSimulatedHandleStatusInfo2() { +int APS5_VABI sceMbusGetSimulatedHandleStatusInfo2(std::uint32_t deviceClass, std::uint32_t first, std::uint32_t second, std::uint32_t third, MbusSimulatedHandleStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusGetSparkState() { +int APS5_VABI sceMbusGetSparkState(std::uint64_t deviceId, MbusSparkState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -406,22 +411,22 @@ int APS5_VABI sceMbusIsBgmProhibited() { return 0; } -int APS5_VABI sceMbusIsUsingDevice() { +int APS5_VABI sceMbusIsUsingDevice(std::int32_t deviceClass, std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusLeaveAudioOutAutoAttenuation() { +int APS5_VABI sceMbusLeaveAudioOutAutoAttenuation(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusNotifyBtDisconnection() { +int APS5_VABI sceMbusNotifyBtDisconnection(std::uint64_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusNotifyDriverEvent() { +int APS5_VABI sceMbusNotifyDriverEvent(std::int32_t driverId, std::int32_t eventId, std::uint64_t firstValue, std::int32_t flags, std::uint64_t secondValue) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -431,12 +436,12 @@ int APS5_VABI sceMbusReenableBgmForShellUi() { return 0; } -int APS5_VABI sceMbusRegisterDriver() { +int APS5_VABI sceMbusRegisterDriver(std::int32_t driverId, const MbusDriverCallbacks* callbacks, void* userData) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusReleaseControl() { +int APS5_VABI sceMbusReleaseControl(std::uint64_t control) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -446,17 +451,17 @@ int APS5_VABI sceMbusRemoveCameraAppModuleFocus() { return 0; } -int APS5_VABI sceMbusRemoveHandle() { +int APS5_VABI sceMbusRemoveHandle(std::int32_t deviceClass, std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusRemoveProxy() { +int APS5_VABI sceMbusRemoveProxy(std::int32_t processId, std::uint32_t deviceClass, std::uint64_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusResolveByDeviceId() { +int APS5_VABI sceMbusResolveByDeviceId(std::int32_t deviceClass, std::uint64_t deviceId, std::uint32_t first, std::uint32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index 5286392e2b..e4313ab744 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,7 +8,7 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - 52 remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. @@ -17,3 +17,5 @@ - [sceMbus debug process and device queries](../../../core/libs/prx/libSceMbus/PendingExports.cpp): process information is transmitted as 16 bytes, startup input/output as eight bytes, control status as 24 bytes, internal information as 2832 bytes and priority information as 3088 bytes. Device enumeration writes a 32-bit count followed by 396-byte entries. These pointee fields and the semantics of unnamed process flags remain unresolved; declarations use distinct incomplete types instead of guessing their fields. - [sceMbus event subscriptions](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the subscription transports two 32-bit and two 64-bit fields whose service-side meanings remain unresolved. EventCreate uses 80-byte queue records and copies a 16-byte callback table; callback signatures and event fields still require the dispatch path. EventCallbackFuncsInit only clears memory and has no defined return value; its protected stub retains the existing integer return. Eight dump/BGM wrappers have confirmed zero-argument ABIs. EventBusStatusChangeUnsubscribe ignores its first incoming argument and consumes a 32-bit second argument; its first parameter type remains unresolved. + +- [sceMbus device and driver payloads](../../../core/libs/prx/libSceMbus/PendingExports.cpp): device-info records occupy 256 bytes, descriptions 264 bytes, simulated-handle status and Spark state 16 bytes each. Their service-side fields remain unresolved. Driver registration copies four callback pointers; driver-event notification transports two 64-bit values without dereferencing them. Init, IsBgmProhibited, ReenableBgmForShellUi and RemoveCameraAppModuleFocus consume no incoming arguments. From ab4a949f07f678d96d4d12d24c86b3a5a5ff8d00 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:18:05 +0300 Subject: [PATCH 270/523] fix(mbus): restore routing and user setting arguments --- core/libs/prx/libSceMbus/PendingExports.cpp | 39 +++++++++++---------- docs/dev/debt/libSceMbus.md | 4 ++- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index b78144821c..b69e0f5add 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -5,6 +5,7 @@ struct MbusDeviceInfo; struct MbusSimulatedHandleStatus; struct MbusSparkState; struct MbusDriverCallbacks; +struct MbusUsbPowerEntry; struct MbusEvent; struct MbusEventCallbacks; struct MbusProcessInfo; @@ -466,27 +467,27 @@ int APS5_VABI sceMbusResolveByDeviceId(std::int32_t deviceClass, std::uint64_t d return 0; } -int APS5_VABI sceMbusResolveByHandle() { +int APS5_VABI sceMbusResolveByHandle(std::int32_t deviceClass, std::int32_t handle, std::int32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusResolveByPlayerId() { +int APS5_VABI sceMbusResolveByPlayerId(std::uint32_t deviceClass, std::uint32_t playerId, std::uint32_t first, std::uint32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusResolveByUserId() { +int APS5_VABI sceMbusResolveByUserId(std::uint32_t deviceClass, std::int32_t userId, std::uint32_t first, std::uint32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetAutoLoginMode() { +int APS5_VABI sceMbusSetAutoLoginMode(std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetCameraAppModuleFocus() { +int APS5_VABI sceMbusSetCameraAppModuleFocus(std::int32_t moduleId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -496,37 +497,37 @@ int APS5_VABI sceMbusSetDeviceFunctionState() { return 0; } -int APS5_VABI sceMbusSetDuckingSetting() { +int APS5_VABI sceMbusSetDuckingSetting(std::int32_t setting) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetProxy() { +int APS5_VABI sceMbusSetProxy(std::int32_t processId, std::int32_t first, std::int32_t second, std::int32_t third, std::uint64_t deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetRoutingMode() { +int APS5_VABI sceMbusSetRoutingMode(std::int32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetScratchData() { +int APS5_VABI sceMbusSetScratchData(std::uint64_t deviceId, std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetScratchDataUpdatedEventMask() { +int APS5_VABI sceMbusSetScratchDataUpdatedEventMask(std::uint32_t mask) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetSocialScreenAudioMode() { +int APS5_VABI sceMbusSetSocialScreenAudioMode(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetTestFlag() { +int APS5_VABI sceMbusSetTestFlag(std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -536,17 +537,17 @@ int APS5_VABI sceMbusSetThreadParam(std::int32_t priority, std::uint64_t affinit return 0; } -int APS5_VABI sceMbusSetUsbPowerBlackList() { +int APS5_VABI sceMbusSetUsbPowerBlackList(std::int32_t first, std::uint8_t count, const MbusUsbPowerEntry* entries) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetUserMuteStatus() { +int APS5_VABI sceMbusSetUserMuteStatus(std::int32_t userId, std::int32_t status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusSetVolumeDown() { +int APS5_VABI sceMbusSetVolumeDown(std::uint32_t deviceClass, std::uint8_t volume) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -556,7 +557,7 @@ int APS5_VABI sceMbusStartAudioOutAuxBroadcast() { return 0; } -int APS5_VABI sceMbusStartAudioOutSharePlay() { +int APS5_VABI sceMbusStartAudioOutSharePlay(std::int32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -575,17 +576,17 @@ int APS5_VABI sceMbusTerm() { return 0; } -int APS5_VABI sceMbusUnregisterDriver() { +int APS5_VABI sceMbusUnregisterDriver(std::int32_t driverId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusUserLogin() { +int APS5_VABI sceMbusUserLogin(std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMbusUserLogout() { +int APS5_VABI sceMbusUserLogout(std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index e4313ab744..3329a8071c 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,7 +8,7 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [libSceMbus firmware 3.20 pending exports](../../../core/libs/prx/libSceMbus/PendingExports.cpp) - remaining symbol names are present in [the PS5 3.20 libSceMbus export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMbus.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): GetDeviceInfoByConditionForDeviceService, GetDeviceInfoByCondition_ and SetDeviceFunctionState still require by-value aggregate layouts. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. @@ -19,3 +19,5 @@ - [sceMbus event subscriptions](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the subscription transports two 32-bit and two 64-bit fields whose service-side meanings remain unresolved. EventCreate uses 80-byte queue records and copies a 16-byte callback table; callback signatures and event fields still require the dispatch path. EventCallbackFuncsInit only clears memory and has no defined return value; its protected stub retains the existing integer return. Eight dump/BGM wrappers have confirmed zero-argument ABIs. EventBusStatusChangeUnsubscribe ignores its first incoming argument and consumes a 32-bit second argument; its first parameter type remains unresolved. - [sceMbus device and driver payloads](../../../core/libs/prx/libSceMbus/PendingExports.cpp): device-info records occupy 256 bytes, descriptions 264 bytes, simulated-handle status and Spark state 16 bytes each. Their service-side fields remain unresolved. Driver registration copies four callback pointers; driver-event notification transports two 64-bit values without dereferencing them. Init, IsBgmProhibited, ReenableBgmForShellUi and RemoveCameraAppModuleFocus consume no incoming arguments. + +- [sceMbus settings](../../../core/libs/prx/libSceMbus/PendingExports.cpp): USB power-list entries occupy 12 bytes, with an 8-bit count limited to 100; their fields remain unresolved. Several routing, scratch-data and proxy scalar meanings still require the service handler. Start/StopAudioOutAuxBroadcast and StopAudioOutSharePlay have confirmed zero-argument ABIs. From 0d4c2591a8dfaf607f682baf35908459a342e862 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:19:13 +0300 Subject: [PATCH 271/523] fix(mbus): restore event callbacks and condition query arguments --- core/libs/prx/libSceMbus/PendingExports.cpp | 10 ++++++++-- docs/dev/debt/libSceMbus.md | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index b69e0f5add..d0596729f0 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -7,7 +7,13 @@ struct MbusSparkState; struct MbusDriverCallbacks; struct MbusUsbPowerEntry; struct MbusEvent; -struct MbusEventCallbacks; +struct MbusDeviceCondition; +struct MbusEventCallbacks { + void (APS5_VABI *onOverflow)(std::int32_t eventHandle, void* userData); + void (APS5_VABI *onEvent)(std::int32_t eventHandle, void* userData, const MbusEvent* event); +}; + +static_assert(sizeof(MbusEventCallbacks) == 16); struct MbusProcessInfo; struct MbusStartupInfo; struct MbusStartupParam; @@ -372,7 +378,7 @@ int APS5_VABI sceMbusGetDeviceInfoByConditionForDeviceService() { return 0; } -int APS5_VABI sceMbusGetDeviceInfoByCondition_() { +int APS5_VABI sceMbusGetDeviceInfoByCondition_(const MbusDeviceCondition* condition, std::int32_t flags, MbusDeviceInfo* infos, std::int32_t capacity, std::int32_t* first, std::int32_t* second, std::uint32_t infoSize) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index 3329a8071c..a892a6c785 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,7 +8,7 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): GetDeviceInfoByConditionForDeviceService, GetDeviceInfoByCondition_ and SetDeviceFunctionState still require by-value aggregate layouts. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. +- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): GetDeviceInfoByConditionForDeviceService and SetDeviceFunctionState still require by-value aggregate layouts. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. @@ -16,8 +16,10 @@ - [sceMbus debug process and device queries](../../../core/libs/prx/libSceMbus/PendingExports.cpp): process information is transmitted as 16 bytes, startup input/output as eight bytes, control status as 24 bytes, internal information as 2832 bytes and priority information as 3088 bytes. Device enumeration writes a 32-bit count followed by 396-byte entries. These pointee fields and the semantics of unnamed process flags remain unresolved; declarations use distinct incomplete types instead of guessing their fields. -- [sceMbus event subscriptions](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the subscription transports two 32-bit and two 64-bit fields whose service-side meanings remain unresolved. EventCreate uses 80-byte queue records and copies a 16-byte callback table; callback signatures and event fields still require the dispatch path. EventCallbackFuncsInit only clears memory and has no defined return value; its protected stub retains the existing integer return. Eight dump/BGM wrappers have confirmed zero-argument ABIs. EventBusStatusChangeUnsubscribe ignores its first incoming argument and consumes a 32-bit second argument; its first parameter type remains unresolved. +- [sceMbus event subscriptions](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the subscription transports two 32-bit and two 64-bit fields whose service-side meanings remain unresolved. EventCreate uses 80-byte queue records and copies a 16-byte callback table; event fields still require the dispatch path; the overflow callback takes an event handle and user data, and the event callback additionally takes a pointer to the queued event. EventCallbackFuncsInit only clears memory and has no defined return value; its protected stub retains the existing integer return. Eight dump/BGM wrappers have confirmed zero-argument ABIs. EventBusStatusChangeUnsubscribe ignores its first incoming argument and consumes a 32-bit second argument; its first parameter type remains unresolved. - [sceMbus device and driver payloads](../../../core/libs/prx/libSceMbus/PendingExports.cpp): device-info records occupy 256 bytes, descriptions 264 bytes, simulated-handle status and Spark state 16 bytes each. Their service-side fields remain unresolved. Driver registration copies four callback pointers; driver-event notification transports two 64-bit values without dereferencing them. Init, IsBgmProhibited, ReenableBgmForShellUi and RemoveCameraAppModuleFocus consume no incoming arguments. - [sceMbus settings](../../../core/libs/prx/libSceMbus/PendingExports.cpp): USB power-list entries occupy 12 bytes, with an 8-bit count limited to 100; their fields remain unresolved. Several routing, scratch-data and proxy scalar meanings still require the service handler. Start/StopAudioOutAuxBroadcast and StopAudioOutSharePlay have confirmed zero-argument ABIs. + +- [sceMbusGetDeviceInfoByCondition_](../../../core/libs/prx/libSceMbus/PendingExports.cpp): its first parameter points to a 256-byte query condition; the second is a 32-bit scalar. Two optional 32-bit outputs and the sized 256-byte record array are confirmed, while condition fields and output meanings remain unresolved. From 5277d5b3385ca3c42bd02de44ec2bd4ce2047855 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:20:03 +0300 Subject: [PATCH 272/523] fix(mbus): validate device queries against the recovered abi --- core/libs/prx/libSceMbus/Export.cpp | 27 +++++++++++++++++++++------ core/libs/tests/GuestMbus.cpp | 24 ++++++++++++++++-------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceMbus/Export.cpp b/core/libs/prx/libSceMbus/Export.cpp index 6d67b46184..ca49b545be 100644 --- a/core/libs/prx/libSceMbus/Export.cpp +++ b/core/libs/prx/libSceMbus/Export.cpp @@ -53,14 +53,29 @@ std::int32_t APS5_VABI sceDeviceServiceGetEventState(std::int32_t clear) { } std::int32_t APS5_VABI sceDeviceServiceQueryDeviceInfo_(std::int32_t deviceClass, const DeviceServiceUsageFilter* filter, std::int32_t arg2, void* infos, std::int32_t maxInfos, std::int32_t* count, std::int32_t* reserved, std::uint32_t infoSize) { - (void)deviceClass; - (void)filter; (void)arg2; - (void)infos; (void)reserved; - (void)infoSize; - if (count == nullptr || maxInfos < 0) throw std::invalid_argument(std::string(__func__) + ": invalid argument"); - *count = 0; + constexpr auto invalidArgument = static_cast(0x809B0080u); + switch (deviceClass) { + case 0x1001: + case 0x2001: + case 0x3001: + case 0x4001: + case 0x4002: + case 0x5001: + case 0x6001: + case 0x7001: + case 0x9001: + if (filter != nullptr) return invalidArgument; + break; + case 0x8001: + if (filter == nullptr || filter->minimumUsage > filter->maximumUsage) return invalidArgument; + break; + default: + return invalidArgument; + } + if (infoSize != 112 || maxInfos < 0 || (maxInfos != 0 && infos == nullptr)) return invalidArgument; + if (count != nullptr) *count = 0; return 0; } diff --git a/core/libs/tests/GuestMbus.cpp b/core/libs/tests/GuestMbus.cpp index d1c7c85258..e867068157 100644 --- a/core/libs/tests/GuestMbus.cpp +++ b/core/libs/tests/GuestMbus.cpp @@ -12,7 +12,11 @@ struct DeviceServiceInitParam { std::uint32_t reserved; const DeviceServiceThreadParam* threadParam; }; -struct DeviceServiceUsageFilter; +struct DeviceServiceUsageFilter { + std::uint16_t usagePage; + std::uint16_t minimumUsage; + std::uint16_t maximumUsage; +}; extern "C" { std::int32_t APS5_VABI sceDeviceServiceInitialize(std::int32_t mode, const DeviceServiceInitParam* param); @@ -49,13 +53,17 @@ int main() { std::int32_t count = 7; std::int32_t reserved = 0; Require(sceDeviceServiceQueryDeviceInfo_(0x7001, 0, 0, info.data(), 1, &count, &reserved, info.size()) == 0 && count == 0, "a device was listed"); - bool threw = false; - try { - sceDeviceServiceQueryDeviceInfo_(0x7001, 0, 0, info.data(), 1, nullptr, &reserved, info.size()); - } catch (const std::invalid_argument&) { - threw = true; - } - Require(threw, "a null count was accepted"); + Require(sceDeviceServiceQueryDeviceInfo_(0x7001, nullptr, 0, info.data(), 1, nullptr, &reserved, info.size()) == 0, "optional count output rejected"); + Require(sceDeviceServiceQueryDeviceInfo_(0x7001, nullptr, 0, nullptr, 0, nullptr, nullptr, info.size()) == 0, "empty query rejected"); + Require(sceDeviceServiceQueryDeviceInfo_(0x7001, nullptr, 0, nullptr, 1, &count, nullptr, info.size()) == invalidArgument, "null record buffer accepted"); + Require(sceDeviceServiceQueryDeviceInfo_(0x7001, nullptr, 0, info.data(), -1, &count, nullptr, info.size()) == invalidArgument, "negative capacity accepted"); + Require(sceDeviceServiceQueryDeviceInfo_(0x7001, nullptr, 0, info.data(), 1, &count, nullptr, info.size() - 1) == invalidArgument, "incorrect record size accepted"); + DeviceServiceUsageFilter filter{1, 2, 3}; + Require(sceDeviceServiceQueryDeviceInfo_(0x8001, &filter, 0, info.data(), 1, &count, nullptr, info.size()) == 0, "valid HID filter rejected"); + Require(sceDeviceServiceQueryDeviceInfo_(0x8001, nullptr, 0, info.data(), 1, &count, nullptr, info.size()) == invalidArgument, "missing HID filter accepted"); + Require(sceDeviceServiceQueryDeviceInfo_(0x7001, &filter, 0, info.data(), 1, &count, nullptr, info.size()) == invalidArgument, "unexpected HID filter accepted"); + filter.minimumUsage = 4; + Require(sceDeviceServiceQueryDeviceInfo_(0x8001, &filter, 0, info.data(), 1, &count, nullptr, info.size()) == invalidArgument, "reversed HID usage range accepted"); Require(sceDeviceServiceGetGeneration() == 0, "the generation advanced without a device change"); Require(sceDeviceServiceTerminate() == 0, "termination failed"); std::puts("DeviceService tests passed"); From ef071a8a633721b1bb917b2838b0dad8d2632a43 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:22:40 +0300 Subject: [PATCH 273/523] fix(systemservice): restore app messaging signatures --- .../prx/libSceSystemService/PendingExports.cpp | 18 ++++++++++-------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 9ac7176e02..c0b6fe5877 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,44 +7,46 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } +struct AppMessagingMessage; + extern "C" { -int APS5_VABI sceAppMessagingClearEventFlag() { +int APS5_VABI sceAppMessagingClearEventFlag(std::int32_t appId, std::uint64_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingReceiveMsg() { +int APS5_VABI sceAppMessagingReceiveMsg(AppMessagingMessage* message) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingSendMsg() { +int APS5_VABI sceAppMessagingSendMsg(std::uint32_t appId, std::uint32_t messageId, const void* data, std::size_t size, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingSendMsgToShellCore() { +int APS5_VABI sceAppMessagingSendMsgToShellCore(std::uint32_t messageId, const void* data, std::size_t size, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingSendMsgToShellUI() { +int APS5_VABI sceAppMessagingSendMsgToShellUI(std::uint32_t messageId, const void* data, std::size_t size, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingSetEventFlag() { +int APS5_VABI sceAppMessagingSetEventFlag(std::int32_t appId, std::uint64_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingTryGetEventFlag() { +int APS5_VABI sceAppMessagingTryGetEventFlag(std::uint64_t mask, std::uint64_t* flags, std::uint8_t clear) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppMessagingTryReceiveMsg() { +int APS5_VABI sceAppMessagingTryReceiveMsg(AppMessagingMessage* message) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 2e396200ca..ee2fc16c5e 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -105,3 +105,5 @@ - [Running application ID](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - `sceSystemServiceGetAppIdOfRunningBigApp` reports 1 as the running title's app ID. The value is AnyPS5's own: the console's shell assigns app IDs and their values are not known here, so other calls that take an app ID must use the same one. `sceSystemServiceKillApp` with that ID and the arguments -1, 0 and 0, which [SwanStationPS5](https://github.com/darkxex/SwanStationPS5/blob/4cdc981d1a64ffb7f2af73f7a27a9e91a3cb5eb4/src/main.c#L1050-L1058) passes to close itself and [ps5-payload-dev's hbldr](https://github.com/ps5-payload-dev/websrv/blob/46374edee6627885abac717de24da7a1d73af6f1/src/ps5/hbldr.c#L425-L435) passes to close the running game, ends the title as its own `exit(0)` would: the guest's shutdown and `atexit` handlers run and the process exits with status 0. On the console the call returns and the shell then ends the title, and whether those handlers run there is unknown; here there is no shell, so the call does not return. Another app ID or other arguments throw. - [sceLncUtilKillApp, sceLncUtilKillAppWithReason](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the running title's app ID ends the title as `sceSystemServiceKillApp` does, which [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L219-L227) describes as a wrapper of `sceLncUtilKillApp`; the signatures follow its [launcher utility bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L262-L279). Another app ID throws, and so does a reason other than 0, whose meaning and effect on the console are not documented - [LoadExec descriptors](../../../core/libs/prx/libSceSystemService/LoadExec.cpp) (libSceSystemService) - on Linux, a call with an argument list replaces the process with `fexecve`, which first closes every descriptor above 2, and on macOS with `posix_spawn`, which closes them through `POSIX_SPAWN_CLOEXEC_DEFAULT`; a call without one uses `execv`, and the guest's descriptors stay open across it. On both paths descriptor 2 first gets back the host standard error a guest redirect kept, so the runtime's output does not follow the title's redirect (RetroArch's trace file, VLC's log) into the next executable. Which descriptors the console passes to the new process is not known + +- [AppMessaging receive buffers](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the supplied module confirms the eight messaging argument lists, 64-bit event masks, byte-sized clear flag and 8192-byte send limit. Receive writes an 8216-byte message whose field meanings still require the sender/service handler. The protected bodies remain unchanged. From 1c2ed0446c5095c39d59ecd8077b15bb506f4dcc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:23:57 +0300 Subject: [PATCH 274/523] fix(systemservice): restore launcher process and status signatures --- .../libSceSystemService/PendingExports.cpp | 26 +++++++++++-------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index c0b6fe5877..132a392cef 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,6 +7,10 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } + +struct LncLocalProcessParam; +struct LncAppStatus; +struct LncShellUiAppStatus; struct AppMessagingMessage; extern "C" { @@ -71,12 +75,12 @@ int APS5_VABI sceLncUtilActivateCdlg() { return 0; } -int APS5_VABI sceLncUtilAddLocalProcess() { +int APS5_VABI sceLncUtilAddLocalProcess(std::int32_t appId, const char* path, const char* const* argv, const LncLocalProcessParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilBlockAppSuspend() { +int APS5_VABI sceLncUtilBlockAppSuspend(const char* reason) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -121,22 +125,22 @@ int APS5_VABI sceLncUtilFinishSpecialResume() { return 0; } -int APS5_VABI sceLncUtilForceKillApp() { +int APS5_VABI sceLncUtilForceKillApp(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilForceKillLocalProcess() { +int APS5_VABI sceLncUtilForceKillLocalProcess(std::int32_t appId, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetAppCategory() { +int APS5_VABI sceLncUtilGetAppCategory(std::int32_t appId, std::uint32_t* category) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetAppCategoryType() { +int APS5_VABI sceLncUtilGetAppCategoryType(std::int32_t appId, std::uint32_t* categoryType) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -151,7 +155,7 @@ int APS5_VABI sceLncUtilGetAppId() { return 0; } -int APS5_VABI sceLncUtilGetAppIdListOfBigApp() { +int APS5_VABI sceLncUtilGetAppIdListOfBigApp(std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -161,7 +165,7 @@ int APS5_VABI sceLncUtilGetAppIdOfRunningBigApp() { return 0; } -int APS5_VABI sceLncUtilGetAppLaunchedUser() { +int APS5_VABI sceLncUtilGetAppLaunchedUser(std::int32_t appId, std::int32_t* userId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -171,17 +175,17 @@ int APS5_VABI sceLncUtilGetAppStatus() { return 0; } -int APS5_VABI sceLncUtilGetAppStatusList() { +int APS5_VABI sceLncUtilGetAppStatusList(LncAppStatus* statuses, std::uint32_t capacity, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetAppStatusListForShellUIReboot() { +int APS5_VABI sceLncUtilGetAppStatusListForShellUIReboot(LncShellUiAppStatus* statuses, std::uint32_t capacity, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetAppTitleId() { +int APS5_VABI sceLncUtilGetAppTitleId(std::int32_t appId, char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index ee2fc16c5e..5be32ae15e 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -107,3 +107,5 @@ - [LoadExec descriptors](../../../core/libs/prx/libSceSystemService/LoadExec.cpp) (libSceSystemService) - on Linux, a call with an argument list replaces the process with `fexecve`, which first closes every descriptor above 2, and on macOS with `posix_spawn`, which closes them through `POSIX_SPAWN_CLOEXEC_DEFAULT`; a call without one uses `execv`, and the guest's descriptors stay open across it. On both paths descriptor 2 first gets back the host standard error a guest redirect kept, so the runtime's output does not follow the title's redirect (RetroArch's trace file, VLC's log) into the next executable. Which descriptors the console passes to the new process is not known - [AppMessaging receive buffers](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the supplied module confirms the eight messaging argument lists, 64-bit event masks, byte-sized clear flag and 8192-byte send limit. Receive writes an 8216-byte message whose field meanings still require the sender/service handler. The protected bodies remain unchanged. + +- [LncUtil process and status payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process creation transmits a 72-byte parameter block; application-status entries occupy 64 bytes and shell-UI reboot entries 1336 bytes. Their fields remain unresolved. Title-ID output occupies 16 bytes; category, category type and launched-user outputs are 32-bit scalars. The argument lists are recovered through the indirect dispatch targets rather than the decompiler's apparent zero-argument wrappers. From 85b972ee406b34158da4ff0b06075ca3856e6eb7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:25:16 +0300 Subject: [PATCH 275/523] fix(systemservice): restore launcher query output types --- .../libSceSystemService/PendingExports.cpp | 42 +++++++++++-------- docs/dev/debt/libSceSystemService.md | 2 + 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 132a392cef..d25650ca75 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -8,6 +8,12 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } + +struct LncDaemonEvent; +struct LncShellUiEvent; +struct LncLocalProcessStatus; +struct LncCoredumpState; +struct LncVideoFinalizeState; struct LncLocalProcessParam; struct LncAppStatus; struct LncShellUiAppStatus; @@ -190,37 +196,37 @@ int APS5_VABI sceLncUtilGetAppTitleId(std::int32_t appId, char* titleId) { return 0; } -int APS5_VABI sceLncUtilGetAppType() { +int APS5_VABI sceLncUtilGetAppType(std::int32_t appId, std::uint32_t* type) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetCdlgExec() { +int APS5_VABI sceLncUtilGetCdlgExec(std::int32_t appId, bool* executing) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetCdlgType() { +int APS5_VABI sceLncUtilGetCdlgType(std::int32_t appId, std::uint32_t* type) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetCoredumpState() { +int APS5_VABI sceLncUtilGetCoredumpState(std::int32_t appId, std::int32_t processId, LncCoredumpState* state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetDbgExecutablePath() { +int APS5_VABI sceLncUtilGetDbgExecutablePath(char* path, std::uint64_t selector) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetEventForDaemon() { +int APS5_VABI sceLncUtilGetEventForDaemon(LncDaemonEvent* event) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetEventForShellUI() { +int APS5_VABI sceLncUtilGetEventForShellUI(LncShellUiEvent* event) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -230,7 +236,7 @@ int APS5_VABI sceLncUtilGetGpuCrashFullDumpAppStatus() { return 0; } -int APS5_VABI sceLncUtilGetLocalProcessStatusList() { +int APS5_VABI sceLncUtilGetLocalProcessStatusList(LncLocalProcessStatus* statuses, std::uint32_t capacity, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -240,17 +246,17 @@ int APS5_VABI sceLncUtilGetParentSocket() { return 0; } -int APS5_VABI sceLncUtilGetResultKillApp() { +int APS5_VABI sceLncUtilGetResultKillApp(std::uint32_t requestId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetResultLaunchAppByTitleId() { +int APS5_VABI sceLncUtilGetResultLaunchAppByTitleId(std::uint32_t requestId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetVideoFinalizeState() { +int APS5_VABI sceLncUtilGetVideoFinalizeState(std::int32_t appId, std::int32_t processId, LncVideoFinalizeState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -260,17 +266,17 @@ int APS5_VABI sceLncUtilInitialize() { return 0; } -int APS5_VABI sceLncUtilIsActiveCdlg() { +int APS5_VABI sceLncUtilIsActiveCdlg(bool* active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilIsAppLaunched() { +int APS5_VABI sceLncUtilIsAppLaunched(const char* titleId, bool* launched) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilIsAppSuspended() { +int APS5_VABI sceLncUtilIsAppSuspended(std::int32_t appId, bool* suspended) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -280,22 +286,22 @@ int APS5_VABI sceLncUtilIsCdlgActivated() { return 0; } -int APS5_VABI sceLncUtilIsCpuBudgetOfExtraAudioDevicesAvailable() { +bool APS5_VABI sceLncUtilIsCpuBudgetOfExtraAudioDevicesAvailable() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilIsHostBoot() { +int APS5_VABI sceLncUtilIsHostBoot(std::int32_t appId, bool* hostBoot) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilIsPs2Emu() { +int APS5_VABI sceLncUtilIsPs2Emu(bool* emulated) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilIsShellUiFgAndGameBgCpuMode() { +bool APS5_VABI sceLncUtilIsShellUiFgAndGameBgCpuMode() { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 5be32ae15e..70122d4518 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -109,3 +109,5 @@ - [AppMessaging receive buffers](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the supplied module confirms the eight messaging argument lists, 64-bit event masks, byte-sized clear flag and 8192-byte send limit. Receive writes an 8216-byte message whose field meanings still require the sender/service handler. The protected bodies remain unchanged. - [LncUtil process and status payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process creation transmits a 72-byte parameter block; application-status entries occupy 64 bytes and shell-UI reboot entries 1336 bytes. Their fields remain unresolved. Title-ID output occupies 16 bytes; category, category type and launched-user outputs are 32-bit scalars. The argument lists are recovered through the indirect dispatch targets rather than the decompiler's apparent zero-argument wrappers. + +- [LncUtil event and diagnostic payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process status entries are 36 bytes, coredump state 12 bytes and video-finalization state eight bytes; their field meanings remain unresolved. Daemon and shell-UI events are distinct output structures. GetDbgExecutablePath writes up to a 1024-byte protocol output and forwards a 64-bit selector whose meaning is unresolved. Boolean outputs and the two direct boolean budget/mode queries are confirmed. From bad5fb14a2bacc22c83cbcc548df59aa446c51f5 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:26:10 +0300 Subject: [PATCH 276/523] fix(systemservice): restore launcher lifecycle arguments --- .../libSceSystemService/PendingExports.cpp | 46 ++++++++++--------- docs/dev/debt/libSceSystemService.md | 2 + 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index d25650ca75..b7d7760e88 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -9,6 +9,8 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); + +struct LncLaunchParam; struct LncDaemonEvent; struct LncShellUiEvent; struct LncLocalProcessStatus; @@ -306,37 +308,37 @@ bool APS5_VABI sceLncUtilIsShellUiFgAndGameBgCpuMode() { return 0; } -int APS5_VABI sceLncUtilIsSpeculativeLaunch() { +int APS5_VABI sceLncUtilIsSpeculativeLaunch(std::int32_t appId, bool* speculative) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilKickCoredumpOnlyProcMem() { +int APS5_VABI sceLncUtilKickCoredumpOnlyProcMem(std::int32_t appId, std::int32_t processId, const char* first, const char* second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilKillLocalProcess() { +int APS5_VABI sceLncUtilKillLocalProcess(std::int32_t appId, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilLaunchApp() { +int APS5_VABI sceLncUtilLaunchApp(const char* titleId, const char* const* argv, const LncLaunchParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilLoadExec() { +int APS5_VABI sceLncUtilLoadExec(const char* path, const char* const* argv) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilNotifyCoredumpRequestEnd() { +int APS5_VABI sceLncUtilNotifyCoredumpRequestEnd(std::int32_t appId, std::int32_t processId, std::int32_t result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilNotifyCoredumpRequestProgress() { +int APS5_VABI sceLncUtilNotifyCoredumpRequestProgress(std::int32_t appId, std::int32_t processId, std::int32_t progress) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -346,12 +348,12 @@ int APS5_VABI sceLncUtilNotifyVshReady() { return 0; } -int APS5_VABI sceLncUtilRaiseException() { +int APS5_VABI sceLncUtilRaiseException(std::int32_t appId, std::uint64_t exception) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilRaiseExceptionLocalProcess() { +int APS5_VABI sceLncUtilRaiseExceptionLocalProcess(std::int32_t appId, std::int32_t processId, std::uint64_t exception) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -386,57 +388,57 @@ int APS5_VABI sceLncUtilReleaseCpuBudgetOfInGameStore() { return 0; } -int APS5_VABI sceLncUtilResumeApp() { +int APS5_VABI sceLncUtilResumeApp(std::int32_t appId, std::uint64_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilResumeLocalProcess() { +int APS5_VABI sceLncUtilResumeLocalProcess(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSetAppFocus() { +int APS5_VABI sceLncUtilSetAppFocus(std::int32_t appId, std::uint64_t first, std::uint64_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSetCdlgExec() { +int APS5_VABI sceLncUtilSetCdlgExec(std::int32_t appId, std::uint8_t executing) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSetControllerFocus() { +int APS5_VABI sceLncUtilSetControllerFocus(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSetControllerFocusPermission() { +int APS5_VABI sceLncUtilSetControllerFocusPermission(std::uint8_t permission) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilStartKillApp() { +int APS5_VABI sceLncUtilStartKillApp(std::int32_t appId, std::uint32_t* requestId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilStartLaunchAppByTitleId() { +int APS5_VABI sceLncUtilStartLaunchAppByTitleId(const char* titleId, const char* const* argv, const LncLaunchParam* param, std::uint32_t* requestId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSuspendApp() { +int APS5_VABI sceLncUtilSuspendApp(std::int32_t appId, std::uint64_t flags, std::int32_t reason) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSuspendBackgroundApp() { +int APS5_VABI sceLncUtilSuspendBackgroundApp(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilSuspendLocalProcess() { +int APS5_VABI sceLncUtilSuspendLocalProcess(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -446,12 +448,12 @@ int APS5_VABI sceLncUtilTerminate() { return 0; } -int APS5_VABI sceLncUtilTryBlockAppSuspend() { +int APS5_VABI sceLncUtilTryBlockAppSuspend(const char* reason) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilUnblockAppSuspend() { +int APS5_VABI sceLncUtilUnblockAppSuspend(const char* reason) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 70122d4518..6178137fe0 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -111,3 +111,5 @@ - [LncUtil process and status payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process creation transmits a 72-byte parameter block; application-status entries occupy 64 bytes and shell-UI reboot entries 1336 bytes. Their fields remain unresolved. Title-ID output occupies 16 bytes; category, category type and launched-user outputs are 32-bit scalars. The argument lists are recovered through the indirect dispatch targets rather than the decompiler's apparent zero-argument wrappers. - [LncUtil event and diagnostic payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process status entries are 36 bytes, coredump state 12 bytes and video-finalization state eight bytes; their field meanings remain unresolved. Daemon and shell-UI events are distinct output structures. GetDbgExecutablePath writes up to a 1024-byte protocol output and forwards a 64-bit selector whose meaning is unresolved. Boolean outputs and the two direct boolean budget/mode queries are confirmed. + +- [LncUtil launch and lifecycle payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 40-byte launch parameter fields and meanings of several 64-bit focus/resume/suspend/exception values remain unresolved. Launch and load-exec use null-terminated argument vectors; asynchronous launch/kill write a 32-bit request ID. The argument positions and transported widths are confirmed, but the service operations remain protected stubs. From 0cdc168bc3c2b69155114e6b29f8d41afc5a4b1f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:30:32 +0300 Subject: [PATCH 277/523] fix(systemservice): restore shell application data signatures --- .../libSceSystemService/PendingExports.cpp | 28 +++++++++---------- docs/dev/debt/libSceSystemService.md | 4 ++- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index b7d7760e88..5d978434c3 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,9 +7,7 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } - - - +struct ShellCoreAppLaunchTypeInfo; struct LncLaunchParam; struct LncDaemonEvent; struct LncShellUiEvent; @@ -533,7 +531,7 @@ int APS5_VABI sceShellCoreUtilActivateTerm() { return 0; } -int APS5_VABI sceShellCoreUtilChangeRunLevel() { +int APS5_VABI sceShellCoreUtilChangeRunLevel(std::int32_t runLevel) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -588,7 +586,7 @@ int APS5_VABI sceShellCoreUtilDeleteDiscInstalledTitleWorkaroundFile() { return 0; } -int APS5_VABI sceShellCoreUtilDeleteDownloadedHidConfigFile() { +int APS5_VABI sceShellCoreUtilDeleteDownloadedHidConfigFile(std::uint32_t deviceType) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -603,12 +601,12 @@ int APS5_VABI sceShellCoreUtilDeleteDownloadedTitleWorkaroundFile() { return 0; } -int APS5_VABI sceShellCoreUtilDoFsck() { +int APS5_VABI sceShellCoreUtilDoFsck(const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilDownloadHidConfigFileFromServer() { +int APS5_VABI sceShellCoreUtilDownloadHidConfigFileFromServer(std::uint32_t deviceType) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -638,7 +636,7 @@ int APS5_VABI sceShellCoreUtilExecuteCrashReport() { return 0; } -int APS5_VABI sceShellCoreUtilExfatFormatExternalHdd() { +int APS5_VABI sceShellCoreUtilExfatFormatExternalHdd(const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -653,12 +651,12 @@ int APS5_VABI sceShellCoreUtilExitMiniApp() { return 0; } -int APS5_VABI sceShellCoreUtilExitMiniAppWithValue() { +int APS5_VABI sceShellCoreUtilExitMiniAppWithValue(const char* value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilFormatExternalHdd() { +int APS5_VABI sceShellCoreUtilFormatExternalHdd(const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -668,27 +666,27 @@ int APS5_VABI sceShellCoreUtilFormatHddForRestore() { return 0; } -int APS5_VABI sceShellCoreUtilGetAppData() { +int APS5_VABI sceShellCoreUtilGetAppData(const char* key, void* data, std::int32_t capacity, std::int32_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetAppEnableTTS() { +int APS5_VABI sceShellCoreUtilGetAppEnableTTS(std::int32_t appId, bool* enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetAppEnterButtonAssign() { +int APS5_VABI sceShellCoreUtilGetAppEnterButtonAssign(std::int32_t appId, std::int32_t* assignment) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetAppLaunchTypeInfo() { +int APS5_VABI sceShellCoreUtilGetAppLaunchTypeInfo(std::int32_t appId, ShellCoreAppLaunchTypeInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetAppLaunchedParamInt() { +int APS5_VABI sceShellCoreUtilGetAppLaunchedParamInt(std::int32_t appId, const char* key, std::int32_t* value) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 6178137fe0..21292cfdde 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -19,7 +19,7 @@ - [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the time zone (parameter 4) and summer time (parameter 5) follow the host's local time zone at the moment of the call. Titles compute the local time as UTC plus the time zone in minutes plus 60 minutes when summer time is 1 ([Apollo](https://github.com/bucanero/apollo-ps4/blob/252a9d5a187c463994263009851a179f4f910c49/source/exec_cmd.c#L70-L92), [GoldHEN's frame logger](https://github.com/GoldHEN/GoldHEN_Plugins_Repository/blob/6c3a06f53d9caebadfb0aeed9bb94119012010a7/plugin_src/frame_logger/source/main.c#L60-L82), and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/413d2e3518fb435f40971d238fe0ecae7ee551da/src/SharpProspero/Platform/SystemParameters.cs#L75-L79) calls them the offset from UTC in minutes and whether summer time is in effect), so the time zone is the host's current offset east of UTC less the summer time shift while summer time is in effect: 60 minutes on Linux, and on Windows the C runtime's own summer time bias (`_get_dstbias`), which is also what shifted its local time. For a host zone whose summer time is not one hour, Linux keeps the sum right and the standard offset wrong, and Windows the opposite; how the console reports such a zone is unknown. libkernel's `sceKernelConvertUtcToLocaltime`, `sceKernelConvertLocaltimeToUtc` and `sceKernelGettimezone` still use UTC - [sceSystemServiceInitializePlayerDialogParam](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - a null param throws: what the console does with it is not measured. The console run in [prosper](https://github.com/mattias800/prosper/blob/40d95db32efe1ad00106a5b9a84e018fffa9ecff/prosper/tests/data/console_oracle/nethttp.golden.tsv#L46) shows only the 0x40 bytes written (size 0x40, user ID -1 at 0xC, zeros), so the field names and the mode and target account fields follow [SharpProspero's struct](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L637-L655), and the `void` return [its declaration](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L419-L426); the console left 0x800693020 in `rax`, not an SCE error code. Closes when: the null case is measured on a PS5 -- [libSceSystemService firmware 3.20 pending exports](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) - 475 symbol names are present in [the PS5 3.20 libSceSystemService export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemService.c). Their signatures and console behaviour are unverified. The declarations use the existing no-parameter unknown-signature convention solely to resolve imports, and every call throws through `NotImplemented_nid_no_patch`. +- [libSceSystemService pending exports](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): unreviewed argument lists and service behavior remain unresolved. The supplied module defines 381 of the 477 pending names; the other 96 require another module/version or caller evidence. Confirmed argument lists are being restored independently of their protected throwing bodies; remaining empty declarations must not be treated as proof of a zero-argument ABI. - [sceSystemServiceLaunchSystemDeeplink](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature - [sceShellCoreUtilLaunchByUriParamInit](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature - [sceSystemStateMgrTickVideoPlayback](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature @@ -113,3 +113,5 @@ - [LncUtil event and diagnostic payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process status entries are 36 bytes, coredump state 12 bytes and video-finalization state eight bytes; their field meanings remain unresolved. Daemon and shell-UI events are distinct output structures. GetDbgExecutablePath writes up to a 1024-byte protocol output and forwards a 64-bit selector whose meaning is unresolved. Boolean outputs and the two direct boolean budget/mode queries are confirmed. - [LncUtil launch and lifecycle payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 40-byte launch parameter fields and meanings of several 64-bit focus/resume/suspend/exception values remain unresolved. Launch and load-exec use null-terminated argument vectors; asynchronous launch/kill write a 32-bit request ID. The argument positions and transported widths are confirmed, but the service operations remain protected stubs. + +- [ShellCoreUtil application data](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): GetAppLaunchTypeInfo writes eight bytes whose fields are unresolved. Application-data and launched-parameter keys are null-terminated strings, with 32-bit capacities/outputs. Format/fsck paths and mini-app return values are strings. File-formatting and service implementations remain protected stubs. From c13664d573c675defb9abfafbc6b40a171d7fd12 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:31:32 +0300 Subject: [PATCH 278/523] fix(systemservice): restore shell query outputs and string buffers --- .../libSceSystemService/PendingExports.cpp | 20 +++++++++---------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 5d978434c3..5969fc5468 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -691,17 +691,17 @@ int APS5_VABI sceShellCoreUtilGetAppLaunchedParamInt(std::int32_t appId, const c return 0; } -int APS5_VABI sceShellCoreUtilGetAppLaunchedParamIntByBudgetType() { +int APS5_VABI sceShellCoreUtilGetAppLaunchedParamIntByBudgetType(std::int32_t budgetType, const char* key, std::int32_t* value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetAppLaunchedParamString() { +int APS5_VABI sceShellCoreUtilGetAppLaunchedParamString(std::int32_t appId, const char* key, char* value, std::int32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetAppLaunchedParamStringByBudgetType() { +int APS5_VABI sceShellCoreUtilGetAppLaunchedParamStringByBudgetType(std::int32_t budgetType, const char* key, char* value, std::int32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -716,17 +716,17 @@ int APS5_VABI sceShellCoreUtilGetBasicProductShape() { return 0; } -int APS5_VABI sceShellCoreUtilGetCheckerString() { +int APS5_VABI sceShellCoreUtilGetCheckerString(const char* key, char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetCheckerStringEx() { +int APS5_VABI sceShellCoreUtilGetCheckerStringEx(const char* firstKey, const char* secondKey, char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetCloudClientStatus() { +int APS5_VABI sceShellCoreUtilGetCloudClientStatus(std::uint32_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -771,22 +771,22 @@ int APS5_VABI sceShellCoreUtilGetCrashReportUploadStatus() { return 0; } -int APS5_VABI sceShellCoreUtilGetDeviceIndexBehavior() { +int APS5_VABI sceShellCoreUtilGetDeviceIndexBehavior(std::uint32_t* first, std::uint32_t* second, std::uint32_t* third) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetDeviceIndexBehaviorWithTimeout() { +int APS5_VABI sceShellCoreUtilGetDeviceIndexBehaviorWithTimeout(std::uint32_t* first, std::uint32_t* second, std::uint32_t* third, std::uint8_t timeout) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetEffectiveTotalSizeOfUserPartition() { +int APS5_VABI sceShellCoreUtilGetEffectiveTotalSizeOfUserPartition(std::uint64_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetFreeSizeOfAvContentsTmp() { +std::uint64_t APS5_VABI sceShellCoreUtilGetFreeSizeOfAvContentsTmp() { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 21292cfdde..5b13cb0bd8 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -115,3 +115,5 @@ - [LncUtil launch and lifecycle payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 40-byte launch parameter fields and meanings of several 64-bit focus/resume/suspend/exception values remain unresolved. Launch and load-exec use null-terminated argument vectors; asynchronous launch/kill write a 32-bit request ID. The argument positions and transported widths are confirmed, but the service operations remain protected stubs. - [ShellCoreUtil application data](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): GetAppLaunchTypeInfo writes eight bytes whose fields are unresolved. Application-data and launched-parameter keys are null-terminated strings, with 32-bit capacities/outputs. Format/fsck paths and mini-app return values are strings. File-formatting and service implementations remain protected stubs. + +- [ShellCoreUtil queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): launched-parameter strings have signed 32-bit capacities; checker strings use machine-sized capacities and one or two string keys. Cloud status and device-index outputs are 32-bit scalars; the timeout variant consumes one byte. Partition-size output and the direct temporary-content free-size return are 64-bit. Device-index field meanings and the service-side checker protocol remain unresolved. From 0d911b3aecaf7282e1b8dc0b0275edd81fd63503 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:32:46 +0300 Subject: [PATCH 279/523] fix(systemservice): restore storage and hid query signatures --- .../libSceSystemService/PendingExports.cpp | 24 +++++++++---------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 5969fc5468..420763bbe6 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -791,22 +791,22 @@ std::uint64_t APS5_VABI sceShellCoreUtilGetFreeSizeOfAvContentsTmp() { return 0; } -int APS5_VABI sceShellCoreUtilGetFreeSizeOfUserPartition() { +int APS5_VABI sceShellCoreUtilGetFreeSizeOfUserPartition(std::uint64_t* firstSize, std::uint64_t* secondSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetFsckProgress() { +int APS5_VABI sceShellCoreUtilGetFsckProgress(const char* path, std::uint32_t* progress) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetGameLiveStreamingStatus() { +int APS5_VABI sceShellCoreUtilGetGameLiveStreamingStatus(bool* active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetGnmCompositorOnScreenProfilerFlag() { +bool APS5_VABI sceShellCoreUtilGetGnmCompositorOnScreenProfilerFlag() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -816,32 +816,32 @@ int APS5_VABI sceShellCoreUtilGetGpuLoadEmulationMode() { return 0; } -int APS5_VABI sceShellCoreUtilGetGpuLoadEmulationModeByAppId() { +int APS5_VABI sceShellCoreUtilGetGpuLoadEmulationModeByAppId(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigFileInfoString() { +int APS5_VABI sceShellCoreUtilGetHidConfigFileInfoString(char* value, std::size_t capacity, std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigFileString() { +int APS5_VABI sceShellCoreUtilGetHidConfigFileString(char* value, std::size_t capacity, std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigName() { +int APS5_VABI sceShellCoreUtilGetHidConfigName(char* value, std::size_t capacity, std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigNum() { +int APS5_VABI sceShellCoreUtilGetHidConfigNum(std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetIDUMode() { +int APS5_VABI sceShellCoreUtilGetIDUMode(std::uint8_t* mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -856,12 +856,12 @@ int APS5_VABI sceShellCoreUtilGetManifestFileStatus() { return 0; } -int APS5_VABI sceShellCoreUtilGetNeedSizeOfAppContent() { +int APS5_VABI sceShellCoreUtilGetNeedSizeOfAppContent(std::uint64_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetNetEvConfigFileInfoString() { +int APS5_VABI sceShellCoreUtilGetNetEvConfigFileInfoString(char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 5b13cb0bd8..3277bb053c 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -117,3 +117,5 @@ - [ShellCoreUtil application data](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): GetAppLaunchTypeInfo writes eight bytes whose fields are unresolved. Application-data and launched-parameter keys are null-terminated strings, with 32-bit capacities/outputs. Format/fsck paths and mini-app return values are strings. File-formatting and service implementations remain protected stubs. - [ShellCoreUtil queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): launched-parameter strings have signed 32-bit capacities; checker strings use machine-sized capacities and one or two string keys. Cloud status and device-index outputs are 32-bit scalars; the timeout variant consumes one byte. Partition-size output and the direct temporary-content free-size return are 64-bit. Device-index field meanings and the service-side checker protocol remain unresolved. + +- [ShellCoreUtil storage and HID queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): partition free sizes and required application-content size are 64-bit outputs; fsck takes a path and a 32-bit progress output. HID string queries transport a 64-bit capacity and 32-bit selector, while output-vector lengths sign-extend the capacity's low 32 bits. The selector meaning and IDU mode byte values remain unresolved. Streaming status and the compositor-profiler return are boolean; the compositor-profiler and current GPU-load queries consume no caller arguments. From 8b48df0b928de51d4d956822109432eae2cde7dd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:33:32 +0300 Subject: [PATCH 280/523] fix(systemservice): restore account and display query arguments --- .../libSceSystemService/PendingExports.cpp | 37 +++++++++++-------- docs/dev/debt/libSceSystemService.md | 2 + 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 420763bbe6..abdf06671c 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,6 +7,13 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } +struct ShellCorePbtcUserInfoList; +struct ShellCorePrivacyEventData; +struct ShellCorePsnAccountInfoFirst; +struct ShellCorePsnAccountInfoSecond; +struct ShellCorePsnAccountInfoThird; +struct ShellCorePsnAccountInfoFourth; +struct ShellCorePsnAccountInfoFifth; struct ShellCoreAppLaunchTypeInfo; struct LncLaunchParam; struct LncDaemonEvent; @@ -876,12 +883,12 @@ int APS5_VABI sceShellCoreUtilGetPapcGamePcl() { return 0; } -int APS5_VABI sceShellCoreUtilGetPbtcUserInfoList() { +int APS5_VABI sceShellCoreUtilGetPbtcUserInfoList(ShellCorePbtcUserInfoList* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetPlatformPrivacyDefinitionEventData() { +int APS5_VABI sceShellCoreUtilGetPlatformPrivacyDefinitionEventData(const char* firstKey, const char* secondKey, ShellCorePrivacyEventData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -896,32 +903,32 @@ int APS5_VABI sceShellCoreUtilGetProgressOfFsck() { return 0; } -int APS5_VABI sceShellCoreUtilGetPs4AppCategoryDefaultsListId() { +int APS5_VABI sceShellCoreUtilGetPs4AppCategoryDefaultsListId(void* data, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetPs4AppCategoryForTitleId() { +int APS5_VABI sceShellCoreUtilGetPs4AppCategoryForTitleId(const char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetPsStoreIconLayout() { +int APS5_VABI sceShellCoreUtilGetPsStoreIconLayout(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetPsStoreIconState() { +int APS5_VABI sceShellCoreUtilGetPsStoreIconState(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetPsnAccountInfo() { +int APS5_VABI sceShellCoreUtilGetPsnAccountInfo(ShellCorePsnAccountInfoFirst* first, ShellCorePsnAccountInfoSecond* second, ShellCorePsnAccountInfoThird* third, ShellCorePsnAccountInfoFourth* fourth, ShellCorePsnAccountInfoFifth* fifth) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetRemotePlayStatus() { +int APS5_VABI sceShellCoreUtilGetRemotePlayStatus(bool* active) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -931,37 +938,37 @@ int APS5_VABI sceShellCoreUtilGetRunLevel() { return 0; } -int APS5_VABI sceShellCoreUtilGetSharePlayStatus() { +int APS5_VABI sceShellCoreUtilGetSharePlayStatus(bool* active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetSocialScreenStatus() { +int APS5_VABI sceShellCoreUtilGetSocialScreenStatus(bool* active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetSplashScreenState() { +int APS5_VABI sceShellCoreUtilGetSplashScreenState(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetSupportSiteURL() { +int APS5_VABI sceShellCoreUtilGetSupportSiteURL(std::uint32_t first, std::uint32_t second, char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetSuspendConfirmationDialogFlag() { +int APS5_VABI sceShellCoreUtilGetSuspendConfirmationDialogFlag(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetSystemBGState() { +int APS5_VABI sceShellCoreUtilGetSystemBGState(std::int32_t appId, std::uint32_t* state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetSystemBGWaveColor() { +int APS5_VABI sceShellCoreUtilGetSystemBGWaveColor(std::int32_t appId, std::uint32_t* color) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 3277bb053c..4c9db52da8 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -119,3 +119,5 @@ - [ShellCoreUtil queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): launched-parameter strings have signed 32-bit capacities; checker strings use machine-sized capacities and one or two string keys. Cloud status and device-index outputs are 32-bit scalars; the timeout variant consumes one byte. Partition-size output and the direct temporary-content free-size return are 64-bit. Device-index field meanings and the service-side checker protocol remain unresolved. - [ShellCoreUtil storage and HID queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): partition free sizes and required application-content size are 64-bit outputs; fsck takes a path and a 32-bit progress output. HID string queries transport a 64-bit capacity and 32-bit selector, while output-vector lengths sign-extend the capacity's low 32 bits. The selector meaning and IDU mode byte values remain unresolved. Streaming status and the compositor-profiler return are boolean; the compositor-profiler and current GPU-load queries consume no caller arguments. + +- [ShellCoreUtil account and privacy payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Pbtc user information occupies 800 bytes; privacy-event data occupies eight bytes after two string keys, each limited to 32 characters. The five account-information outputs occupy 8, 104, 136, 1024 and 1032 bytes; their fields remain unresolved. Category-default list output has a byte capacity and unresolved element format. Remote-play/share-play/social-screen outputs are boolean; application display-state queries take a 32-bit application ID. Only the support-site query signature is restored; no endpoint content is included. From d34675bb212a671f434de9160f7925dce5ddd8b8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:34:28 +0300 Subject: [PATCH 281/523] fix(systemservice): restore telemetry and user query signatures --- .../libSceSystemService/PendingExports.cpp | 30 ++++++++++--------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index abdf06671c..748f5f12cf 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,6 +7,8 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } +struct ShellCoreTelemetryToken; +struct ShellCoreUmmStatus; struct ShellCorePbtcUserInfoList; struct ShellCorePrivacyEventData; struct ShellCorePsnAccountInfoFirst; @@ -973,37 +975,37 @@ int APS5_VABI sceShellCoreUtilGetSystemBGWaveColor(std::int32_t appId, std::uint return 0; } -int APS5_VABI sceShellCoreUtilGetSystemBGWaveState() { +int APS5_VABI sceShellCoreUtilGetSystemBGWaveState(std::int32_t appId, std::uint32_t* state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetTelemetryToken() { +int APS5_VABI sceShellCoreUtilGetTelemetryToken(ShellCoreTelemetryToken* token) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetTitleWorkaroundFileInfoString() { +int APS5_VABI sceShellCoreUtilGetTitleWorkaroundFileInfoString(char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetTitleWorkaroundFileString() { +int APS5_VABI sceShellCoreUtilGetTitleWorkaroundFileString(char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetUIStatus() { +int APS5_VABI sceShellCoreUtilGetUIStatus(std::uint64_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetUmmStatus() { +int APS5_VABI sceShellCoreUtilGetUmmStatus(std::uint32_t id, ShellCoreUmmStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetUserFocus() { +int APS5_VABI sceShellCoreUtilGetUserFocus(std::int32_t userId, std::int32_t* appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1013,7 +1015,7 @@ int APS5_VABI sceShellCoreUtilGetUserIdOfMorpheusUser() { return 0; } -int APS5_VABI sceShellCoreUtilGetVMStatsForTelemetry() { +int APS5_VABI sceShellCoreUtilGetVMStatsForTelemetry(char* value, std::size_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1028,7 +1030,7 @@ int APS5_VABI sceShellCoreUtilGoBackToKratosCurrentSessionGame() { return 0; } -int APS5_VABI sceShellCoreUtilHideBlocksFromUser() { +int APS5_VABI sceShellCoreUtilHideBlocksFromUser(std::uint64_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1043,12 +1045,12 @@ int APS5_VABI sceShellCoreUtilIsAccessibilityZoomLocked() { return 0; } -int APS5_VABI sceShellCoreUtilIsBgmCpuBudgetAcquired() { +bool APS5_VABI sceShellCoreUtilIsBgmCpuBudgetAcquired() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsBgmCpuBudgetAvailable() { +bool APS5_VABI sceShellCoreUtilIsBgmCpuBudgetAvailable() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1058,17 +1060,17 @@ int APS5_VABI sceShellCoreUtilIsBgmPlaying() { return 0; } -int APS5_VABI sceShellCoreUtilIsExternalStorageAppMoveInProgress() { +bool APS5_VABI sceShellCoreUtilIsExternalStorageAppMoveInProgress() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsEyeToEyeDistanceAdjusted() { +int APS5_VABI sceShellCoreUtilIsEyeToEyeDistanceAdjusted(std::uint32_t userId, std::uint8_t* adjusted) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsGameLiveStreamingOnAir() { +bool APS5_VABI sceShellCoreUtilIsGameLiveStreamingOnAir() { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 4c9db52da8..3e9d921cd8 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -121,3 +121,5 @@ - [ShellCoreUtil storage and HID queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): partition free sizes and required application-content size are 64-bit outputs; fsck takes a path and a 32-bit progress output. HID string queries transport a 64-bit capacity and 32-bit selector, while output-vector lengths sign-extend the capacity's low 32 bits. The selector meaning and IDU mode byte values remain unresolved. Streaming status and the compositor-profiler return are boolean; the compositor-profiler and current GPU-load queries consume no caller arguments. - [ShellCoreUtil account and privacy payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Pbtc user information occupies 800 bytes; privacy-event data occupies eight bytes after two string keys, each limited to 32 characters. The five account-information outputs occupy 8, 104, 136, 1024 and 1032 bytes; their fields remain unresolved. Category-default list output has a byte capacity and unresolved element format. Remote-play/share-play/social-screen outputs are boolean; application display-state queries take a 32-bit application ID. Only the support-site query signature is restored; no endpoint content is included. + +- [ShellCoreUtil telemetry and user queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): telemetry-token output is 4112 bytes, including an eight-byte service result at offset 4104; its meaning remains unresolved. UMM status is an eight-byte output with unresolved fields. UI status is a 64-bit event mask; VM statistics and workaround-file strings use machine-sized capacities. HideBlocksFromUser transports a 64-bit value and a 32-bit value whose exact semantics remain unresolved. Eye-distance adjustment writes one byte. From 315c4b65a1760e85067063d0bc3f6cfbedf41b70 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:35:30 +0300 Subject: [PATCH 282/523] fix(systemservice): restore shell navigation and mount arguments --- .../libSceSystemService/PendingExports.cpp | 29 ++++++++++--------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 748f5f12cf..6801d3acc5 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,6 +7,7 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } +struct ShellCoreLaunchByUriParam; struct ShellCoreTelemetryToken; struct ShellCoreUmmStatus; struct ShellCorePbtcUserInfoList; @@ -1080,7 +1081,7 @@ int APS5_VABI sceShellCoreUtilIsIDUState() { return 0; } -int APS5_VABI sceShellCoreUtilIsImposeScreenOverlaid() { +bool APS5_VABI sceShellCoreUtilIsImposeScreenOverlaid() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1095,7 +1096,7 @@ int APS5_VABI sceShellCoreUtilIsInternalKratosUser() { return 0; } -int APS5_VABI sceShellCoreUtilIsKilledOrSuspendedByLogout() { +int APS5_VABI sceShellCoreUtilIsKilledOrSuspendedByLogout(std::int32_t first, std::int32_t second, std::uint8_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1105,22 +1106,22 @@ int APS5_VABI sceShellCoreUtilIsNeededCrashReport() { return 0; } -int APS5_VABI sceShellCoreUtilIsPowerSaveAlertRequested() { +bool APS5_VABI sceShellCoreUtilIsPowerSaveAlertRequested() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsRemotePlayCpuBudgetAcquired() { +bool APS5_VABI sceShellCoreUtilIsRemotePlayCpuBudgetAcquired() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsScreenSaverOn() { +bool APS5_VABI sceShellCoreUtilIsScreenSaverOn() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsSharePlayCpuBudgetAcquired() { +bool APS5_VABI sceShellCoreUtilIsSharePlayCpuBudgetAcquired() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1135,17 +1136,17 @@ int APS5_VABI sceShellCoreUtilIsTemperatureDanger() { return 0; } -int APS5_VABI sceShellCoreUtilIsTitleWorkaroundEnabled() { +int APS5_VABI sceShellCoreUtilIsTitleWorkaroundEnabled(std::int32_t selector, const char* key) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilIsUsbMassStorageMounted() { +bool APS5_VABI sceShellCoreUtilIsUsbMassStorageMounted(std::uint32_t deviceIndex) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilLaunchByUri() { +int APS5_VABI sceShellCoreUtilLaunchByUri(const char* uri, const ShellCoreLaunchByUriParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1160,12 +1161,12 @@ int APS5_VABI sceShellCoreUtilMakeManifestFile() { return 0; } -int APS5_VABI sceShellCoreUtilMountAppRight() { +int APS5_VABI sceShellCoreUtilMountAppRight(const char* key) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilMountDownloadDataForShellUI() { +int APS5_VABI sceShellCoreUtilMountDownloadDataForShellUI(const char* key) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1180,17 +1181,17 @@ int APS5_VABI sceShellCoreUtilMountHddForRestore() { return 0; } -int APS5_VABI sceShellCoreUtilNavigateToAnotherApp() { +int APS5_VABI sceShellCoreUtilNavigateToAnotherApp(std::uint32_t target) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilNavigateToGoHome() { +int APS5_VABI sceShellCoreUtilNavigateToGoHome(std::uint32_t target) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilNavigateToLaunchedApp() { +int APS5_VABI sceShellCoreUtilNavigateToLaunchedApp(std::uint32_t target) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 3e9d921cd8..6cf0154ae6 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -123,3 +123,5 @@ - [ShellCoreUtil account and privacy payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Pbtc user information occupies 800 bytes; privacy-event data occupies eight bytes after two string keys, each limited to 32 characters. The five account-information outputs occupy 8, 104, 136, 1024 and 1032 bytes; their fields remain unresolved. Category-default list output has a byte capacity and unresolved element format. Remote-play/share-play/social-screen outputs are boolean; application display-state queries take a 32-bit application ID. Only the support-site query signature is restored; no endpoint content is included. - [ShellCoreUtil telemetry and user queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): telemetry-token output is 4112 bytes, including an eight-byte service result at offset 4104; its meaning remains unresolved. UMM status is an eight-byte output with unresolved fields. UI status is a 64-bit event mask; VM statistics and workaround-file strings use machine-sized capacities. HideBlocksFromUser transports a 64-bit value and a 32-bit value whose exact semantics remain unresolved. Eye-distance adjustment writes one byte. + +- [ShellCoreUtil launch and status arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): LaunchByUri reads an eight-byte parameter containing two 32-bit fields and swaps their order in its message; field meanings remain unresolved. Navigation consumes one 32-bit value, not the phantom vector/register arguments shown by decompilation. Workaround checks and mount requests take strings. Logout-state queries transmit two 32-bit values and return a byte output; selector meanings remain unresolved. From c1843d13506d59acd68d2f5999ab340584583e25 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:36:12 +0300 Subject: [PATCH 283/523] fix(systemservice): restore notification and error report arguments --- .../libSceSystemService/PendingExports.cpp | 28 +++++++++---------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 6801d3acc5..8a26ee3e4d 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -1196,12 +1196,12 @@ int APS5_VABI sceShellCoreUtilNavigateToLaunchedApp(std::uint32_t target) { return 0; } -int APS5_VABI sceShellCoreUtilNotificationCancelForIDU() { +int APS5_VABI sceShellCoreUtilNotificationCancelForIDU(std::int64_t notificationId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilNotificationRequestedForIDU() { +int APS5_VABI sceShellCoreUtilNotificationRequestedForIDU(std::int64_t notificationId, const char* text, std::uint32_t type) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1211,12 +1211,12 @@ int APS5_VABI sceShellCoreUtilNotifyBgmCoreTermination() { return 0; } -int APS5_VABI sceShellCoreUtilNotifyFsReadError() { +int APS5_VABI sceShellCoreUtilNotifyFsReadError(std::int32_t first, std::int32_t second, std::int32_t third, const char* detail) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilNotifyImeStatusChanged() { +int APS5_VABI sceShellCoreUtilNotifyImeStatusChanged(std::int32_t appId, std::uint8_t status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1226,7 +1226,7 @@ int APS5_VABI sceShellCoreUtilNotifyPsnAccountInfoReceived() { return 0; } -int APS5_VABI sceShellCoreUtilNotifySystemSuspendTelemetryInfo() { +int APS5_VABI sceShellCoreUtilNotifySystemSuspendTelemetryInfo(std::uint32_t info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1236,22 +1236,22 @@ int APS5_VABI sceShellCoreUtilPfAuthClientConsoleTokenClearCache() { return 0; } -int APS5_VABI sceShellCoreUtilPostActivityForPsNow() { +int APS5_VABI sceShellCoreUtilPostActivityForPsNow(std::int32_t type, const char* activity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilPostErrorLog() { +int APS5_VABI sceShellCoreUtilPostErrorLog(const char* firstText, std::uint32_t firstValue, const char* secondText, const char* thirdText, std::uint32_t secondValue, const char* fourthText) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilPostLaunchConfirmResult() { +int APS5_VABI sceShellCoreUtilPostLaunchConfirmResult(std::uint32_t first, std::uint32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilPostPsmEventToShellUI() { +int APS5_VABI sceShellCoreUtilPostPsmEventToShellUI(std::int32_t eventId, const char* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1286,12 +1286,12 @@ int APS5_VABI sceShellCoreUtilReleaseSharePlayCpuBudget() { return 0; } -int APS5_VABI sceShellCoreUtilReportSessionErrorToGaikaiController() { +int APS5_VABI sceShellCoreUtilReportSessionErrorToGaikaiController(std::int32_t error, const char* detail) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilReportUnexpectedFatalErrorToSystemTelemetry() { +int APS5_VABI sceShellCoreUtilReportUnexpectedFatalErrorToSystemTelemetry(std::int32_t error, const char* detail) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1301,17 +1301,17 @@ int APS5_VABI sceShellCoreUtilRequestCameraCalibration() { return 0; } -int APS5_VABI sceShellCoreUtilRequestEjectDevice() { +int APS5_VABI sceShellCoreUtilRequestEjectDevice(const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilRequestRebootApp() { +int APS5_VABI sceShellCoreUtilRequestRebootApp(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilRequestShutdown() { +int APS5_VABI sceShellCoreUtilRequestShutdown(std::int32_t reason) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 6cf0154ae6..4479df83b0 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -125,3 +125,5 @@ - [ShellCoreUtil telemetry and user queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): telemetry-token output is 4112 bytes, including an eight-byte service result at offset 4104; its meaning remains unresolved. UMM status is an eight-byte output with unresolved fields. UI status is a 64-bit event mask; VM statistics and workaround-file strings use machine-sized capacities. HideBlocksFromUser transports a 64-bit value and a 32-bit value whose exact semantics remain unresolved. Eye-distance adjustment writes one byte. - [ShellCoreUtil launch and status arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): LaunchByUri reads an eight-byte parameter containing two 32-bit fields and swaps their order in its message; field meanings remain unresolved. Navigation consumes one 32-bit value, not the phantom vector/register arguments shown by decompilation. Workaround checks and mount requests take strings. Logout-state queries transmit two 32-bit values and return a byte output; selector meanings remain unresolved. + +- [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. Error-log submission consumes four strings and two 32-bit values in the restored order; their semantic names remain unresolved. PSM events carry a 32-bit event ID and a string. IME notification consumes a 32-bit value and one byte; service meanings remain unresolved. From a1e95b92ed01666960dea76d938de9a3eb331893 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:37:06 +0300 Subject: [PATCH 284/523] fix(systemservice): restore shell state setter arguments --- .../libSceSystemService/PendingExports.cpp | 46 +++++++++---------- docs/dev/debt/libSceSystemService.md | 2 + 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 8a26ee3e4d..06d15bf183 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -1006,7 +1006,7 @@ int APS5_VABI sceShellCoreUtilGetUmmStatus(std::uint32_t id, ShellCoreUmmStatus* return 0; } -int APS5_VABI sceShellCoreUtilGetUserFocus(std::int32_t userId, std::int32_t* appId) { +int APS5_VABI sceShellCoreUtilGetUserFocus(std::int32_t appId, std::int32_t* userId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1326,112 +1326,112 @@ int APS5_VABI sceShellCoreUtilResetBgdcConfig() { return 0; } -int APS5_VABI sceShellCoreUtilSetAppData() { +int APS5_VABI sceShellCoreUtilSetAppData(const char* key, const char* value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetBgmProhibition() { +int APS5_VABI sceShellCoreUtilSetBgmProhibition(std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetDeviceIndexBehavior() { +int APS5_VABI sceShellCoreUtilSetDeviceIndexBehavior(std::uint8_t first, std::uint32_t second, std::uint16_t third) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetGameLiveStreamingOnAirFlag() { +int APS5_VABI sceShellCoreUtilSetGameLiveStreamingOnAirFlag(std::int32_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetGameLiveStreamingStatus() { +int APS5_VABI sceShellCoreUtilSetGameLiveStreamingStatus(std::int32_t active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetGnmCompositorOnScreenProfilerFlag() { +int APS5_VABI sceShellCoreUtilSetGnmCompositorOnScreenProfilerFlag(std::int32_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetGpuLoadEmulationMode() { +int APS5_VABI sceShellCoreUtilSetGpuLoadEmulationMode(std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetGpuLoadEmulationModeByAppId() { +int APS5_VABI sceShellCoreUtilSetGpuLoadEmulationModeByAppId(std::int32_t appId, std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetIDUMode() { +int APS5_VABI sceShellCoreUtilSetIDUMode(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetImposeStatusFlag() { +int APS5_VABI sceShellCoreUtilSetImposeStatusFlag(std::int32_t enabled) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetPsStoreIconLayout() { +int APS5_VABI sceShellCoreUtilSetPsStoreIconLayout(std::uint32_t layout) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetPsStoreIconState() { +int APS5_VABI sceShellCoreUtilSetPsStoreIconState(std::uint32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetRemotePlayStatus() { +int APS5_VABI sceShellCoreUtilSetRemotePlayStatus(std::int32_t active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSharePlayStatus() { +int APS5_VABI sceShellCoreUtilSetSharePlayStatus(std::int32_t active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSkipUpdateCheck() { +int APS5_VABI sceShellCoreUtilSetSkipUpdateCheck(std::uint8_t skip) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSocialScreenStatus() { +int APS5_VABI sceShellCoreUtilSetSocialScreenStatus(std::int32_t active) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSplashScreenState() { +int APS5_VABI sceShellCoreUtilSetSplashScreenState(std::uint32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSystemBGState() { +int APS5_VABI sceShellCoreUtilSetSystemBGState(std::uint32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSystemBGWaveColor() { +int APS5_VABI sceShellCoreUtilSetSystemBGWaveColor(std::uint32_t color) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetSystemBGWaveState() { +int APS5_VABI sceShellCoreUtilSetSystemBGWaveState(std::uint32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetUIStatus() { +int APS5_VABI sceShellCoreUtilSetUIStatus(std::uint64_t status, std::uint64_t mask) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilSetUserFocus() { +int APS5_VABI sceShellCoreUtilSetUserFocus(std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 4479df83b0..09b5471c3e 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -127,3 +127,5 @@ - [ShellCoreUtil launch and status arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): LaunchByUri reads an eight-byte parameter containing two 32-bit fields and swaps their order in its message; field meanings remain unresolved. Navigation consumes one 32-bit value, not the phantom vector/register arguments shown by decompilation. Workaround checks and mount requests take strings. Logout-state queries transmit two 32-bit values and return a byte output; selector meanings remain unresolved. - [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. Error-log submission consumes four strings and two 32-bit values in the restored order; their semantic names remain unresolved. PSM events carry a 32-bit event ID and a string. IME notification consumes a 32-bit value and one byte; service meanings remain unresolved. + +- [ShellCoreUtil state setters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): UI updates take a 64-bit status and mask; app data takes two strings. Display-state setters mask their low bits, so source enum names and declared widths cannot be inferred from those masks alone. Streaming/impose/profiler setters test all 32 bits. User-focus reads take an application ID and return its user ID; writes apply a user ID to the current application. Device-index field meanings remain unresolved. From 797fb303c5cad840bc148ed7a9c8df366175b8a4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:37:41 +0300 Subject: [PATCH 285/523] fix(systemservice): restore error dialog layout and event arguments --- .../libSceSystemService/PendingExports.cpp | 33 ++++++++++++------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 06d15bf183..fa19512d5e 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -7,6 +7,17 @@ constexpr int SystemServiceRejected = static_cast(0x80A10005); } +struct ShellCoreErrorDialogParam { + const char* message; + const char* const* formatArgs; + std::uint8_t flag; +}; + +static_assert(sizeof(ShellCoreErrorDialogParam) == 24); +static_assert(offsetof(ShellCoreErrorDialogParam, message) == 0); +static_assert(offsetof(ShellCoreErrorDialogParam, formatArgs) == 8); +static_assert(offsetof(ShellCoreErrorDialogParam, flag) == 16); + struct ShellCoreLaunchByUriParam; struct ShellCoreTelemetryToken; struct ShellCoreUmmStatus; @@ -1436,22 +1447,22 @@ int APS5_VABI sceShellCoreUtilSetUserFocus(std::int32_t userId) { return 0; } -int APS5_VABI sceShellCoreUtilShowCriticalErrorDialog() { +int APS5_VABI sceShellCoreUtilShowCriticalErrorDialog(std::uint32_t error, const char* message) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilShowErrorDialog() { +int APS5_VABI sceShellCoreUtilShowErrorDialog(std::uint32_t error, const char* message) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilShowErrorDialogWithFormatArgs() { +int APS5_VABI sceShellCoreUtilShowErrorDialogWithFormatArgs(std::uint32_t error, const char* message, const char* const* formatArgs) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilShowErrorDialogWithParam() { +int APS5_VABI sceShellCoreUtilShowErrorDialogWithParam(std::uint32_t error, const ShellCoreErrorDialogParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1466,22 +1477,22 @@ int APS5_VABI sceShellCoreUtilSignalUserInput() { return 0; } -int APS5_VABI sceShellCoreUtilStartPsNowGame() { +int APS5_VABI sceShellCoreUtilStartPsNowGame(const char* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilStopPsNowGame() { +int APS5_VABI sceShellCoreUtilStopPsNowGame(const char* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilTalkKeyEventReceived() { +int APS5_VABI sceShellCoreUtilTalkKeyEventReceived(std::uint64_t first, std::uint32_t second, std::uint32_t third) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilTestBusTransferSpeed() { +int APS5_VABI sceShellCoreUtilTestBusTransferSpeed(const char* path, std::uint8_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1491,7 +1502,7 @@ int APS5_VABI sceShellCoreUtilTickHeartBeat() { return 0; } -int APS5_VABI sceShellCoreUtilTriggerPapcUpdate() { +int APS5_VABI sceShellCoreUtilTriggerPapcUpdate(std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1501,12 +1512,12 @@ int APS5_VABI sceShellCoreUtilTurnOffScreenSaver() { return 0; } -int APS5_VABI sceShellCoreUtilUnmountAppRight() { +int APS5_VABI sceShellCoreUtilUnmountAppRight(const char* key) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilUnmountDownloadDataForShellUI() { +int APS5_VABI sceShellCoreUtilUnmountDownloadDataForShellUI(const char* key) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 09b5471c3e..d757cac56d 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -129,3 +129,5 @@ - [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. Error-log submission consumes four strings and two 32-bit values in the restored order; their semantic names remain unresolved. PSM events carry a 32-bit event ID and a string. IME notification consumes a 32-bit value and one byte; service meanings remain unresolved. - [ShellCoreUtil state setters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): UI updates take a 64-bit status and mask; app data takes two strings. Display-state setters mask their low bits, so source enum names and declared widths cannot be inferred from those masks alone. Streaming/impose/profiler setters test all 32 bits. User-focus reads take an application ID and return its user ID; writes apply a user ID to the current application. Device-index field meanings remain unresolved. + +- [ShellCoreUtil dialog parameters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 24-byte error-dialog parameter contains a message pointer at offset zero, a null-terminated vector of string arguments at offset eight and a byte flag at offset 16. Field offsets and size are asserted; the flag meaning remains unresolved. Start/stop streaming-game and unmount requests take strings. Talk-key events transport a 64-bit value and two 32-bit values whose meanings remain unresolved. Bus-transfer testing writes one byte after a string input. From 2386ab8e84b9a00835a72b43ddfcdf99737de64e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:38:54 +0300 Subject: [PATCH 286/523] fix(systemservice): restore activation handles and process signatures --- .../libSceSystemService/PendingExports.cpp | 63 ++++++++++--------- docs/dev/debt/libSceSystemService.md | 2 + 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index fa19512d5e..1ea502ba66 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -18,6 +18,11 @@ static_assert(offsetof(ShellCoreErrorDialogParam, message) == 0); static_assert(offsetof(ShellCoreErrorDialogParam, formatArgs) == 8); static_assert(offsetof(ShellCoreErrorDialogParam, flag) == 16); +struct SystemServiceLocalProcessParam; +struct SystemServiceJvmProcessParam; +struct SystemServicePs2ProcessParam; +struct SystemServiceAppInstallStatus; +struct SystemServiceAppStatus; struct ShellCoreLaunchByUriParam; struct ShellCoreTelemetryToken; struct ShellCoreUmmStatus; @@ -521,17 +526,17 @@ int APS5_VABI sceShellCoreUtilActivateAbort() { return 0; } -int APS5_VABI sceShellCoreUtilActivateGetStatus() { +int APS5_VABI sceShellCoreUtilActivateGetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilActivateInit() { +int APS5_VABI sceShellCoreUtilActivateInit(std::uint32_t type, std::uintptr_t* handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilActivateIsActivated() { +int APS5_VABI sceShellCoreUtilActivateIsActivated(std::uint32_t type, std::uint8_t* activated) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1561,22 +1566,22 @@ int APS5_VABI sceSystemServiceActivateHevc() { return 0; } -int APS5_VABI sceSystemServiceActivateHevcAbort() { +int APS5_VABI sceSystemServiceActivateHevcAbort(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcGetStatus() { +int APS5_VABI sceSystemServiceActivateHevcGetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcInit() { +int APS5_VABI sceSystemServiceActivateHevcInit(std::uintptr_t* handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcIsActivated() { +int APS5_VABI sceSystemServiceActivateHevcIsActivated(std::uint8_t* activated) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1586,92 +1591,92 @@ int APS5_VABI sceSystemServiceActivateHevcSoft() { return 0; } -int APS5_VABI sceSystemServiceActivateHevcSoftAbort() { +int APS5_VABI sceSystemServiceActivateHevcSoftAbort(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcSoftGetStatus() { +int APS5_VABI sceSystemServiceActivateHevcSoftGetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcSoftInit() { +int APS5_VABI sceSystemServiceActivateHevcSoftInit(std::uintptr_t* handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcSoftIsActivated() { +int APS5_VABI sceSystemServiceActivateHevcSoftIsActivated(std::uint8_t* activated) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcSoftStart() { +int APS5_VABI sceSystemServiceActivateHevcSoftStart(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcSoftTerm() { +int APS5_VABI sceSystemServiceActivateHevcSoftTerm(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcStart() { +int APS5_VABI sceSystemServiceActivateHevcStart(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateHevcTerm() { +int APS5_VABI sceSystemServiceActivateHevcTerm(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateMpeg2Abort() { +int APS5_VABI sceSystemServiceActivateMpeg2Abort(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateMpeg2GetStatus() { +int APS5_VABI sceSystemServiceActivateMpeg2GetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateMpeg2Init() { +int APS5_VABI sceSystemServiceActivateMpeg2Init(std::uintptr_t* handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateMpeg2IsActivated() { +int APS5_VABI sceSystemServiceActivateMpeg2IsActivated(std::uint8_t* activated) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateMpeg2Start() { +int APS5_VABI sceSystemServiceActivateMpeg2Start(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceActivateMpeg2Term() { +int APS5_VABI sceSystemServiceActivateMpeg2Term(std::uintptr_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceAddLocalProcess() { +int APS5_VABI sceSystemServiceAddLocalProcess(std::int32_t appId, const char* path, const char* const* argv, const SystemServiceLocalProcessParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceAddLocalProcessForJvm() { +int APS5_VABI sceSystemServiceAddLocalProcessForJvm(const char* path, const char* const* argv, const SystemServiceJvmProcessParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceAddLocalProcessForPs2Emu() { +int APS5_VABI sceSystemServiceAddLocalProcessForPs2Emu(const char* path, const char* const* argv, const SystemServicePs2ProcessParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceAppGetAppInstallStatus() { +int APS5_VABI sceSystemServiceAppGetAppInstallStatus(const char* titleId, SystemServiceAppInstallStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1778,12 +1783,12 @@ int APS5_VABI sceSystemServiceEnableSuspendNotification() { return 0; } -int APS5_VABI sceSystemServiceGetAppCategoryType() { +int APS5_VABI sceSystemServiceGetAppCategoryType(std::int32_t appId, std::uint32_t* categoryType) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceGetAppFocusedAppStatus() { +int APS5_VABI sceSystemServiceGetAppFocusedAppStatus(SystemServiceAppStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1798,12 +1803,12 @@ int APS5_VABI sceSystemServiceGetAppIdListOfBigApp() { return 0; } -int APS5_VABI sceSystemServiceGetAppLaunchedUser() { +int APS5_VABI sceSystemServiceGetAppLaunchedUser(std::int32_t appId, std::int32_t* userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceGetAppStatus() { +int APS5_VABI sceSystemServiceGetAppStatus(SystemServiceAppStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index d757cac56d..464ff980d3 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -131,3 +131,5 @@ - [ShellCoreUtil state setters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): UI updates take a 64-bit status and mask; app data takes two strings. Display-state setters mask their low bits, so source enum names and declared widths cannot be inferred from those masks alone. Streaming/impose/profiler setters test all 32 bits. User-focus reads take an application ID and return its user ID; writes apply a user ID to the current application. Device-index field meanings remain unresolved. - [ShellCoreUtil dialog parameters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 24-byte error-dialog parameter contains a message pointer at offset zero, a null-terminated vector of string arguments at offset eight and a byte flag at offset 16. Field offsets and size are asserted; the flag meaning remains unresolved. Start/stop streaming-game and unmount requests take strings. Talk-key events transport a 64-bit value and two 32-bit values whose meanings remain unresolved. Bus-transfer testing writes one byte after a string input. + +- [SystemService activation and process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): activation wrappers validate a machine-sized handle and expose 32-bit status/result outputs or a byte activation flag. Shell activation helpers also receive a type selector; constant-return activation exports still need caller evidence for unused parameters. SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. From 46e9270dc4c1a41aaf61e862b21cf42d3a918e95 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:40:28 +0300 Subject: [PATCH 287/523] fix(systemservice): restore public query payload signatures --- .../libSceSystemService/PendingExports.cpp | 31 ++++++++++++------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 1ea502ba66..22ff539f47 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -18,6 +18,13 @@ static_assert(offsetof(ShellCoreErrorDialogParam, message) == 0); static_assert(offsetof(ShellCoreErrorDialogParam, formatArgs) == 8); static_assert(offsetof(ShellCoreErrorDialogParam, flag) == 16); +struct SystemServiceDaemonEvent; +struct SystemServiceLocalProcessStatus; +struct SystemServiceEvent; +struct SystemServicePSButtonEvent; +struct SystemServicePrivacyDefinitionData; +struct SystemServicePrivacyDefinitionVersion; +struct SystemServiceTitleWorkaroundInfo; struct SystemServiceLocalProcessParam; struct SystemServiceJvmProcessParam; struct SystemServicePs2ProcessParam; @@ -1813,22 +1820,22 @@ int APS5_VABI sceSystemServiceGetAppStatus(SystemServiceAppStatus* status) { return 0; } -int APS5_VABI sceSystemServiceGetAppTitleId() { +int APS5_VABI sceSystemServiceGetAppTitleId(std::int32_t appId, char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceGetAppType() { +int APS5_VABI sceSystemServiceGetAppType(std::int32_t appId, std::int32_t* type) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceGetDbgExecutablePath() { +int APS5_VABI sceSystemServiceGetDbgExecutablePath(char* path, std::uint64_t selector) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceGetEventForDaemon() { +int APS5_VABI sceSystemServiceGetEventForDaemon(SystemServiceDaemonEvent* event) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1838,7 +1845,7 @@ int APS5_VABI sceSystemServiceGetGpuLoadEmulationMode() { return 0; } -int APS5_VABI sceSystemServiceGetLocalProcessStatusList() { +int APS5_VABI sceSystemServiceGetLocalProcessStatusList(SystemServiceLocalProcessStatus* statuses, std::uint32_t capacity, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1848,7 +1855,7 @@ int APS5_VABI sceSystemServiceGetMainAppTitleId() { return 0; } -int APS5_VABI sceSystemServiceGetPSButtonEvent() { +int APS5_VABI sceSystemServiceGetPSButtonEvent(const SystemServiceEvent* event, SystemServicePSButtonEvent* buttonEvent) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1873,12 +1880,12 @@ int APS5_VABI sceSystemServiceGetParentSocketForPsmKit() { return 0; } -int APS5_VABI sceSystemServiceGetPlatformPrivacyDefinitionData() { +int APS5_VABI sceSystemServiceGetPlatformPrivacyDefinitionData(const char* key, SystemServicePrivacyDefinitionData* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceGetPlatformPrivacyDefinitionVersion() { +int APS5_VABI sceSystemServiceGetPlatformPrivacyDefinitionVersion(const char* key, SystemServicePrivacyDefinitionVersion* version) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1888,7 +1895,7 @@ int APS5_VABI sceSystemServiceGetPlatformPrivacySetting() { return 0; } -int APS5_VABI sceSystemServiceGetRecentLotusPushedButton() { +int APS5_VABI sceSystemServiceGetRecentLotusPushedButton(std::uint32_t timeout, std::int32_t* deviceId, std::uint32_t* buttons) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1913,7 +1920,7 @@ int APS5_VABI sceSystemServiceGetSubAppTitleIdForSP() { return 0; } -int APS5_VABI sceSystemServiceGetTitleWorkaroundInfo() { +int APS5_VABI sceSystemServiceGetTitleWorkaroundInfo(SystemServiceTitleWorkaroundInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1933,12 +1940,12 @@ int APS5_VABI sceSystemServiceInitializeForShellCore() { return 0; } -int APS5_VABI sceSystemServiceInvokeAppLaunchLink() { +int APS5_VABI sceSystemServiceInvokeAppLaunchLink(const char* uri) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceIsAppSuspended() { +int APS5_VABI sceSystemServiceIsAppSuspended(std::int32_t appId, bool* suspended) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 464ff980d3..1cf7485a3b 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -133,3 +133,5 @@ - [ShellCoreUtil dialog parameters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 24-byte error-dialog parameter contains a message pointer at offset zero, a null-terminated vector of string arguments at offset eight and a byte flag at offset 16. Field offsets and size are asserted; the flag meaning remains unresolved. Start/stop streaming-game and unmount requests take strings. Talk-key events transport a 64-bit value and two 32-bit values whose meanings remain unresolved. Bus-transfer testing writes one byte after a string input. - [SystemService activation and process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): activation wrappers validate a machine-sized handle and expose 32-bit status/result outputs or a byte activation flag. Shell activation helpers also receive a type selector; constant-return activation exports still need caller evidence for unused parameters. SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. + +- [SystemService query payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): daemon events write 12 bytes; local-process entries occupy 36 bytes, consisting of a 32-bit value and 32-byte name. PS-button extraction reads a 16-byte event and rearranges its fields into a distinct 16-byte output; semantic names remain unresolved. Privacy definition/version queries take string keys and distinct output types; their layouts remain unresolved. Workaround information writes four bytes at offset zero and 16 bytes at offset eight. From c9c8675b1d4c0f9d6c464e4ac60d83bc3c383fdf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:41:40 +0300 Subject: [PATCH 288/523] fix(systemservice): restore assembly alias signatures --- .../libSceSystemService/PendingExports.cpp | 22 +++++++++---------- docs/dev/debt/libSceSystemService.md | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 22ff539f47..28bdc18d54 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -1805,7 +1805,7 @@ int APS5_VABI sceSystemServiceGetAppId() { return 0; } -int APS5_VABI sceSystemServiceGetAppIdListOfBigApp() { +int APS5_VABI sceSystemServiceGetAppIdListOfBigApp(std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1900,7 +1900,7 @@ int APS5_VABI sceSystemServiceGetRecentLotusPushedButton(std::uint32_t timeout, return 0; } -int APS5_VABI sceSystemServiceGetRemoteplayStatus() { +int APS5_VABI sceSystemServiceGetRemoteplayStatus(bool* active) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1925,7 +1925,7 @@ int APS5_VABI sceSystemServiceGetTitleWorkaroundInfo(SystemServiceTitleWorkaroun return 0; } -int APS5_VABI sceSystemServiceGetUIStatus() { +int APS5_VABI sceSystemServiceGetUIStatus(std::uint64_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1950,7 +1950,7 @@ int APS5_VABI sceSystemServiceIsAppSuspended(std::int32_t appId, bool* suspended return 0; } -int APS5_VABI sceSystemServiceIsBgmCpuBudgetAvailable() { +bool APS5_VABI sceSystemServiceIsBgmCpuBudgetAvailable() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1965,17 +1965,17 @@ int APS5_VABI sceSystemServiceIsEyeToEyeDistanceAdjusted() { return 0; } -int APS5_VABI sceSystemServiceIsGameLiveStreamingOnAir() { +bool APS5_VABI sceSystemServiceIsGameLiveStreamingOnAir() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceIsScreenSaverOn() { +bool APS5_VABI sceSystemServiceIsScreenSaverOn() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceIsShellUiFgAndGameBgCpuMode() { +bool APS5_VABI sceSystemServiceIsShellUiFgAndGameBgCpuMode() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2028,7 +2028,7 @@ int APS5_VABI sceSystemServiceLoadExecVideoServiceWebApp() { return SystemServiceUnavailable; } -int APS5_VABI sceSystemServiceNavigateToAnotherApp() { +int APS5_VABI sceSystemServiceNavigateToAnotherApp(std::uint32_t target) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2038,12 +2038,12 @@ int APS5_VABI sceSystemServiceNavigateToGoBack() { return 0; } -int APS5_VABI sceSystemServiceNavigateToGoBackWithValue() { +int APS5_VABI sceSystemServiceNavigateToGoBackWithValue(const char* value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceNavigateToGoHome() { +int APS5_VABI sceSystemServiceNavigateToGoHome(std::uint32_t target) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2109,7 +2109,7 @@ int APS5_VABI sceSystemServiceSaveVideoToken() { return 0; } -int APS5_VABI sceSystemServiceSetControllerFocusPermission() { +int APS5_VABI sceSystemServiceSetControllerFocusPermission(std::uint8_t permission) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 1cf7485a3b..dbd19c0a01 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -19,7 +19,7 @@ - [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the time zone (parameter 4) and summer time (parameter 5) follow the host's local time zone at the moment of the call. Titles compute the local time as UTC plus the time zone in minutes plus 60 minutes when summer time is 1 ([Apollo](https://github.com/bucanero/apollo-ps4/blob/252a9d5a187c463994263009851a179f4f910c49/source/exec_cmd.c#L70-L92), [GoldHEN's frame logger](https://github.com/GoldHEN/GoldHEN_Plugins_Repository/blob/6c3a06f53d9caebadfb0aeed9bb94119012010a7/plugin_src/frame_logger/source/main.c#L60-L82), and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/413d2e3518fb435f40971d238fe0ecae7ee551da/src/SharpProspero/Platform/SystemParameters.cs#L75-L79) calls them the offset from UTC in minutes and whether summer time is in effect), so the time zone is the host's current offset east of UTC less the summer time shift while summer time is in effect: 60 minutes on Linux, and on Windows the C runtime's own summer time bias (`_get_dstbias`), which is also what shifted its local time. For a host zone whose summer time is not one hour, Linux keeps the sum right and the standard offset wrong, and Windows the opposite; how the console reports such a zone is unknown. libkernel's `sceKernelConvertUtcToLocaltime`, `sceKernelConvertLocaltimeToUtc` and `sceKernelGettimezone` still use UTC - [sceSystemServiceInitializePlayerDialogParam](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - a null param throws: what the console does with it is not measured. The console run in [prosper](https://github.com/mattias800/prosper/blob/40d95db32efe1ad00106a5b9a84e018fffa9ecff/prosper/tests/data/console_oracle/nethttp.golden.tsv#L46) shows only the 0x40 bytes written (size 0x40, user ID -1 at 0xC, zeros), so the field names and the mode and target account fields follow [SharpProspero's struct](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L637-L655), and the `void` return [its declaration](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L419-L426); the console left 0x800693020 in `rax`, not an SCE error code. Closes when: the null case is measured on a PS5 -- [libSceSystemService pending exports](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): unreviewed argument lists and service behavior remain unresolved. The supplied module defines 381 of the 477 pending names; the other 96 require another module/version or caller evidence. Confirmed argument lists are being restored independently of their protected throwing bodies; remaining empty declarations must not be treated as proof of a zero-argument ABI. +- [libSceSystemService pending exports](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): unreviewed argument lists and service behavior remain unresolved. The C decompilation contains 381 of the 477 pending names; additional exports exist as direct assembly jumps, so absence from that listing does not imply absence from the module. Confirmed argument lists are being restored independently of their protected throwing bodies; remaining empty declarations must not be treated as proof of a zero-argument ABI. - [sceSystemServiceLaunchSystemDeeplink](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature - [sceShellCoreUtilLaunchByUriParamInit](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature - [sceSystemStateMgrTickVideoPlayback](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - unknown signature @@ -135,3 +135,5 @@ - [SystemService activation and process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): activation wrappers validate a machine-sized handle and expose 32-bit status/result outputs or a byte activation flag. Shell activation helpers also receive a type selector; constant-return activation exports still need caller evidence for unused parameters. SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. - [SystemService query payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): daemon events write 12 bytes; local-process entries occupy 36 bytes, consisting of a 32-bit value and 32-byte name. PS-button extraction reads a 16-byte event and rearranges its fields into a distinct 16-byte output; semantic names remain unresolved. Privacy definition/version queries take string keys and distinct output types; their layouts remain unresolved. Workaround information writes four bytes at offset zero and 16 bytes at offset eight. + +- [SystemService direct aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly-only jumps now share the confirmed signatures of their LncUtil/ShellCoreUtil targets for application-ID lists, remote-play/UI status, navigation, controller-focus permissions and boolean budget/status queries. Aliases of unresolved targets still require target or caller analysis. From d4f2911b74287939104ce1b22034d07858558ee8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:42:23 +0300 Subject: [PATCH 289/523] fix(systemservice): restore collapsed launcher dispatch arguments --- .../libSceSystemService/PendingExports.cpp | 21 ++++++++++--------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 28bdc18d54..1343f07d4e 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -41,6 +41,7 @@ struct ShellCorePsnAccountInfoThird; struct ShellCorePsnAccountInfoFourth; struct ShellCorePsnAccountInfoFifth; struct ShellCoreAppLaunchTypeInfo; +struct LncGpuCrashAppStatus; struct LncLaunchParam; struct LncDaemonEvent; struct LncShellUiEvent; @@ -129,7 +130,7 @@ int APS5_VABI sceLncUtilBlockingGetEventForDaemon() { return 0; } -int APS5_VABI sceLncUtilContinueApp() { +int APS5_VABI sceLncUtilContinueApp(std::uint32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -184,12 +185,12 @@ int APS5_VABI sceLncUtilGetAppCategoryType(std::int32_t appId, std::uint32_t* ca return 0; } -int APS5_VABI sceLncUtilGetAppFocusedAppStatus() { +int APS5_VABI sceLncUtilGetAppFocusedAppStatus(LncAppStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceLncUtilGetAppId() { +int APS5_VABI sceLncUtilGetAppId(const char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -209,7 +210,7 @@ int APS5_VABI sceLncUtilGetAppLaunchedUser(std::int32_t appId, std::int32_t* use return 0; } -int APS5_VABI sceLncUtilGetAppStatus() { +int APS5_VABI sceLncUtilGetAppStatus(LncAppStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -264,7 +265,7 @@ int APS5_VABI sceLncUtilGetEventForShellUI(LncShellUiEvent* event) { return 0; } -int APS5_VABI sceLncUtilGetGpuCrashFullDumpAppStatus() { +int APS5_VABI sceLncUtilGetGpuCrashFullDumpAppStatus(LncGpuCrashAppStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -314,7 +315,7 @@ int APS5_VABI sceLncUtilIsAppSuspended(std::int32_t appId, bool* suspended) { return 0; } -int APS5_VABI sceLncUtilIsCdlgActivated() { +int APS5_VABI sceLncUtilIsCdlgActivated(std::uint8_t* activated) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -374,7 +375,7 @@ int APS5_VABI sceLncUtilNotifyCoredumpRequestProgress(std::int32_t appId, std::i return 0; } -int APS5_VABI sceLncUtilNotifyVshReady() { +int APS5_VABI sceLncUtilNotifyVshReady(std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -389,7 +390,7 @@ int APS5_VABI sceLncUtilRaiseExceptionLocalProcess(std::int32_t appId, std::int3 return 0; } -int APS5_VABI sceLncUtilRegisterCdlgSharedMemoryName() { +int APS5_VABI sceLncUtilRegisterCdlgSharedMemoryName(const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -489,7 +490,7 @@ int APS5_VABI sceLncUtilUnblockAppSuspend(const char* reason) { return 0; } -int APS5_VABI sceLncUtilUnregisterCdlgSharedMemoryName() { +int APS5_VABI sceLncUtilUnregisterCdlgSharedMemoryName(const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1800,7 +1801,7 @@ int APS5_VABI sceSystemServiceGetAppFocusedAppStatus(SystemServiceAppStatus* sta return 0; } -int APS5_VABI sceSystemServiceGetAppId() { +int APS5_VABI sceSystemServiceGetAppId(const char* titleId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index dbd19c0a01..e9afdfd880 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -137,3 +137,5 @@ - [SystemService query payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): daemon events write 12 bytes; local-process entries occupy 36 bytes, consisting of a 32-bit value and 32-byte name. PS-button extraction reads a 16-byte event and rearranges its fields into a distinct 16-byte output; semantic names remain unresolved. Privacy definition/version queries take string keys and distinct output types; their layouts remain unresolved. Workaround information writes four bytes at offset zero and 16 bytes at offset eight. - [SystemService direct aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly-only jumps now share the confirmed signatures of their LncUtil/ShellCoreUtil targets for application-ID lists, remote-play/UI status, navigation, controller-focus permissions and boolean budget/status queries. Aliases of unresolved targets still require target or caller analysis. + +- [LncUtil collapsed dispatch stubs](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly table targets confirm application-ID lookup takes a title-ID string, current/focused status outputs occupy 64 bytes, GPU-crash status occupies 1320 bytes, and dialog activation writes one byte. Shared-memory registration takes a string. GPU-crash fields and the VSH-ready selector meaning remain unresolved; collapsed assembly procedures are not missing exports. From 32b7c50240ed15349d3ddbbba2fc983d146383cc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:43:39 +0300 Subject: [PATCH 290/523] fix(systemservice): restore process control and telemetry arguments --- .../libSceSystemService/PendingExports.cpp | 40 +++++++++++-------- docs/dev/debt/libSceSystemService.md | 2 + 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 1343f07d4e..e46b041d22 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -25,6 +25,12 @@ struct SystemServicePSButtonEvent; struct SystemServicePrivacyDefinitionData; struct SystemServicePrivacyDefinitionVersion; struct SystemServiceTitleWorkaroundInfo; +struct SystemServiceStoreParam { + std::uint32_t size; +}; + +static_assert(sizeof(SystemServiceStoreParam) == 4); + struct SystemServiceLocalProcessParam; struct SystemServiceJvmProcessParam; struct SystemServicePs2ProcessParam; @@ -1961,7 +1967,7 @@ int APS5_VABI sceSystemServiceIsBgmPlaying() { return 0; } -int APS5_VABI sceSystemServiceIsEyeToEyeDistanceAdjusted() { +int APS5_VABI sceSystemServiceIsEyeToEyeDistanceAdjusted(std::uint32_t userId, std::uint8_t* adjusted) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1981,17 +1987,17 @@ bool APS5_VABI sceSystemServiceIsShellUiFgAndGameBgCpuMode() { return 0; } -int APS5_VABI sceSystemServiceKillLocalProcess() { +int APS5_VABI sceSystemServiceKillLocalProcess(std::int32_t appId, std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceKillLocalProcessForJvm() { +int APS5_VABI sceSystemServiceKillLocalProcessForJvm(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceKillLocalProcessForPs2Emu() { +int APS5_VABI sceSystemServiceKillLocalProcessForPs2Emu(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2001,7 +2007,7 @@ int APS5_VABI sceSystemServiceLaunchEventDetails() { return 0; } -int APS5_VABI sceSystemServiceLaunchStore() { +int APS5_VABI sceSystemServiceLaunchStore(const char* uri, const SystemServiceStoreParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2020,7 +2026,7 @@ int APS5_VABI sceSystemServiceLaunchUdsApp() { return SystemServiceRejected; } -int APS5_VABI sceSystemServiceLaunchWebApp() { +int APS5_VABI sceSystemServiceLaunchWebApp(const char* argument) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2054,7 +2060,7 @@ int APS5_VABI sceSystemServiceNotifyBgmCoreTermination() { return 0; } -int APS5_VABI sceSystemServiceRaiseExceptionLocalProcess() { +int APS5_VABI sceSystemServiceRaiseExceptionLocalProcess(std::int32_t appId, std::int32_t processId, std::uint64_t exception) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2100,12 +2106,12 @@ int APS5_VABI sceSystemServiceRequestToChangeRenderingMode() { return SystemServiceRejected; } -int APS5_VABI sceSystemServiceResumeLocalProcess() { +int APS5_VABI sceSystemServiceResumeLocalProcess(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceSaveVideoToken() { +int APS5_VABI sceSystemServiceSaveVideoToken(std::int32_t userId, const char* token) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2115,12 +2121,12 @@ int APS5_VABI sceSystemServiceSetControllerFocusPermission(std::uint8_t permissi return 0; } -int APS5_VABI sceSystemServiceSetGpuLoadEmulationMode() { +int APS5_VABI sceSystemServiceSetGpuLoadEmulationMode(std::uint32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceSetOutOfVrPlayAreaFlag() { +int APS5_VABI sceSystemServiceSetOutOfVrPlayAreaFlag(std::uint8_t flag) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2129,7 +2135,7 @@ int APS5_VABI sceSystemServiceSetOutOfVrPlayZoneWarning() { return 0; } -int APS5_VABI sceSystemServiceSetPowerSaveLevel() { +int APS5_VABI sceSystemServiceSetPowerSaveLevel(std::uint32_t level) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2144,7 +2150,7 @@ int APS5_VABI sceSystemServiceShowClosedCaptionSettings() { return 0; } -int APS5_VABI sceSystemServiceShowErrorDialog() { +int APS5_VABI sceSystemServiceShowErrorDialog(std::uint32_t error, const char* message) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2159,22 +2165,22 @@ int APS5_VABI sceSystemServiceShowImposeMenuForPs2Emu() { return 0; } -int APS5_VABI sceSystemServiceSuspendBackgroundApp() { +int APS5_VABI sceSystemServiceSuspendBackgroundApp(std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceSuspendLocalProcess() { +int APS5_VABI sceSystemServiceSuspendLocalProcess(std::int32_t processId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceTelemetrySetData() { +int APS5_VABI sceSystemServiceTelemetrySetData(const char* key, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceTelemetrySetData2() { +int APS5_VABI sceSystemServiceTelemetrySetData2(const char* key, const char* data) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index e9afdfd880..5d427df1b0 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -139,3 +139,5 @@ - [SystemService direct aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly-only jumps now share the confirmed signatures of their LncUtil/ShellCoreUtil targets for application-ID lists, remote-play/UI status, navigation, controller-focus permissions and boolean budget/status queries. Aliases of unresolved targets still require target or caller analysis. - [LncUtil collapsed dispatch stubs](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly table targets confirm application-ID lookup takes a title-ID string, current/focused status outputs occupy 64 bytes, GPU-crash status occupies 1320 bytes, and dialog activation writes one byte. Shared-memory registration takes a string. GPU-crash fields and the VSH-ready selector meaning remain unresolved; collapsed assembly procedures are not missing exports. + +- [SystemService launch and telemetry arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): store-launch parameters contain only a four-byte size; web-app launch takes one string argument. Process lifecycle wrappers forward the verified LncUtil argument lists. TelemetrySetData takes a string key, binary payload and machine-sized length; TelemetrySetData2 takes two strings. Token storage consumes a 32-bit identifier and string; only its signature is restored, with no token values included. From 4e9520a9dbc2e7ec26658049d9c8aff5ffd6d577 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:44:57 +0300 Subject: [PATCH 291/523] fix(systemservice): restore state manager and modal arguments --- .../libSceSystemService/PendingExports.cpp | 20 +++++++++---------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index e46b041d22..7cb020b76e 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -1556,12 +1556,12 @@ int APS5_VABI sceShellCoreutilGetCrashReportProcessInformation() { return 0; } -int APS5_VABI sceSystemModalDialogClose() { +int APS5_VABI sceSystemModalDialogClose(std::uint32_t dialogId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemModalDialogOpen() { +int APS5_VABI sceSystemModalDialogOpen(const char* message) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1897,7 +1897,7 @@ int APS5_VABI sceSystemServiceGetPlatformPrivacyDefinitionVersion(const char* ke return 0; } -int APS5_VABI sceSystemServiceGetPlatformPrivacySetting() { +int APS5_VABI sceSystemServiceGetPlatformPrivacySetting(std::uint32_t* setting) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2315,7 +2315,7 @@ int APS5_VABI sceSystemStateMgrGetTriggerCode() { return 0; } -int APS5_VABI sceSystemStateMgrIsBdDriveReady() { +bool APS5_VABI sceSystemStateMgrIsBdDriveReady() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2325,12 +2325,12 @@ int APS5_VABI sceSystemStateMgrIsGpuPerformanceNormal() { return 0; } -int APS5_VABI sceSystemStateMgrIsShellUIShutdownInProgress() { +bool APS5_VABI sceSystemStateMgrIsShellUIShutdownInProgress() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemStateMgrIsStandbyModeEnabled() { +bool APS5_VABI sceSystemStateMgrIsStandbyModeEnabled() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2340,7 +2340,7 @@ int APS5_VABI sceSystemStateMgrLeaveMediaPlaybackMode() { return 0; } -int APS5_VABI sceSystemStateMgrNotifySystemSuspendResumeProgress() { +int APS5_VABI sceSystemStateMgrNotifySystemSuspendResumeProgress(const char* progress) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2350,7 +2350,7 @@ int APS5_VABI sceSystemStateMgrReboot() { return 0; } -int APS5_VABI sceSystemStateMgrRequestToKeepMainOnStandby() { +int APS5_VABI sceSystemStateMgrRequestToKeepMainOnStandby(const char* reason) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2360,7 +2360,7 @@ int APS5_VABI sceSystemStateMgrSendCecOneTouchPlayCommand() { return 0; } -int APS5_VABI sceSystemStateMgrStartRebootTimer() { +int APS5_VABI sceSystemStateMgrStartRebootTimer(std::int32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2405,7 +2405,7 @@ int APS5_VABI sceSystemStateMgrWaitVshAutoUpdateVerifyDone() { return 0; } -int APS5_VABI sceSystemStateMgrWakeUp() { +int APS5_VABI sceSystemStateMgrWakeUp(std::uint16_t trigger) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 5d427df1b0..88483669b9 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -141,3 +141,5 @@ - [LncUtil collapsed dispatch stubs](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly table targets confirm application-ID lookup takes a title-ID string, current/focused status outputs occupy 64 bytes, GPU-crash status occupies 1320 bytes, and dialog activation writes one byte. Shared-memory registration takes a string. GPU-crash fields and the VSH-ready selector meaning remain unresolved; collapsed assembly procedures are not missing exports. - [SystemService launch and telemetry arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): store-launch parameters contain only a four-byte size; web-app launch takes one string argument. Process lifecycle wrappers forward the verified LncUtil argument lists. TelemetrySetData takes a string key, binary payload and machine-sized length; TelemetrySetData2 takes two strings. Token storage consumes a 32-bit identifier and string; only its signature is restored, with no token values included. + +- [SystemStateMgr and modal signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): keep-main-on-standby takes a string, reboot timer consumes a 32-bit mode and wake-up consumes a 16-bit trigger. Suspend/resume progress forwards a string to kernel logging. Disc readiness, shell shutdown progress and standby enablement return booleans. Modal open takes a string and close takes a 32-bit dialog ID. Platform privacy setting writes a 32-bit value; the kernel platform query it calls consumes no caller arguments. From 9b8c8e51d5b8c34fa07678c5b35e80c29b3254dd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:46:19 +0300 Subject: [PATCH 292/523] fix(usbstorage): restore mapping callbacks and system service aliases --- .../libSceSystemService/PendingExports.cpp | 36 ++++++++++--------- core/libs/prx/libSceUsbStorage/Export.cpp | 24 +++++++------ docs/dev/debt/libSceSystemService.md | 2 ++ 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 7cb020b76e..536ab51996 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -59,6 +59,10 @@ struct LncAppStatus; struct LncShellUiAppStatus; struct AppMessagingMessage; +struct UsbStorageEvent; +struct UsbStorageDeviceInfo; +using UsbStorageCallback = void (APS5_VABI *)(const UsbStorageEvent* event, void* userData); + extern "C" { int APS5_VABI sceAppMessagingClearEventFlag(std::int32_t appId, std::uint64_t flags) { @@ -2195,67 +2199,67 @@ int APS5_VABI sceSystemServiceTurnOffScreenSaver() { return 0; } -int APS5_VABI sceSystemServiceUsbStorageGetDeviceInfo() { +int APS5_VABI sceSystemServiceUsbStorageGetDeviceInfo(std::uint32_t device, UsbStorageDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageGetDeviceList() { +int APS5_VABI sceSystemServiceUsbStorageGetDeviceList(std::uint32_t* devices, std::int32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageGetdentsClose() { +int APS5_VABI sceSystemServiceUsbStorageGetdentsClose(std::int32_t fd) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageGetdentsOpen() { +int APS5_VABI sceSystemServiceUsbStorageGetdentsOpen(std::uint32_t device, std::int32_t* fd, std::uint64_t* bufferSize) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageGetdentsRead() { +int APS5_VABI sceSystemServiceUsbStorageGetdentsRead(std::int32_t fd, void* buffer, std::uint64_t bufferSize, std::uint64_t* bytesRead) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageInit() { +int APS5_VABI sceSystemServiceUsbStorageInit(void* attr) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageIsExist() { +bool APS5_VABI sceSystemServiceUsbStorageIsExist(std::uint32_t device, const char* directory, std::uint8_t* exists) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageRegisterCallback() { +int APS5_VABI sceSystemServiceUsbStorageRegisterCallback(std::int32_t eventType, UsbStorageCallback callback, void* arg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageRegisterCallbackForMapAvailable() { +int APS5_VABI sceSystemServiceUsbStorageRegisterCallbackForMapAvailable(std::uint32_t device, const char* directory, UsbStorageCallback callback, void* arg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageRegisterCallbackForMapUnavailable() { +int APS5_VABI sceSystemServiceUsbStorageRegisterCallbackForMapUnavailable(std::uint32_t device, const char* directory, UsbStorageCallback callback, void* arg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageRequestMap() { +int APS5_VABI sceSystemServiceUsbStorageRequestMap(std::uint32_t device, const char* directory, std::int32_t flags, std::uint64_t volumeSize, char* mountPoint, std::uint64_t* mountId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageRequestMapWSB() { +int APS5_VABI sceSystemServiceUsbStorageRequestMapWSB(std::uint32_t device, const char* directory, std::int32_t flags, std::uint64_t volumeSize, char* mountPoint, std::uint64_t* mountId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageRequestUnmap() { +int APS5_VABI sceSystemServiceUsbStorageRequestUnmap(std::uint32_t device, const char* directory) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2265,17 +2269,17 @@ int APS5_VABI sceSystemServiceUsbStorageTerm() { return 0; } -int APS5_VABI sceSystemServiceUsbStorageUnregisterCallback() { +int APS5_VABI sceSystemServiceUsbStorageUnregisterCallback(std::int32_t eventType, UsbStorageCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageUnregisterCallbackForMapAvailable() { +int APS5_VABI sceSystemServiceUsbStorageUnregisterCallbackForMapAvailable(std::uint32_t device, const char* directory, UsbStorageCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceSystemServiceUsbStorageUnregisterCallbackForMapUnavailable() { +int APS5_VABI sceSystemServiceUsbStorageUnregisterCallbackForMapUnavailable(std::uint32_t device, const char* directory, UsbStorageCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/prx/libSceUsbStorage/Export.cpp b/core/libs/prx/libSceUsbStorage/Export.cpp index 0704602439..96c447c943 100644 --- a/core/libs/prx/libSceUsbStorage/Export.cpp +++ b/core/libs/prx/libSceUsbStorage/Export.cpp @@ -3,6 +3,10 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct UsbStorageEvent; +struct UsbStorageDeviceInfo; +using UsbStorageCallback = void (APS5_VABI *)(const UsbStorageEvent* event, void* userData); + extern "C" { int APS5_VABI sceUsbStorageInit(void* attr) { @@ -13,31 +17,31 @@ int APS5_VABI sceUsbStorageTerm(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageRegisterCallback(int32_t eventType, void* callback, void* arg) { +int APS5_VABI sceUsbStorageRegisterCallback(int32_t eventType, UsbStorageCallback callback, void* arg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageUnregisterCallback(int32_t eventType, void* callback) { +int APS5_VABI sceUsbStorageUnregisterCallback(int32_t eventType, UsbStorageCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageRegisterCallbackForMapAvailable(uint32_t device, const char* directory, void* callback, void* arg) { +int APS5_VABI sceUsbStorageRegisterCallbackForMapAvailable(uint32_t device, const char* directory, UsbStorageCallback callback, void* arg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageUnregisterCallbackForMapAvailable(uint32_t device, const char* directory, void* callback) { +int APS5_VABI sceUsbStorageUnregisterCallbackForMapAvailable(uint32_t device, const char* directory, UsbStorageCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageRegisterCallbackForMapUnavailable(uint32_t device, const char* directory, void* callback, void* arg) { +int APS5_VABI sceUsbStorageRegisterCallbackForMapUnavailable(uint32_t device, const char* directory, UsbStorageCallback callback, void* arg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageUnregisterCallbackForMapUnavailable(uint32_t device, const char* directory, void* callback) { +int APS5_VABI sceUsbStorageUnregisterCallbackForMapUnavailable(uint32_t device, const char* directory, UsbStorageCallback callback) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageGetDeviceInfo(uint32_t device, void* info) { +int APS5_VABI sceUsbStorageGetDeviceInfo(uint32_t device, UsbStorageDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -49,13 +53,11 @@ int APS5_VABI sceUsbStorageGetMountPointOfShellCore(uint32_t device, char* mount NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageIsExist(uint32_t device) { +bool APS5_VABI sceUsbStorageIsExist(uint32_t device, const char* directory, std::uint8_t* exists) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUsbStorageRequestMap(uint32_t device, const char* directory, int32_t flags, - uint64_t volumeSize, char* mountPoint, uint64_t* mountId, - const void* reserved, size_t reservedSize) { +int APS5_VABI sceUsbStorageRequestMap(uint32_t device, const char* directory, int32_t flags, uint64_t volumeSize, char* mountPoint, uint64_t* mountId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 88483669b9..cf7798a4ed 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -143,3 +143,5 @@ - [SystemService launch and telemetry arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): store-launch parameters contain only a four-byte size; web-app launch takes one string argument. Process lifecycle wrappers forward the verified LncUtil argument lists. TelemetrySetData takes a string key, binary payload and machine-sized length; TelemetrySetData2 takes two strings. Token storage consumes a 32-bit identifier and string; only its signature is restored, with no token values included. - [SystemStateMgr and modal signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): keep-main-on-standby takes a string, reboot timer consumes a 32-bit mode and wake-up consumes a 16-bit trigger. Suspend/resume progress forwards a string to kernel logging. Disc readiness, shell shutdown progress and standby enablement return booleans. Modal open takes a string and close takes a 32-bit dialog ID. Platform privacy setting writes a 32-bit value; the kernel platform query it calls consumes no caller arguments. + +- [USB storage aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) and [USB exports](../../../core/libs/prx/libSceUsbStorage/Export.cpp): direct-jump signatures are matched to the supplied USB module. RequestMap/RequestMapWSB take six arguments; the two extra helper arguments are internal. IsExist takes a device, directory and byte output, with a separate boolean failure result. Callbacks receive an event pointer and user data. Device information writes 824 bytes, device lists hold eight 32-bit IDs, and mount-point outputs occupy 128 bytes. Event/device field meanings and initialization attributes remain unresolved. From 28fdabd59cfcc5b9b5c89921d23b5037f87f71d3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:48:57 +0300 Subject: [PATCH 293/523] fix(systemservice): restore float progress returns and clock arguments --- .../libSceSystemService/PendingExports.cpp | 19 ++++++++++--------- docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 536ab51996..face484405 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -31,6 +31,7 @@ struct SystemServiceStoreParam { static_assert(sizeof(SystemServiceStoreParam) == 4); +struct SystemServiceCpuPstateParam; struct SystemServiceLocalProcessParam; struct SystemServiceJvmProcessParam; struct SystemServicePs2ProcessParam; @@ -890,7 +891,7 @@ int APS5_VABI sceShellCoreUtilGetIDUMode(std::uint8_t* mode) { return 0; } -int APS5_VABI sceShellCoreUtilGetImposeMenuFlagForPs2Emu() { +bool APS5_VABI sceShellCoreUtilGetImposeMenuFlagForPs2Emu(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -930,12 +931,12 @@ int APS5_VABI sceShellCoreUtilGetPlatformPrivacyDefinitionEventData(const char* return 0; } -int APS5_VABI sceShellCoreUtilGetProgressOfFormatExternalHdd() { +float APS5_VABI sceShellCoreUtilGetProgressOfFormatExternalHdd() { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetProgressOfFsck() { +float APS5_VABI sceShellCoreUtilGetProgressOfFsck() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -995,7 +996,7 @@ int APS5_VABI sceShellCoreUtilGetSupportSiteURL(std::uint32_t first, std::uint32 return 0; } -int APS5_VABI sceShellCoreUtilGetSuspendConfirmationDialogFlag(std::int32_t appId) { +bool APS5_VABI sceShellCoreUtilGetSuspendConfirmationDialogFlag(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1075,7 +1076,7 @@ int APS5_VABI sceShellCoreUtilIncrementVersionNumberOfCameraCalibrationData() { return 0; } -int APS5_VABI sceShellCoreUtilIsAccessibilityZoomLocked() { +bool APS5_VABI sceShellCoreUtilIsAccessibilityZoomLocked(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1120,7 +1121,7 @@ bool APS5_VABI sceShellCoreUtilIsImposeScreenOverlaid() { return 0; } -int APS5_VABI sceShellCoreUtilIsInSystemSuspendBlackList() { +bool APS5_VABI sceShellCoreUtilIsInSystemSuspendBlackList(std::uint32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1703,7 +1704,7 @@ int APS5_VABI sceSystemServiceChangeAcpClock() { return 0; } -int APS5_VABI sceSystemServiceChangeClock() { +int APS5_VABI sceSystemServiceChangeClock(const char* clock, std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1712,7 +1713,7 @@ int APS5_VABI sceSystemServiceChangeCpuClock() { return 0; } -int APS5_VABI sceSystemServiceChangeCpuPstate() { +int APS5_VABI sceSystemServiceChangeCpuPstate(const SystemServiceCpuPstateParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1721,7 +1722,7 @@ int APS5_VABI sceSystemServiceChangeGpuClock() { return 0; } -int APS5_VABI sceSystemServiceChangeMemPstate() { +int APS5_VABI sceSystemServiceChangeMemPstate(std::uint32_t state) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index cf7798a4ed..5206f219ab 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -145,3 +145,5 @@ - [SystemStateMgr and modal signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): keep-main-on-standby takes a string, reboot timer consumes a 32-bit mode and wake-up consumes a 16-bit trigger. Suspend/resume progress forwards a string to kernel logging. Disc readiness, shell shutdown progress and standby enablement return booleans. Modal open takes a string and close takes a 32-bit dialog ID. Platform privacy setting writes a 32-bit value; the kernel platform query it calls consumes no caller arguments. - [USB storage aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) and [USB exports](../../../core/libs/prx/libSceUsbStorage/Export.cpp): direct-jump signatures are matched to the supplied USB module. RequestMap/RequestMapWSB take six arguments; the two extra helper arguments are internal. IsExist takes a device, directory and byte output, with a separate boolean failure result. Callbacks receive an event pointer and user data. Device information writes 824 bytes, device lists hold eight 32-bit IDs, and mount-point outputs occupy 128 bytes. Event/device field meanings and initialization attributes remain unresolved. + +- [Assembly-only shell and clock queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): format/fsck progress returns a float in XMM0. Impose-menu, accessibility-zoom and suspend-confirmation queries take an application ID and return a boolean. Suspend-blacklist lookup forwards a 32-bit ID to SysCore. Clock changes take a string and 32-bit value; CPU pstate takes a 32-byte input structure whose fields remain unresolved, and memory pstate takes one 32-bit value. From a8cb8345cd56b46d29e63c7a841431a6d901d34f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:57:21 +0300 Subject: [PATCH 294/523] fix(systemservice): validate system name buffers and termination --- core/libs/prx/libSceSystemService/Export.cpp | 1 + core/libs/tests/GuestSystemService.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/core/libs/prx/libSceSystemService/Export.cpp b/core/libs/prx/libSceSystemService/Export.cpp index 6c37c6863d..a923164119 100644 --- a/core/libs/prx/libSceSystemService/Export.cpp +++ b/core/libs/prx/libSceSystemService/Export.cpp @@ -323,6 +323,7 @@ int APS5_VABI sceSystemServiceParamGetString(int paramId, char* buf, size_t bufS constexpr char SystemName[] = "PS5"; std::memset(buf, 0, bufSize); std::memcpy(buf, SystemName, sizeof(SystemName)); + buf[SYSTEM_SERVICE_MAX_SYSTEM_NAME_LENGTH - 1] = '\0'; return SYSTEM_SERVICE_OK; } diff --git a/core/libs/tests/GuestSystemService.cpp b/core/libs/tests/GuestSystemService.cpp index 113cb6aa2d..3913b7dc1c 100644 --- a/core/libs/tests/GuestSystemService.cpp +++ b/core/libs/tests/GuestSystemService.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include extern "C" int APS5_VABI sceSystemServiceGetHdrToneMapLuminance(SystemServiceHdrToneMapLuminance* luminance); From 9e94b0cb712f1dd2109ffed769fbc1bd134e595c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:58:20 +0300 Subject: [PATCH 295/523] fix(systemservice): define launch and microphone status layouts --- .../libSceSystemService/PendingExports.cpp | 28 ++++++++++++++----- docs/dev/debt/libSceSystemService.md | 6 ++-- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index face484405..d17afe85ce 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -37,9 +37,23 @@ struct SystemServiceJvmProcessParam; struct SystemServicePs2ProcessParam; struct SystemServiceAppInstallStatus; struct SystemServiceAppStatus; -struct ShellCoreLaunchByUriParam; +struct ShellCoreLaunchByUriParam { + std::uint32_t option; + std::int32_t userId; +}; + +static_assert(sizeof(ShellCoreLaunchByUriParam) == 8); +static_assert(offsetof(ShellCoreLaunchByUriParam, option) == 0); +static_assert(offsetof(ShellCoreLaunchByUriParam, userId) == 4); struct ShellCoreTelemetryToken; -struct ShellCoreUmmStatus; +struct ShellCoreUmmStatus { + std::int32_t state; + std::int32_t inDeviceMuteState; +}; + +static_assert(sizeof(ShellCoreUmmStatus) == 8); +static_assert(offsetof(ShellCoreUmmStatus, state) == 0); +static_assert(offsetof(ShellCoreUmmStatus, inDeviceMuteState) == 4); struct ShellCorePbtcUserInfoList; struct ShellCorePrivacyEventData; struct ShellCorePsnAccountInfoFirst; @@ -836,7 +850,7 @@ std::uint64_t APS5_VABI sceShellCoreUtilGetFreeSizeOfAvContentsTmp() { return 0; } -int APS5_VABI sceShellCoreUtilGetFreeSizeOfUserPartition(std::uint64_t* firstSize, std::uint64_t* secondSize) { +int APS5_VABI sceShellCoreUtilGetFreeSizeOfUserPartition(std::uint64_t* freeSize, std::uint64_t* totalSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -881,7 +895,7 @@ int APS5_VABI sceShellCoreUtilGetHidConfigName(char* value, std::size_t capacity return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigNum(std::uint32_t* count) { +int APS5_VABI sceShellCoreUtilGetHidConfigNum(std::int32_t* configNum) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1036,7 +1050,7 @@ int APS5_VABI sceShellCoreUtilGetUIStatus(std::uint64_t* status) { return 0; } -int APS5_VABI sceShellCoreUtilGetUmmStatus(std::uint32_t id, ShellCoreUmmStatus* status) { +int APS5_VABI sceShellCoreUtilGetUmmStatus(std::int32_t userId, ShellCoreUmmStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1066,7 +1080,7 @@ int APS5_VABI sceShellCoreUtilGoBackToKratosCurrentSessionGame() { return 0; } -int APS5_VABI sceShellCoreUtilHideBlocksFromUser(std::uint64_t first, std::int32_t second) { +int APS5_VABI sceShellCoreUtilHideBlocksFromUser(std::int64_t bytes, std::int32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1276,7 +1290,7 @@ int APS5_VABI sceShellCoreUtilPostActivityForPsNow(std::int32_t type, const char return 0; } -int APS5_VABI sceShellCoreUtilPostErrorLog(const char* firstText, std::uint32_t firstValue, const char* secondText, const char* thirdText, std::uint32_t secondValue, const char* fourthText) { +int APS5_VABI sceShellCoreUtilPostErrorLog(const char* titleId, std::int32_t internalErrorCode, const char* shortError, const char* messageBuffer, std::int32_t suggestionType, const char* logReport) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 5206f219ab..20a5299373 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -122,11 +122,11 @@ - [ShellCoreUtil account and privacy payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Pbtc user information occupies 800 bytes; privacy-event data occupies eight bytes after two string keys, each limited to 32 characters. The five account-information outputs occupy 8, 104, 136, 1024 and 1032 bytes; their fields remain unresolved. Category-default list output has a byte capacity and unresolved element format. Remote-play/share-play/social-screen outputs are boolean; application display-state queries take a 32-bit application ID. Only the support-site query signature is restored; no endpoint content is included. -- [ShellCoreUtil telemetry and user queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): telemetry-token output is 4112 bytes, including an eight-byte service result at offset 4104; its meaning remains unresolved. UMM status is an eight-byte output with unresolved fields. UI status is a 64-bit event mask; VM statistics and workaround-file strings use machine-sized capacities. HideBlocksFromUser transports a 64-bit value and a 32-bit value whose exact semantics remain unresolved. Eye-distance adjustment writes one byte. +- [ShellCoreUtil telemetry and user queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): telemetry-token output is 4112 bytes, including an eight-byte service result at offset 4104; its meaning remains unresolved. UI status is a 64-bit event mask; VM statistics and workaround-file strings use machine-sized capacities. Eye-distance adjustment writes one byte. -- [ShellCoreUtil launch and status arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): LaunchByUri reads an eight-byte parameter containing two 32-bit fields and swaps their order in its message; field meanings remain unresolved. Navigation consumes one 32-bit value, not the phantom vector/register arguments shown by decompilation. Workaround checks and mount requests take strings. Logout-state queries transmit two 32-bit values and return a byte output; selector meanings remain unresolved. +- [ShellCoreUtil launch and status arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Navigation consumes one 32-bit value, not the phantom vector/register arguments shown by decompilation. Workaround checks and mount requests take strings. Logout-state queries transmit two 32-bit values and return a byte output; selector meanings remain unresolved. -- [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. Error-log submission consumes four strings and two 32-bit values in the restored order; their semantic names remain unresolved. PSM events carry a 32-bit event ID and a string. IME notification consumes a 32-bit value and one byte; service meanings remain unresolved. +- [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. PSM events carry a 32-bit event ID and a string. IME notification consumes a 32-bit value and one byte; service meanings remain unresolved. - [ShellCoreUtil state setters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): UI updates take a 64-bit status and mask; app data takes two strings. Display-state setters mask their low bits, so source enum names and declared widths cannot be inferred from those masks alone. Streaming/impose/profiler setters test all 32 bits. User-focus reads take an application ID and return its user ID; writes apply a user ID to the current application. Device-index field meanings remain unresolved. From 1db52e3e4091719c9b6102e8e9ccaad16b14dd75 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 18:59:59 +0300 Subject: [PATCH 296/523] fix(systemservice): reject unsupported integer parameters --- core/libs/prx/libSceSystemService/Export.cpp | 6 ++++- .../libs/tests/GuestSystemServiceLanguage.cpp | 23 +++++++++++++++++++ docs/dev/debt/libSceSystemService.md | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceSystemService/Export.cpp b/core/libs/prx/libSceSystemService/Export.cpp index a923164119..d0b5a7623b 100644 --- a/core/libs/prx/libSceSystemService/Export.cpp +++ b/core/libs/prx/libSceSystemService/Export.cpp @@ -311,7 +311,11 @@ int APS5_VABI sceSystemServiceParamGetInt(int paramId, int* value) { case SYSTEM_SERVICE_PARAM_ID_SUMMERTIME: *value = HostTimeZone().summer ? 1 : 0; break; case SYSTEM_SERVICE_PARAM_ID_GAME_PARENTAL_LEVEL: *value = SYSTEM_SERVICE_PARAM_GAME_PARENTAL_OFF; break; case SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN: *value = SYSTEM_SERVICE_PARAM_ENTER_BUTTON_CROSS; break; - default: *value = 0; break; + default: + if ((paramId >= 100 && paramId <= 111) || (paramId >= 200 && paramId <= 213) || paramId == 300) { + NotImplemented_nid_no_patch("sceSystemServiceParamGetInt: registry-backed parameter"); + } + return SYSTEM_SERVICE_ERROR_PARAMETER; } return SYSTEM_SERVICE_OK; } diff --git a/core/libs/tests/GuestSystemServiceLanguage.cpp b/core/libs/tests/GuestSystemServiceLanguage.cpp index efd37f499b..cf65b0d3c8 100644 --- a/core/libs/tests/GuestSystemServiceLanguage.cpp +++ b/core/libs/tests/GuestSystemServiceLanguage.cpp @@ -1,6 +1,7 @@ #include "SceTypes.hpp" #include "prx/libSceSystemService/SystemService.hpp" #include +#include #include #include @@ -45,6 +46,28 @@ bool Rejected(const char* value) { } int main() { + for (int paramId = -1; paramId <= 1001; ++paramId) { + Require(sceSystemServiceParamGetInt(paramId, nullptr) == SYSTEM_SERVICE_ERROR_PARAMETER); + if ((paramId >= 1 && paramId <= 5) || paramId == 7 || paramId == 400 || paramId == 1000) continue; + int value = -12345; + if ((paramId >= 100 && paramId <= 111) || (paramId >= 200 && paramId <= 213) || paramId == 300) { + bool threw = false; + try { + static_cast(sceSystemServiceParamGetInt(paramId, &value)); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw); + } else { + Require(sceSystemServiceParamGetInt(paramId, &value) == SYSTEM_SERVICE_ERROR_PARAMETER); + } + Require(value == -12345); + } + for (int paramId : {std::numeric_limits::min(), std::numeric_limits::max()}) { + int value = -12345; + Require(sceSystemServiceParamGetInt(paramId, &value) == SYSTEM_SERVICE_ERROR_PARAMETER); + Require(value == -12345); + } SetLanguage(nullptr); Require(Language() == SYSTEM_SERVICE_PARAM_LANG_ENGLISH_US); SetLanguage(""); diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 20a5299373..0033dc629e 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -147,3 +147,5 @@ - [USB storage aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) and [USB exports](../../../core/libs/prx/libSceUsbStorage/Export.cpp): direct-jump signatures are matched to the supplied USB module. RequestMap/RequestMapWSB take six arguments; the two extra helper arguments are internal. IsExist takes a device, directory and byte output, with a separate boolean failure result. Callbacks receive an event pointer and user data. Device information writes 824 bytes, device lists hold eight 32-bit IDs, and mount-point outputs occupy 128 bytes. Event/device field meanings and initialization attributes remain unresolved. - [Assembly-only shell and clock queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): format/fsck progress returns a float in XMM0. Impose-menu, accessibility-zoom and suspend-confirmation queries take an application ID and return a boolean. Suspend-blacklist lookup forwards a 32-bit ID to SysCore. Clock changes take a string and 32-bit value; CPU pstate takes a 32-byte input structure whose fields remain unresolved, and memory pstate takes one 32-bit value. + +- [Integer system parameters](../../../core/libs/prx/libSceSystemService/Export.cpp): registry-backed parameters 100–111, 200–213 and 300 throw because their settings are not modeled. Language results omit the console SDK-version compatibility remapping, and parameters 7, 400 and 1000 do not reproduce platform-dependent availability. From 67ff7609c284a87b9abeb14f4b7ecf78d284142b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:00:52 +0300 Subject: [PATCH 297/523] fix(systemservice): define application message payload layout --- .../prx/libSceSystemService/PendingExports.cpp | 15 ++++++++++++++- docs/dev/debt/libSceSystemService.md | 1 - 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index d17afe85ce..c53f5c4acd 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -72,7 +72,20 @@ struct LncVideoFinalizeState; struct LncLocalProcessParam; struct LncAppStatus; struct LncShellUiAppStatus; -struct AppMessagingMessage; +struct AppMessagingMessage { + std::int32_t sender; + std::uint32_t messageType; + std::uint8_t payload[8192]; + std::uint32_t payloadSize; + std::uint64_t timestamp; +}; + +static_assert(sizeof(AppMessagingMessage) == 8216); +static_assert(offsetof(AppMessagingMessage, sender) == 0); +static_assert(offsetof(AppMessagingMessage, messageType) == 4); +static_assert(offsetof(AppMessagingMessage, payload) == 8); +static_assert(offsetof(AppMessagingMessage, payloadSize) == 8200); +static_assert(offsetof(AppMessagingMessage, timestamp) == 8208); struct UsbStorageEvent; struct UsbStorageDeviceInfo; diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 0033dc629e..3362d201bf 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -106,7 +106,6 @@ - [sceLncUtilKillApp, sceLncUtilKillAppWithReason](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the running title's app ID ends the title as `sceSystemServiceKillApp` does, which [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L219-L227) describes as a wrapper of `sceLncUtilKillApp`; the signatures follow its [launcher utility bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L262-L279). Another app ID throws, and so does a reason other than 0, whose meaning and effect on the console are not documented - [LoadExec descriptors](../../../core/libs/prx/libSceSystemService/LoadExec.cpp) (libSceSystemService) - on Linux, a call with an argument list replaces the process with `fexecve`, which first closes every descriptor above 2, and on macOS with `posix_spawn`, which closes them through `POSIX_SPAWN_CLOEXEC_DEFAULT`; a call without one uses `execv`, and the guest's descriptors stay open across it. On both paths descriptor 2 first gets back the host standard error a guest redirect kept, so the runtime's output does not follow the title's redirect (RetroArch's trace file, VLC's log) into the next executable. Which descriptors the console passes to the new process is not known -- [AppMessaging receive buffers](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the supplied module confirms the eight messaging argument lists, 64-bit event masks, byte-sized clear flag and 8192-byte send limit. Receive writes an 8216-byte message whose field meanings still require the sender/service handler. The protected bodies remain unchanged. - [LncUtil process and status payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process creation transmits a 72-byte parameter block; application-status entries occupy 64 bytes and shell-UI reboot entries 1336 bytes. Their fields remain unresolved. Title-ID output occupies 16 bytes; category, category type and launched-user outputs are 32-bit scalars. The argument lists are recovered through the indirect dispatch targets rather than the decompiler's apparent zero-argument wrappers. From 50c8103af21f9b0ff2a1720aa4c0527ee5036be5 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:03:00 +0300 Subject: [PATCH 298/523] fix(systemservice): recover launch and process status structures --- .../libSceSystemService/PendingExports.cpp | 145 ++++++++++++++++-- docs/dev/debt/libSceSystemService.md | 8 +- 2 files changed, 138 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index c53f5c4acd..64e265492b 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -62,16 +62,139 @@ struct ShellCorePsnAccountInfoThird; struct ShellCorePsnAccountInfoFourth; struct ShellCorePsnAccountInfoFifth; struct ShellCoreAppLaunchTypeInfo; -struct LncGpuCrashAppStatus; -struct LncLaunchParam; +struct LncGpuCrashAppStatus { + char titleId[16]; + std::int32_t appId; + std::int32_t appLocalPid; + char path[1024]; + std::int32_t appType; + std::int32_t coredumpReason; + char corefileName[256]; + std::uint32_t exceptionCode; + std::uint32_t appCategoryType; +}; + +static_assert(sizeof(LncGpuCrashAppStatus) == 1320); +static_assert(offsetof(LncGpuCrashAppStatus, titleId) == 0); +static_assert(offsetof(LncGpuCrashAppStatus, appId) == 16); +static_assert(offsetof(LncGpuCrashAppStatus, appLocalPid) == 20); +static_assert(offsetof(LncGpuCrashAppStatus, path) == 24); +static_assert(offsetof(LncGpuCrashAppStatus, appType) == 1048); +static_assert(offsetof(LncGpuCrashAppStatus, coredumpReason) == 1052); +static_assert(offsetof(LncGpuCrashAppStatus, corefileName) == 1056); +static_assert(offsetof(LncGpuCrashAppStatus, exceptionCode) == 1312); +static_assert(offsetof(LncGpuCrashAppStatus, appCategoryType) == 1316); +struct LncLaunchParam { + std::uint32_t size; + std::int32_t userId; + std::int32_t appAttr; + std::int32_t enableCrashReport; + std::uint64_t checkFlag; + std::uint64_t contextId; + bool isSpeculativeLaunch; +}; + +static_assert(sizeof(LncLaunchParam) == 40); +static_assert(offsetof(LncLaunchParam, size) == 0); +static_assert(offsetof(LncLaunchParam, userId) == 4); +static_assert(offsetof(LncLaunchParam, appAttr) == 8); +static_assert(offsetof(LncLaunchParam, enableCrashReport) == 12); +static_assert(offsetof(LncLaunchParam, checkFlag) == 16); +static_assert(offsetof(LncLaunchParam, contextId) == 24); +static_assert(offsetof(LncLaunchParam, isSpeculativeLaunch) == 32); struct LncDaemonEvent; struct LncShellUiEvent; -struct LncLocalProcessStatus; -struct LncCoredumpState; -struct LncVideoFinalizeState; +struct LncLocalProcessStatus { + std::int32_t appLocalPid; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(LncLocalProcessStatus) == 36); +static_assert(offsetof(LncLocalProcessStatus, appLocalPid) == 0); +static_assert(offsetof(LncLocalProcessStatus, reserved) == 4); +struct LncCoredumpState { + std::uint32_t progress; + std::uint32_t isFinished; + std::int32_t result; +}; + +static_assert(sizeof(LncCoredumpState) == 12); +static_assert(offsetof(LncCoredumpState, progress) == 0); +static_assert(offsetof(LncCoredumpState, isFinished) == 4); +static_assert(offsetof(LncCoredumpState, result) == 8); +struct LncVideoFinalizeState { + std::uint32_t progress; + std::int32_t result; +}; + +static_assert(sizeof(LncVideoFinalizeState) == 8); +static_assert(offsetof(LncVideoFinalizeState, progress) == 0); +static_assert(offsetof(LncVideoFinalizeState, result) == 4); struct LncLocalProcessParam; -struct LncAppStatus; -struct LncShellUiAppStatus; +struct LncAppStatus { + std::int32_t appId; + std::int32_t launchRequestAppId; + std::int32_t appType; + std::int32_t appAttr; + char titleId[16]; + std::uint32_t appCategoryType; + std::int32_t appBootMode; + std::int32_t appBootSubMode; + char appVer[6]; + char contentVersion[11]; + bool isSystemExApp; + bool isCdlgInitialized; +}; + +static_assert(sizeof(LncAppStatus) == 64); +static_assert(offsetof(LncAppStatus, appId) == 0); +static_assert(offsetof(LncAppStatus, launchRequestAppId) == 4); +static_assert(offsetof(LncAppStatus, appType) == 8); +static_assert(offsetof(LncAppStatus, appAttr) == 12); +static_assert(offsetof(LncAppStatus, titleId) == 16); +static_assert(offsetof(LncAppStatus, appCategoryType) == 32); +static_assert(offsetof(LncAppStatus, appBootMode) == 36); +static_assert(offsetof(LncAppStatus, appBootSubMode) == 40); +static_assert(offsetof(LncAppStatus, appVer) == 44); +static_assert(offsetof(LncAppStatus, contentVersion) == 50); +static_assert(offsetof(LncAppStatus, isSystemExApp) == 61); +static_assert(offsetof(LncAppStatus, isCdlgInitialized) == 62); +struct LncShellUiAppStatus { + std::int32_t appId; + std::int32_t appType; + std::int32_t appAttr; + std::int32_t launchRequestAppId; + std::int32_t userId; + std::int32_t isActiveCdlg; + char path[1024]; + char sceSysDir[256]; + std::int32_t isCoredumped; + std::int32_t isPrxModuleLoadFailed; + std::int32_t appLocalPid; + std::int32_t crashReportMode; + char category[4]; + std::uint32_t appCategoryType; + std::int32_t appBootMode; + std::int32_t appBootSubMode; +}; + +static_assert(sizeof(LncShellUiAppStatus) == 1336); +static_assert(offsetof(LncShellUiAppStatus, appId) == 0); +static_assert(offsetof(LncShellUiAppStatus, appType) == 4); +static_assert(offsetof(LncShellUiAppStatus, appAttr) == 8); +static_assert(offsetof(LncShellUiAppStatus, launchRequestAppId) == 12); +static_assert(offsetof(LncShellUiAppStatus, userId) == 16); +static_assert(offsetof(LncShellUiAppStatus, isActiveCdlg) == 20); +static_assert(offsetof(LncShellUiAppStatus, path) == 24); +static_assert(offsetof(LncShellUiAppStatus, sceSysDir) == 1048); +static_assert(offsetof(LncShellUiAppStatus, isCoredumped) == 1304); +static_assert(offsetof(LncShellUiAppStatus, isPrxModuleLoadFailed) == 1308); +static_assert(offsetof(LncShellUiAppStatus, appLocalPid) == 1312); +static_assert(offsetof(LncShellUiAppStatus, crashReportMode) == 1316); +static_assert(offsetof(LncShellUiAppStatus, category) == 1320); +static_assert(offsetof(LncShellUiAppStatus, appCategoryType) == 1324); +static_assert(offsetof(LncShellUiAppStatus, appBootMode) == 1328); +static_assert(offsetof(LncShellUiAppStatus, appBootSubMode) == 1332); struct AppMessagingMessage { std::int32_t sender; std::uint32_t messageType; @@ -168,7 +291,7 @@ int APS5_VABI sceLncUtilBlockingGetEventForDaemon() { return 0; } -int APS5_VABI sceLncUtilContinueApp(std::uint32_t appId) { +int APS5_VABI sceLncUtilContinueApp(std::int32_t appId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -213,7 +336,7 @@ int APS5_VABI sceLncUtilForceKillLocalProcess(std::int32_t appId, std::int32_t p return 0; } -int APS5_VABI sceLncUtilGetAppCategory(std::int32_t appId, std::uint32_t* category) { +int APS5_VABI sceLncUtilGetAppCategory(std::int32_t appId, char* category) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -413,7 +536,7 @@ int APS5_VABI sceLncUtilNotifyCoredumpRequestProgress(std::int32_t appId, std::i return 0; } -int APS5_VABI sceLncUtilNotifyVshReady(std::uint32_t value) { +int APS5_VABI sceLncUtilNotifyVshReady(std::uint32_t errorCode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -468,7 +591,7 @@ int APS5_VABI sceLncUtilResumeLocalProcess(std::int32_t processId) { return 0; } -int APS5_VABI sceLncUtilSetAppFocus(std::int32_t appId, std::uint64_t first, std::uint64_t second) { +int APS5_VABI sceLncUtilSetAppFocus(std::int32_t appId, std::uint64_t flags, std::uint64_t contextId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 3362d201bf..d0eccf7889 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -107,11 +107,11 @@ - [LoadExec descriptors](../../../core/libs/prx/libSceSystemService/LoadExec.cpp) (libSceSystemService) - on Linux, a call with an argument list replaces the process with `fexecve`, which first closes every descriptor above 2, and on macOS with `posix_spawn`, which closes them through `POSIX_SPAWN_CLOEXEC_DEFAULT`; a call without one uses `execv`, and the guest's descriptors stay open across it. On both paths descriptor 2 first gets back the host standard error a guest redirect kept, so the runtime's output does not follow the title's redirect (RetroArch's trace file, VLC's log) into the next executable. Which descriptors the console passes to the new process is not known -- [LncUtil process and status payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process creation transmits a 72-byte parameter block; application-status entries occupy 64 bytes and shell-UI reboot entries 1336 bytes. Their fields remain unresolved. Title-ID output occupies 16 bytes; category, category type and launched-user outputs are 32-bit scalars. The argument lists are recovered through the indirect dispatch targets rather than the decompiler's apparent zero-argument wrappers. +- [LncUtil process and status payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process creation transmits a 72-byte parameter block whose fields remain unresolved. Title-ID output occupies 16 bytes; category output is a four-byte string, while category type and launched-user outputs are 32-bit scalars. The argument lists are recovered through the indirect dispatch targets rather than the decompiler's apparent zero-argument wrappers. -- [LncUtil event and diagnostic payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): local-process status entries are 36 bytes, coredump state 12 bytes and video-finalization state eight bytes; their field meanings remain unresolved. Daemon and shell-UI events are distinct output structures. GetDbgExecutablePath writes up to a 1024-byte protocol output and forwards a 64-bit selector whose meaning is unresolved. Boolean outputs and the two direct boolean budget/mode queries are confirmed. +- [LncUtil event and diagnostic payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Daemon and shell-UI events are distinct output structures. GetDbgExecutablePath writes up to a 1024-byte protocol output and forwards a 64-bit selector whose meaning is unresolved. Boolean outputs and the two direct boolean budget/mode queries are confirmed. -- [LncUtil launch and lifecycle payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 40-byte launch parameter fields and meanings of several 64-bit focus/resume/suspend/exception values remain unresolved. Launch and load-exec use null-terminated argument vectors; asynchronous launch/kill write a 32-bit request ID. The argument positions and transported widths are confirmed, but the service operations remain protected stubs. +- [LncUtil launch and lifecycle payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Flag values and service-side focus/resume/suspend/exception behavior remain unresolved. Launch and load-exec use null-terminated argument vectors; asynchronous launch/kill write a 32-bit request ID. The argument positions and transported widths are confirmed, but the service operations remain protected stubs. - [ShellCoreUtil application data](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): GetAppLaunchTypeInfo writes eight bytes whose fields are unresolved. Application-data and launched-parameter keys are null-terminated strings, with 32-bit capacities/outputs. Format/fsck paths and mini-app return values are strings. File-formatting and service implementations remain protected stubs. @@ -137,7 +137,7 @@ - [SystemService direct aliases](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly-only jumps now share the confirmed signatures of their LncUtil/ShellCoreUtil targets for application-ID lists, remote-play/UI status, navigation, controller-focus permissions and boolean budget/status queries. Aliases of unresolved targets still require target or caller analysis. -- [LncUtil collapsed dispatch stubs](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly table targets confirm application-ID lookup takes a title-ID string, current/focused status outputs occupy 64 bytes, GPU-crash status occupies 1320 bytes, and dialog activation writes one byte. Shared-memory registration takes a string. GPU-crash fields and the VSH-ready selector meaning remain unresolved; collapsed assembly procedures are not missing exports. +- [LncUtil collapsed dispatch stubs](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): assembly table targets confirm application-ID lookup takes a title-ID string, current/focused status outputs occupy 64 bytes, GPU-crash status occupies 1320 bytes, and dialog activation writes one byte. Shared-memory registration takes a string. The protected service implementations remain unchanged; collapsed assembly procedures are not missing exports. - [SystemService launch and telemetry arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): store-launch parameters contain only a four-byte size; web-app launch takes one string argument. Process lifecycle wrappers forward the verified LncUtil argument lists. TelemetrySetData takes a string key, binary payload and machine-sized length; TelemetrySetData2 takes two strings. Token storage consumes a 32-bit identifier and string; only its signature is restored, with no token values included. From b167045830c954df38a1c790c47d8347f3906ce0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:06:25 +0300 Subject: [PATCH 299/523] fix(systemservice): recover activation handles and device argument types --- .../libSceSystemService/PendingExports.cpp | 36 +++++++++---------- docs/dev/debt/libSceSystemService.md | 12 +++---- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 64e265492b..9841ee91cf 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -691,21 +691,21 @@ int APS5_VABI sceShellCoreUtilAcquireSharePlayCpuBudget() { return 0; } -int APS5_VABI sceShellCoreUtilActivateAbort() { +int APS5_VABI sceShellCoreUtilActivateAbort(std::intptr_t handle) { return 0; } -int APS5_VABI sceShellCoreUtilActivateGetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { +int APS5_VABI sceShellCoreUtilActivateGetStatus(std::intptr_t handle, std::int32_t* status, std::int32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilActivateInit(std::uint32_t type, std::uintptr_t* handle) { +int APS5_VABI sceShellCoreUtilActivateInit(std::int32_t type, std::intptr_t* handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilActivateIsActivated(std::uint32_t type, std::uint8_t* activated) { +int APS5_VABI sceShellCoreUtilActivateIsActivated(std::intptr_t handle, bool* activated) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -714,15 +714,15 @@ int APS5_VABI sceShellCoreUtilActivateRecordActivation() { return 0; } -int APS5_VABI sceShellCoreUtilActivateStart() { +int APS5_VABI sceShellCoreUtilActivateStart(std::intptr_t handle) { return 0; } -int APS5_VABI sceShellCoreUtilActivateStartAsync() { +int APS5_VABI sceShellCoreUtilActivateStartAsync(std::intptr_t handle) { return 0; } -int APS5_VABI sceShellCoreUtilActivateTerm() { +int APS5_VABI sceShellCoreUtilActivateTerm(std::intptr_t handle) { return 0; } @@ -781,7 +781,7 @@ int APS5_VABI sceShellCoreUtilDeleteDiscInstalledTitleWorkaroundFile() { return 0; } -int APS5_VABI sceShellCoreUtilDeleteDownloadedHidConfigFile(std::uint32_t deviceType) { +int APS5_VABI sceShellCoreUtilDeleteDownloadedHidConfigFile(std::int32_t configIndex) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -801,7 +801,7 @@ int APS5_VABI sceShellCoreUtilDoFsck(const char* path) { return 0; } -int APS5_VABI sceShellCoreUtilDownloadHidConfigFileFromServer(std::uint32_t deviceType) { +int APS5_VABI sceShellCoreUtilDownloadHidConfigFileFromServer(std::int32_t configIndex) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -966,12 +966,12 @@ int APS5_VABI sceShellCoreUtilGetCrashReportUploadStatus() { return 0; } -int APS5_VABI sceShellCoreUtilGetDeviceIndexBehavior(std::uint32_t* first, std::uint32_t* second, std::uint32_t* third) { +int APS5_VABI sceShellCoreUtilGetDeviceIndexBehavior(std::uint32_t* behavior, std::int32_t* userId, std::uint32_t* scene) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetDeviceIndexBehaviorWithTimeout(std::uint32_t* first, std::uint32_t* second, std::uint32_t* third, std::uint8_t timeout) { +int APS5_VABI sceShellCoreUtilGetDeviceIndexBehaviorWithTimeout(std::uint32_t* behavior, std::int32_t* userId, std::uint32_t* scene, std::uint8_t timeout) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -991,7 +991,7 @@ int APS5_VABI sceShellCoreUtilGetFreeSizeOfUserPartition(std::uint64_t* freeSize return 0; } -int APS5_VABI sceShellCoreUtilGetFsckProgress(const char* path, std::uint32_t* progress) { +int APS5_VABI sceShellCoreUtilGetFsckProgress(const char* titleId, std::uint32_t* progress) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1016,17 +1016,17 @@ int APS5_VABI sceShellCoreUtilGetGpuLoadEmulationModeByAppId(std::int32_t appId) return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigFileInfoString(char* value, std::size_t capacity, std::int32_t index) { +int APS5_VABI sceShellCoreUtilGetHidConfigFileInfoString(char* value, std::size_t capacity, std::int32_t configIndex) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigFileString(char* value, std::size_t capacity, std::int32_t index) { +int APS5_VABI sceShellCoreUtilGetHidConfigFileString(char* value, std::size_t capacity, std::int32_t configIndex) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellCoreUtilGetHidConfigName(char* value, std::size_t capacity, std::int32_t index) { +int APS5_VABI sceShellCoreUtilGetHidConfigName(char* value, std::size_t capacity, std::int32_t configIndex) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1401,7 +1401,7 @@ int APS5_VABI sceShellCoreUtilNotifyFsReadError(std::int32_t first, std::int32_t return 0; } -int APS5_VABI sceShellCoreUtilNotifyImeStatusChanged(std::int32_t appId, std::uint8_t status) { +int APS5_VABI sceShellCoreUtilNotifyImeStatusChanged(std::int32_t imeStatus, std::uint8_t isCalledFromGame) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1521,7 +1521,7 @@ int APS5_VABI sceShellCoreUtilSetBgmProhibition(std::uint32_t value) { return 0; } -int APS5_VABI sceShellCoreUtilSetDeviceIndexBehavior(std::uint8_t first, std::uint32_t second, std::uint16_t third) { +int APS5_VABI sceShellCoreUtilSetDeviceIndexBehavior(std::uint32_t behavior, std::int32_t userId, std::uint8_t scene) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1661,7 +1661,7 @@ int APS5_VABI sceShellCoreUtilStopPsNowGame(const char* data) { return 0; } -int APS5_VABI sceShellCoreUtilTalkKeyEventReceived(std::uint64_t first, std::uint32_t second, std::uint32_t third) { +int APS5_VABI sceShellCoreUtilTalkKeyEventReceived(std::int64_t deviceId, std::int32_t userId, std::int32_t keyEventType) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index d0eccf7889..8d9548f41d 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -115,9 +115,9 @@ - [ShellCoreUtil application data](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): GetAppLaunchTypeInfo writes eight bytes whose fields are unresolved. Application-data and launched-parameter keys are null-terminated strings, with 32-bit capacities/outputs. Format/fsck paths and mini-app return values are strings. File-formatting and service implementations remain protected stubs. -- [ShellCoreUtil queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): launched-parameter strings have signed 32-bit capacities; checker strings use machine-sized capacities and one or two string keys. Cloud status and device-index outputs are 32-bit scalars; the timeout variant consumes one byte. Partition-size output and the direct temporary-content free-size return are 64-bit. Device-index field meanings and the service-side checker protocol remain unresolved. +- [ShellCoreUtil queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): launched-parameter strings have signed 32-bit capacities; checker strings use machine-sized capacities and one or two string keys. Cloud status and device-index outputs are 32-bit scalars; the timeout variant consumes one byte. Partition-size output and the direct temporary-content free-size return are 64-bit. The service-side checker protocol remains unresolved. Device-index scene output uses a 32-bit native store despite the managed wrapper using a byte-backed scene enum; the native output width is preserved. -- [ShellCoreUtil storage and HID queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): partition free sizes and required application-content size are 64-bit outputs; fsck takes a path and a 32-bit progress output. HID string queries transport a 64-bit capacity and 32-bit selector, while output-vector lengths sign-extend the capacity's low 32 bits. The selector meaning and IDU mode byte values remain unresolved. Streaming status and the compositor-profiler return are boolean; the compositor-profiler and current GPU-load queries consume no caller arguments. +- [ShellCoreUtil storage and HID queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): partition free sizes and required application-content size are 64-bit outputs; fsck progress takes a title ID and a 32-bit progress output. HID string queries transport a 64-bit capacity and 32-bit selector, while output-vector lengths sign-extend the capacity's low 32 bits. HID selectors are configuration indices; the wrapper declares 32-bit capacities while the native code transports 64 bits, so their declared width remains unresolved. IDU mode byte values remain unresolved. Streaming status and the compositor-profiler return are boolean; the compositor-profiler and current GPU-load queries consume no caller arguments. - [ShellCoreUtil account and privacy payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Pbtc user information occupies 800 bytes; privacy-event data occupies eight bytes after two string keys, each limited to 32 characters. The five account-information outputs occupy 8, 104, 136, 1024 and 1032 bytes; their fields remain unresolved. Category-default list output has a byte capacity and unresolved element format. Remote-play/share-play/social-screen outputs are boolean; application display-state queries take a 32-bit application ID. Only the support-site query signature is restored; no endpoint content is included. @@ -125,13 +125,13 @@ - [ShellCoreUtil launch and status arguments](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): Navigation consumes one 32-bit value, not the phantom vector/register arguments shown by decompilation. Workaround checks and mount requests take strings. Logout-state queries transmit two 32-bit values and return a byte output; selector meanings remain unresolved. -- [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. PSM events carry a 32-bit event ID and a string. IME notification consumes a 32-bit value and one byte; service meanings remain unresolved. +- [ShellCoreUtil notifications](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): IDU notifications use a signed 64-bit identifier, optional text and a 32-bit type. PSM events carry a 32-bit event ID and a string. -- [ShellCoreUtil state setters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): UI updates take a 64-bit status and mask; app data takes two strings. Display-state setters mask their low bits, so source enum names and declared widths cannot be inferred from those masks alone. Streaming/impose/profiler setters test all 32 bits. User-focus reads take an application ID and return its user ID; writes apply a user ID to the current application. Device-index field meanings remain unresolved. +- [ShellCoreUtil state setters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): UI updates take a 64-bit status and mask; app data takes two strings. Display-state setters mask their low bits, so source enum names and declared widths cannot be inferred from those masks alone. Streaming/impose/profiler setters test all 32 bits. User-focus reads take an application ID and return its user ID; writes apply a user ID to the current application. -- [ShellCoreUtil dialog parameters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 24-byte error-dialog parameter contains a message pointer at offset zero, a null-terminated vector of string arguments at offset eight and a byte flag at offset 16. Field offsets and size are asserted; the flag meaning remains unresolved. Start/stop streaming-game and unmount requests take strings. Talk-key events transport a 64-bit value and two 32-bit values whose meanings remain unresolved. Bus-transfer testing writes one byte after a string input. +- [ShellCoreUtil dialog parameters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 24-byte error-dialog parameter contains a message pointer at offset zero, a null-terminated vector of string arguments at offset eight and a byte flag at offset 16. Field offsets and size are asserted; the flag meaning remains unresolved. Start/stop streaming-game and unmount requests take strings. Bus-transfer testing writes one byte after a string input. -- [SystemService activation and process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): activation wrappers validate a machine-sized handle and expose 32-bit status/result outputs or a byte activation flag. Shell activation helpers also receive a type selector; constant-return activation exports still need caller evidence for unused parameters. SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. +- [SystemService activation and process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): activation wrappers validate a machine-sized handle and expose 32-bit status/result outputs or a byte activation flag. Shell activation initialization also receives a type selector; managed callers confirm the machine-sized handle consumed by constant-return start/abort/terminate exports. SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. - [SystemService query payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): daemon events write 12 bytes; local-process entries occupy 36 bytes, consisting of a 32-bit value and 32-byte name. PS-button extraction reads a 16-byte event and rearranges its fields into a distinct 16-byte output; semantic names remain unresolved. Privacy definition/version queries take string keys and distinct output types; their layouts remain unresolved. Workaround information writes four bytes at offset zero and 16 bytes at offset eight. From abe9c62055be0f6177dbcd53987d2f6ee7f6c24e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:12:47 +0300 Subject: [PATCH 300/523] fix(contentexport): restore file and content operation signatures --- core/libs/SceTypes.hpp | 28 ++++++++++++++++ core/libs/prx/libSceContentExport/Export.cpp | 25 +++++++------- core/libs/tests/GuestContentExport.cpp | 35 ++++++++++---------- docs/dev/debt/libSceContentExport.md | 3 +- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 4f8d8e8fd7..f7b002c4d9 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1690,6 +1690,34 @@ struct AppContentBootParam { struct AppContentMountPoint { char data[16]; }; +using ContentExportMallocCallback = void* (APS5_VABI *)(std::size_t size, void* userData); +using ContentExportFreeCallback = void (APS5_VABI *)(void* memory, void* userData); +using ContentExportDataCallback = std::int32_t (APS5_VABI *)(const void** data, std::size_t* size, void* userData); + +struct ContentExportInitParam { + ContentExportMallocCallback mallocFunc; + ContentExportFreeCallback freeFunc; + void* userData; +}; + +static_assert(sizeof(ContentExportInitParam) == 24); +static_assert(offsetof(ContentExportInitParam, freeFunc) == 8); +static_assert(offsetof(ContentExportInitParam, userData) == 16); + +struct ContentExportSfParam { + std::size_t size; + const char* metadata; + const std::int64_t* contentIds; + std::uint32_t contentCount; + std::uint32_t option; +}; + +static_assert(sizeof(ContentExportSfParam) == 32); +static_assert(offsetof(ContentExportSfParam, metadata) == 8); +static_assert(offsetof(ContentExportSfParam, contentIds) == 16); +static_assert(offsetof(ContentExportSfParam, contentCount) == 24); +static_assert(offsetof(ContentExportSfParam, option) == 28); + struct ContentExportInitParam2 { void* malloc_func; void* free_func; diff --git a/core/libs/prx/libSceContentExport/Export.cpp b/core/libs/prx/libSceContentExport/Export.cpp index b8a366cc65..eb87ef6971 100644 --- a/core/libs/prx/libSceContentExport/Export.cpp +++ b/core/libs/prx/libSceContentExport/Export.cpp @@ -40,22 +40,22 @@ int APS5_VABI sceContentExportFinish(std::int32_t expId) { return 0; } -int APS5_VABI sceContentExportFromData(void) { +int APS5_VABI sceContentExportFromData(std::int32_t expId, const ContentExportParam* param, std::size_t contentLength, ContentExportDataCallback callback, void* userData, char* path, std::size_t pathBufferLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportFromFile(void) { +int APS5_VABI sceContentExportFromFile(std::int32_t expId, const ContentExportParam* param, const char* sourcePath, char* path, std::size_t pathBufferLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportFromFileWithThumbnail(void) { +int APS5_VABI sceContentExportFromFileWithThumbnail(std::int32_t expId, const ContentExportParam* param, const char* sourcePath, const char* thumbnailPath, char* path, std::size_t pathBufferLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportAddToFavorites(void) { +int APS5_VABI sceContentExportAddToFavorites(std::int64_t contentId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -75,22 +75,22 @@ int APS5_VABI sceContentExportFrom(void) { return 0; } -int APS5_VABI sceContentExportFromFileForSf(void) { +int APS5_VABI sceContentExportFromFileForSf(std::int32_t expId, const ContentExportParam* param, const ContentExportSfParam* sfParam, const char* sourcePath, const char* thumbnailPath, char* path, std::size_t pathBufferLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportFromFileWithContentIdList(void) { +int APS5_VABI sceContentExportFromFileWithContentIdList(std::int32_t expId, const ContentExportParam* param, const char* sourcePath, const char* thumbnailPath, const std::int64_t* contentIds, std::uint32_t contentCount, char* path, std::size_t pathBufferLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportFromFileWithTitleIdList(void) { +int APS5_VABI sceContentExportFromFileWithTitleIdList(std::int32_t expId, const ContentExportParam* param, const char* sourcePath, const char* thumbnailPath, const char* titleIdList, char* path, std::size_t pathBufferLength) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportInit(void) { +int APS5_VABI sceContentExportInit(const ContentExportInitParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -100,17 +100,17 @@ int APS5_VABI sceContentExportMalloc(void) { return 0; } -int APS5_VABI sceContentExportRemoveFromFavorites(void) { +int APS5_VABI sceContentExportRemoveFromFavorites(std::int64_t contentId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportRenameContent(void) { +int APS5_VABI sceContentExportRenameContent(std::int64_t contentId, const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceContentExportValidateContents(void) { +int APS5_VABI sceContentExportValidateContents(const std::int64_t* contentIds, std::uint32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -136,8 +136,7 @@ int APS5_VABI sceContentExportCancel(std::int32_t expId) { throw std::runtime_error(std::string(__func__) + ": cancelling a session with no running export is unknown"); } -int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const ContentExportParam* param, const ContentExportCallbackParam* contentCallbackParam, - const char* thumbnailType, const ContentExportCallbackParam* thumbnailCallbackParam, char* path, std::size_t pathBufferLength) { +int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const ContentExportParam* param, const ContentExportCallbackParam* contentCallbackParam, const char* thumbnailType, const ContentExportCallbackParam* thumbnailCallbackParam, char* path, std::size_t pathBufferLength) { std::lock_guard lock(g_sessionMutex); RequireSession(__func__, expId); throw std::runtime_error(std::string(__func__) + ": the host has no media library to export into"); diff --git a/core/libs/tests/GuestContentExport.cpp b/core/libs/tests/GuestContentExport.cpp index 40c6aa941d..21229622e9 100644 --- a/core/libs/tests/GuestContentExport.cpp +++ b/core/libs/tests/GuestContentExport.cpp @@ -4,18 +4,18 @@ #include #include -extern "C" int APS5_VABI sceContentExportAddToFavorites(void); +extern "C" int APS5_VABI sceContentExportAddToFavorites(std::int64_t contentId); extern "C" int APS5_VABI sceContentExportDataProvideFunction(void); extern "C" int APS5_VABI sceContentExportFree(void); extern "C" int APS5_VABI sceContentExportFrom(void); -extern "C" int APS5_VABI sceContentExportFromFileForSf(void); -extern "C" int APS5_VABI sceContentExportFromFileWithContentIdList(void); -extern "C" int APS5_VABI sceContentExportFromFileWithTitleIdList(void); -extern "C" int APS5_VABI sceContentExportInit(void); +extern "C" int APS5_VABI sceContentExportFromFileForSf(std::int32_t expId, const ContentExportParam* param, const ContentExportSfParam* sfParam, const char* sourcePath, const char* thumbnailPath, char* path, std::size_t pathBufferLength); +extern "C" int APS5_VABI sceContentExportFromFileWithContentIdList(std::int32_t expId, const ContentExportParam* param, const char* sourcePath, const char* thumbnailPath, const std::int64_t* contentIds, std::uint32_t contentCount, char* path, std::size_t pathBufferLength); +extern "C" int APS5_VABI sceContentExportFromFileWithTitleIdList(std::int32_t expId, const ContentExportParam* param, const char* sourcePath, const char* thumbnailPath, const char* titleIdList, char* path, std::size_t pathBufferLength); +extern "C" int APS5_VABI sceContentExportInit(const ContentExportInitParam* param); extern "C" int APS5_VABI sceContentExportMalloc(void); -extern "C" int APS5_VABI sceContentExportRemoveFromFavorites(void); -extern "C" int APS5_VABI sceContentExportRenameContent(void); -extern "C" int APS5_VABI sceContentExportValidateContents(void); +extern "C" int APS5_VABI sceContentExportRemoveFromFavorites(std::int64_t contentId); +extern "C" int APS5_VABI sceContentExportRenameContent(std::int64_t contentId, const char* name); +extern "C" int APS5_VABI sceContentExportValidateContents(const std::int64_t* contentIds, std::uint32_t count); extern "C" int APS5_VABI sceContentExportInit2(const ContentExportInitParam2* init_param); extern "C" int APS5_VABI sceContentExportTerm(void); @@ -23,8 +23,7 @@ extern "C" int APS5_VABI sceContentExportStart(void); extern "C" int APS5_VABI sceContentExportFinish(std::int32_t expId); extern "C" int APS5_VABI sceContentExportGetProgress(std::int32_t expId); extern "C" int APS5_VABI sceContentExportCancel(std::int32_t expId); -extern "C" int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const ContentExportParam* param, const ContentExportCallbackParam* contentCallbackParam, - const char* thumbnailType, const ContentExportCallbackParam* thumbnailCallbackParam, char* path, std::size_t pathBufferLength); +extern "C" int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const ContentExportParam* param, const ContentExportCallbackParam* contentCallbackParam, const char* thumbnailType, const ContentExportCallbackParam* thumbnailCallbackParam, char* path, std::size_t pathBufferLength); namespace { @@ -98,17 +97,17 @@ int main() { Require(sceContentExportTerm() == 0); Require(Throws([] { sceContentExportStart(); })); Require(Throws([&] { sceContentExportFinish(reopened); })); - Require(Throws([] { sceContentExportAddToFavorites(); })); + Require(Throws([] { sceContentExportAddToFavorites(0); })); Require(Throws([] { sceContentExportDataProvideFunction(); })); Require(Throws([] { sceContentExportFree(); })); Require(Throws([] { sceContentExportFrom(); })); - Require(Throws([] { sceContentExportFromFileForSf(); })); - Require(Throws([] { sceContentExportFromFileWithContentIdList(); })); - Require(Throws([] { sceContentExportFromFileWithTitleIdList(); })); - Require(Throws([] { sceContentExportInit(); })); + Require(Throws([] { sceContentExportFromFileForSf(0, nullptr, nullptr, nullptr, nullptr, nullptr, 0); })); + Require(Throws([] { sceContentExportFromFileWithContentIdList(0, nullptr, nullptr, nullptr, nullptr, 0, nullptr, 0); })); + Require(Throws([] { sceContentExportFromFileWithTitleIdList(0, nullptr, nullptr, nullptr, nullptr, nullptr, 0); })); + Require(Throws([] { sceContentExportInit(nullptr); })); Require(Throws([] { sceContentExportMalloc(); })); - Require(Throws([] { sceContentExportRemoveFromFavorites(); })); - Require(Throws([] { sceContentExportRenameContent(); })); - Require(Throws([] { sceContentExportValidateContents(); })); + Require(Throws([] { sceContentExportRemoveFromFavorites(0); })); + Require(Throws([] { sceContentExportRenameContent(0, nullptr); })); + Require(Throws([] { sceContentExportValidateContents(nullptr, 0); })); } diff --git a/docs/dev/debt/libSceContentExport.md b/docs/dev/debt/libSceContentExport.md index 18355bf87b..d7d9e2f0d8 100644 --- a/docs/dev/debt/libSceContentExport.md +++ b/docs/dev/debt/libSceContentExport.md @@ -6,5 +6,6 @@ ### Unknown function info -- PlayStation 5 firmware exports declared without parameters, signatures unknown: [sceContentExportAddToFavorites, sceContentExportDataProvideFunction, sceContentExportFree, sceContentExportFrom, sceContentExportFromFileForSf, sceContentExportFromFileWithContentIdList, sceContentExportFromFileWithTitleIdList, sceContentExportInit, sceContentExportMalloc, sceContentExportRemoveFromFavorites, sceContentExportRenameContent, sceContentExportValidateContents](../../../core/libs/prx/libSceContentExport/Export.cpp) (libSceContentExport). Names from the firmware NID database; unimplemented and throw `NotImplemented_nid_no_patch`. +- [sceContentExportDataProvideFunction, sceContentExportFree, sceContentExportFrom and sceContentExportMalloc](../../../core/libs/prx/libSceContentExport/Export.cpp): their signatures remain unresolved; these names have no matching exports in the supplied module's C or assembly listing. +- [Content export arguments](../../../core/libs/prx/libSceContentExport/Export.cpp): file/data entry points and content-ID operations have restored argument lists. The Share Factory parameter contains a size, metadata string, content-ID list, count and option; metadata format and option values remain unresolved. Protected export bodies are unchanged. From 61b365f2d228b3192a84742ec841a46d25cb101f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:13:32 +0300 Subject: [PATCH 301/523] fix(contentexport): recover session slots and idle state errors --- core/libs/prx/libSceContentExport/Export.cpp | 26 +++++++++++--------- core/libs/tests/GuestContentExport.cpp | 23 ++++++++++------- docs/dev/debt/libSceContentExport.md | 2 +- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libSceContentExport/Export.cpp b/core/libs/prx/libSceContentExport/Export.cpp index eb87ef6971..0c5b205edf 100644 --- a/core/libs/prx/libSceContentExport/Export.cpp +++ b/core/libs/prx/libSceContentExport/Export.cpp @@ -3,18 +3,20 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include -#include #include #include #include #include namespace { -constexpr std::size_t MaxExecution = 10; +constexpr std::int32_t MaxExecution = 10; +constexpr int ErrorInvalidState = static_cast(0x809D3002u); +constexpr int ErrorBusy = static_cast(0x809D3003u); +constexpr int ErrorNotInitialized = static_cast(0x809D3004u); +constexpr int ErrorExecutionMax = static_cast(0x809D3018u); std::atomic g_initialized{false}; std::mutex g_sessionMutex; std::set g_sessions; -std::int32_t g_lastSessionId = 0; void RequireSession(const char* function, std::int32_t expId) { if (!g_initialized.load()) throw std::logic_error(std::string(function) + ": not initialized"); @@ -117,23 +119,23 @@ int APS5_VABI sceContentExportValidateContents(const std::int64_t* contentIds, s int APS5_VABI sceContentExportStart(void) { std::lock_guard lock(g_sessionMutex); - if (!g_initialized.load()) throw std::logic_error(std::string(__func__) + ": not initialized"); - if (g_sessions.size() >= MaxExecution) throw std::runtime_error(std::string(__func__) + ": behaviour beyond 10 live export sessions is unknown"); - if (g_lastSessionId == std::numeric_limits::max()) throw std::runtime_error(std::string(__func__) + ": export session ids exhausted"); - g_sessions.insert(++g_lastSessionId); - return g_lastSessionId; + if (!g_initialized.load()) return ErrorNotInitialized; + for (std::int32_t expId = 0; expId < MaxExecution; ++expId) { + if (g_sessions.insert(expId).second) return expId; + } + return ErrorExecutionMax; } int APS5_VABI sceContentExportGetProgress(std::int32_t expId) { std::lock_guard lock(g_sessionMutex); RequireSession(__func__, expId); - throw std::runtime_error(std::string(__func__) + ": progress of a session with no running export is unknown"); + throw std::runtime_error(std::string(__func__) + ": progress requires the calling title SDK compatibility mode"); } int APS5_VABI sceContentExportCancel(std::int32_t expId) { std::lock_guard lock(g_sessionMutex); RequireSession(__func__, expId); - throw std::runtime_error(std::string(__func__) + ": cancelling a session with no running export is unknown"); + return ErrorInvalidState; } int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const ContentExportParam* param, const ContentExportCallbackParam* contentCallbackParam, const char* thumbnailType, const ContentExportCallbackParam* thumbnailCallbackParam, char* path, std::size_t pathBufferLength) { @@ -144,9 +146,9 @@ int APS5_VABI sceContentExportFromDataWithThumbnail(std::int32_t expId, const Co int APS5_VABI sceContentExportTerm(void) { std::lock_guard lock(g_sessionMutex); - if (!g_sessions.empty()) throw std::runtime_error(std::string(__func__) + ": terminating with live export sessions is unknown"); + if (!g_sessions.empty()) return ErrorBusy; bool expected = true; - if (!g_initialized.compare_exchange_strong(expected, false)) throw std::logic_error(std::string(__func__) + ": not initialized"); + if (!g_initialized.compare_exchange_strong(expected, false)) return ErrorNotInitialized; return 0; } diff --git a/core/libs/tests/GuestContentExport.cpp b/core/libs/tests/GuestContentExport.cpp index 21229622e9..4f72b3a7c7 100644 --- a/core/libs/tests/GuestContentExport.cpp +++ b/core/libs/tests/GuestContentExport.cpp @@ -54,8 +54,8 @@ void Export(std::int32_t expId) { int main() { ContentExportInitParam2 param{&allocator, &allocator, nullptr, 0x10000, 0, 0}; Require(Throws([] { sceContentExportInit2(nullptr); })); - Require(Throws([] { sceContentExportTerm(); })); - Require(Throws([] { sceContentExportStart(); })); + Require(sceContentExportTerm() == static_cast(0x809D3004u)); + Require(sceContentExportStart() == static_cast(0x809D3004u)); Require(Throws([] { sceContentExportFinish(1); })); ContentExportInitParam2 missing = param; missing.free_func = nullptr; @@ -66,20 +66,20 @@ int main() { Require(sceContentExportInit2(¶m) == 0); Require(Throws([&] { sceContentExportInit2(¶m); })); Require(sceContentExportTerm() == 0); - Require(Throws([] { sceContentExportTerm(); })); + Require(sceContentExportTerm() == static_cast(0x809D3004u)); Require(sceContentExportInit2(¶m) == 0); std::set sessions; for (int i = 0; i < 10; ++i) { int session = sceContentExportStart(); - Require(session > 0); + Require(session == i); Require(sessions.insert(session).second); } - Require(Throws([] { sceContentExportStart(); })); - Require(Throws([] { sceContentExportTerm(); })); + Require(sceContentExportStart() == static_cast(0x809D3018u)); + Require(sceContentExportTerm() == static_cast(0x809D3003u)); int first = *sessions.begin(); Require(Throws([&] { sceContentExportGetProgress(first); })); - Require(Throws([&] { sceContentExportCancel(first); })); + Require(sceContentExportCancel(first) == static_cast(0x809D3002u)); Require(Throws([&] { Export(first); })); Require(path[0] == 0); Require(sceContentExportFinish(first) == 0); @@ -91,11 +91,16 @@ int main() { Require(Throws([] { sceContentExportFinish(-1); })); sessions.erase(first); int reopened = sceContentExportStart(); - Require(reopened > 0 && reopened != first); + Require(reopened == first); Require(sessions.insert(reopened).second); + Require(sceContentExportStart() == static_cast(0x809D3018u)); + Require(sceContentExportFinish(7) == 0); + Require(sceContentExportFinish(3) == 0); + Require(sceContentExportStart() == 3); + Require(sceContentExportStart() == 7); for (int session : sessions) Require(sceContentExportFinish(session) == 0); Require(sceContentExportTerm() == 0); - Require(Throws([] { sceContentExportStart(); })); + Require(sceContentExportStart() == static_cast(0x809D3004u)); Require(Throws([&] { sceContentExportFinish(reopened); })); Require(Throws([] { sceContentExportAddToFavorites(0); })); Require(Throws([] { sceContentExportDataProvideFunction(); })); diff --git a/docs/dev/debt/libSceContentExport.md b/docs/dev/debt/libSceContentExport.md index d7d9e2f0d8..01f0d64324 100644 --- a/docs/dev/debt/libSceContentExport.md +++ b/docs/dev/debt/libSceContentExport.md @@ -2,7 +2,7 @@ ### Functional -- [Content export sessions](../../../core/libs/prx/libSceContentExport/Export.cpp) (libSceContentExport): `sceContentExportStart` hands out positive session ids, at most 10 live, which `sceContentExportFinish` closes. The signatures follow [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json) and the limit `MaxExecution` [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Content/ContentExport.cs); the parameter layouts follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/443f022450f6857256e780daa3d5a14029a748f7/src/core/libraries/content_export/content_export.h). The host has no media library, so `sceContentExportFromDataWithThumbnail` throws once the session is checked, before calling either data callback, and no export ever runs. `sceContentExportGetProgress` and `sceContentExportCancel` throw for a live session, as their result with no running export is unknown. An eleventh `sceContentExportStart` and `sceContentExportTerm` with live sessions throw: whether the console answers `EXECUTION_MAX` or `BUSY` there, or ends the sessions, is unverified. Session id values and error precedence are unverified +- [Content export sessions](../../../core/libs/prx/libSceContentExport/Export.cpp): the host has no media library, so `sceContentExportFromDataWithThumbnail` throws for a valid session without invoking data callbacks. Session allocation does not reproduce guest allocator calls, worker creation or their failure paths. `sceContentExportGetProgress` throws because an idle session returns either zero or an invalid-state error according to the caller's SDK compatibility mode, which is not available here. Initialization and session lookup still throw for some invalid inputs instead of reproducing the console's error precedence and legacy SDK behavior. Session IDs now use the lowest free slot in 0–9; exhaustion, termination with live sessions and cancellation before an export return the confirmed errors. ### Unknown function info From 813b6f4362f24c96536eddf884a0bac8fde21f65 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:15:35 +0300 Subject: [PATCH 302/523] fix(contentsearch): recover initialization and query validation errors --- core/libs/prx/libSceContentSearch/Export.cpp | 30 +++++++++++--------- core/libs/tests/GuestContentSearch.cpp | 30 ++++++++++++++++---- docs/dev/debt/libSceContentSearch.md | 2 +- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceContentSearch/Export.cpp b/core/libs/prx/libSceContentSearch/Export.cpp index 2c9b8a5777..ac200672de 100644 --- a/core/libs/prx/libSceContentSearch/Export.cpp +++ b/core/libs/prx/libSceContentSearch/Export.cpp @@ -10,6 +10,8 @@ namespace { std::atomic g_initialized{false}; constexpr int ErrorNotInitialized = static_cast(0x809D1001u); +constexpr int ErrorAlreadyInitialized = static_cast(0x809D1002u); +constexpr int ErrorInvalidArgument = static_cast(0x809D1003u); constexpr int ErrorLimitTooBig = static_cast(0x809D100Au); constexpr std::uint32_t MaxSearchLimit = 92; constexpr std::int64_t LibraryUpdateId = 0; @@ -63,42 +65,44 @@ void ValidateOrderBy(const char* funcName, const ContentSearchContentOrderByCond extern "C" { int APS5_VABI sceContentSearchInit(const ContentSearchInitParam* init_param) { - if (init_param == nullptr) APS5_INVALID_ARG_EX; + if (g_initialized.load()) return ErrorAlreadyInitialized; + if (init_param == nullptr || init_param->memory_size == 0 || init_param->memory_size % 16384 != 0) return ErrorInvalidArgument; bool expected = false; - if (!g_initialized.compare_exchange_strong(expected, true)) throw std::logic_error(std::string(__func__) + ": already initialized"); + if (!g_initialized.compare_exchange_strong(expected, true)) return ErrorAlreadyInitialized; return 0; } int APS5_VABI sceContentSearchCloseMetadata(std::int32_t metadataId) { if (!g_initialized.load()) return ErrorNotInitialized; - ThrowUnknownMetadataId(__func__, metadataId); + return ErrorInvalidArgument; } int APS5_VABI sceContentSearchGetMetadataFieldInfo(std::int32_t metadataId, const char* field, std::int32_t* metadataType, std::int32_t* size) { if (!g_initialized.load()) return ErrorNotInitialized; - if (field == nullptr || metadataType == nullptr || size == nullptr) APS5_INVALID_ARG_EX; + if (field == nullptr || metadataType == nullptr || size == nullptr) return ErrorInvalidArgument; ThrowUnknownMetadataId(__func__, metadataId); } int APS5_VABI sceContentSearchGetMetadataValue(std::int32_t metadataId, const char* field, void* value) { if (!g_initialized.load()) return ErrorNotInitialized; - if (field == nullptr || value == nullptr) APS5_INVALID_ARG_EX; + if (field == nullptr || value == nullptr) return ErrorInvalidArgument; ThrowUnknownMetadataId(__func__, metadataId); } int APS5_VABI sceContentSearchOpenMetadata(const char* filePath, std::int32_t* metadataId) { if (!g_initialized.load()) return ErrorNotInitialized; - if (filePath == nullptr || metadataId == nullptr) APS5_INVALID_ARG_EX; + if (filePath == nullptr || metadataId == nullptr) return ErrorInvalidArgument; throw std::logic_error(std::string(__func__) + ": " + filePath + " is not in the host content library, which is empty, and the console's error for it is unknown"); } int APS5_VABI sceContentSearchSearchContent(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, const ContentSearchContentOrderByCondition* orderByConditions, std::uint32_t orderByConditionsLength, std::uint32_t offset, std::uint32_t limit, std::int64_t* numOfContent, void* infos, std::int64_t* lastUpdateId) { (void)offset; if (!g_initialized.load()) return ErrorNotInitialized; - if (numOfContent == nullptr || lastUpdateId == nullptr || (infos == nullptr && limit != 0)) APS5_INVALID_ARG_EX; + if ((columnSet == nullptr) != (columnSetLength == 0) || (orderByConditions == nullptr) != (orderByConditionsLength == 0) || limit == 0) return ErrorInvalidArgument; + if (limit > MaxSearchLimit) return ErrorLimitTooBig; + if (numOfContent == nullptr || lastUpdateId == nullptr || infos == nullptr) return ErrorInvalidArgument; ValidateColumnSets(__func__, columnSet, columnSetLength); ValidateOrderBy(__func__, orderByConditions, orderByConditionsLength); - if (limit > MaxSearchLimit) return ErrorLimitTooBig; *numOfContent = 0; *lastUpdateId = LibraryUpdateId; return 0; @@ -106,20 +110,20 @@ int APS5_VABI sceContentSearchSearchContent(const ContentSearchContentColumnSet* int APS5_VABI sceContentSearchGetContentLastUpdateId(std::int64_t* lastUpdateId) { if (!g_initialized.load()) return ErrorNotInitialized; - if (lastUpdateId == nullptr) APS5_INVALID_ARG_EX; + if (lastUpdateId == nullptr) return ErrorInvalidArgument; *lastUpdateId = LibraryUpdateId; return 0; } int APS5_VABI sceContentSearchOpenMetadataByContentId(std::int64_t contentId, std::int32_t* metadataId) { if (!g_initialized.load()) return ErrorNotInitialized; - if (metadataId == nullptr) APS5_INVALID_ARG_EX; + if (metadataId == nullptr) return ErrorInvalidArgument; throw std::logic_error(std::string(__func__) + ": content id " + std::to_string(contentId) + " is not in the host content library, which is empty, and the console's error for it is unknown"); } int APS5_VABI sceContentSearchGetTotalContentSize(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, std::int64_t* size, std::int64_t* lastUpdateId) { if (!g_initialized.load()) return ErrorNotInitialized; - if (size == nullptr || lastUpdateId == nullptr) APS5_INVALID_ARG_EX; + if ((columnSet == nullptr) != (columnSetLength == 0) || size == nullptr || lastUpdateId == nullptr) return ErrorInvalidArgument; ValidateColumnSets(__func__, columnSet, columnSetLength); *size = 0; *lastUpdateId = LibraryUpdateId; @@ -128,7 +132,7 @@ int APS5_VABI sceContentSearchGetTotalContentSize(const ContentSearchContentColu int APS5_VABI sceContentSearchGetNumOfContent(const ContentSearchContentColumnSet* columnSet, std::uint32_t columnSetLength, std::int64_t* numOfContent, std::int64_t* lastUpdateId) { if (!g_initialized.load()) return ErrorNotInitialized; - if (numOfContent == nullptr || lastUpdateId == nullptr) APS5_INVALID_ARG_EX; + if ((columnSet == nullptr) != (columnSetLength == 0) || numOfContent == nullptr || lastUpdateId == nullptr) return ErrorInvalidArgument; ValidateColumnSets(__func__, columnSet, columnSetLength); *numOfContent = 0; *lastUpdateId = LibraryUpdateId; @@ -137,7 +141,7 @@ int APS5_VABI sceContentSearchGetNumOfContent(const ContentSearchContentColumnSe int APS5_VABI sceContentSearchTerm(void) { bool expected = true; - if (!g_initialized.compare_exchange_strong(expected, false)) throw std::logic_error(std::string(__func__) + ": not initialized"); + if (!g_initialized.compare_exchange_strong(expected, false)) return ErrorNotInitialized; return 0; } diff --git a/core/libs/tests/GuestContentSearch.cpp b/core/libs/tests/GuestContentSearch.cpp index 2447383112..e9facbccc1 100644 --- a/core/libs/tests/GuestContentSearch.cpp +++ b/core/libs/tests/GuestContentSearch.cpp @@ -32,6 +32,8 @@ bool Throws(TAction action) { } constexpr int ErrorNotInitialized = static_cast(0x809D1001u); +constexpr int ErrorInvalidArgument = static_cast(0x809D1003u); +constexpr int ErrorAlreadyInitialized = static_cast(0x809D1002u); constexpr int ErrorLimitTooBig = static_cast(0x809D100Au); struct MetadataValue { @@ -66,7 +68,16 @@ int main() { Require(sceContentSearchCloseMetadata(1) == ErrorNotInitialized); Require(count == -1 && size == -1 && updateId == -1 && metadataId == -1); + Require(sceContentSearchTerm() == ErrorNotInitialized); + Require(sceContentSearchInit(nullptr) == ErrorInvalidArgument); + for (std::size_t memorySize : {std::size_t{0}, std::size_t{1}, std::size_t{16383}, param.memory_size - 1}) { + ContentSearchInitParam invalidParam{memorySize}; + Require(sceContentSearchInit(&invalidParam) == ErrorInvalidArgument); + Require(sceContentSearchTerm() == ErrorNotInitialized); + } Require(sceContentSearchInit(¶m) == 0); + Require(sceContentSearchInit(nullptr) == ErrorAlreadyInitialized); + Require(sceContentSearchInit(¶m) == ErrorAlreadyInitialized); Require(sceContentSearchGetContentLastUpdateId(&updateId) == 0); Require(updateId == 0); @@ -89,11 +100,11 @@ int main() { Require(sceContentSearchSearchContent(&columnSet, 1, &orderBy, 1, 0, 93, &count, infos, &updateId) == ErrorLimitTooBig); Require(count == -1 && updateId == -1); - Require(Throws([&] { sceContentSearchGetContentLastUpdateId(nullptr); })); - Require(Throws([&] { sceContentSearchGetNumOfContent(&columnSet, 1, nullptr, &updateId); })); - Require(Throws([&] { sceContentSearchGetNumOfContent(&columnSet, 1, &count, nullptr); })); - Require(Throws([&] { sceContentSearchGetTotalContentSize(nullptr, 1, &size, &updateId); })); - Require(Throws([&] { sceContentSearchSearchContent(&columnSet, 1, &orderBy, 1, 0, 2, &count, nullptr, &updateId); })); + Require(sceContentSearchGetContentLastUpdateId(nullptr) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfContent(&columnSet, 1, nullptr, &updateId) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfContent(&columnSet, 1, &count, nullptr) == ErrorInvalidArgument); + Require(sceContentSearchGetTotalContentSize(nullptr, 1, &size, &updateId) == ErrorInvalidArgument); + Require(sceContentSearchSearchContent(&columnSet, 1, &orderBy, 1, 0, 2, &count, nullptr, &updateId) == ErrorInvalidArgument); ContentSearchContentColumn unknownColumn{8, 0, &photo, 2}; ContentSearchContentColumnSet unknownColumnSet{&unknownColumn, 1, 2}; Require(Throws([&] { sceContentSearchGetNumOfContent(&unknownColumnSet, 1, &count, &updateId); })); @@ -106,11 +117,18 @@ int main() { ContentSearchContentOrderByCondition unknownSort{3, 0}; Require(Throws([&] { sceContentSearchSearchContent(&columnSet, 1, &unknownSort, 1, 0, 2, &count, infos, &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); + Require(sceContentSearchSearchContent(&columnSet, 0, nullptr, 0, 0, 93, nullptr, nullptr, nullptr) == ErrorInvalidArgument); + Require(sceContentSearchSearchContent(nullptr, 0, &orderBy, 0, 0, 92, &count, infos, &updateId) == ErrorInvalidArgument); + Require(sceContentSearchGetNumOfContent(&columnSet, 0, &count, &updateId) == ErrorInvalidArgument); + Require(sceContentSearchGetTotalContentSize(&columnSet, 0, &size, &updateId) == ErrorInvalidArgument); + Require(count == -1 && updateId == -1); Require(Throws([&] { sceContentSearchOpenMetadata("/data/photo.jpg", &metadataId); })); Require(Throws([&] { sceContentSearchOpenMetadataByContentId(1, &metadataId); })); Require(metadataId == -1); - Require(Throws([] { sceContentSearchCloseMetadata(1); })); + Require(sceContentSearchCloseMetadata(1) == ErrorInvalidArgument); Require(Throws([&] { sceContentSearchGetMetadataFieldInfo(1, "title", &metadataType, &fieldSize); })); Require(Throws([&] { sceContentSearchGetMetadataValue(1, "title", &value); })); Require(metadataType == -1 && fieldSize == -1 && value.size == -1); diff --git a/docs/dev/debt/libSceContentSearch.md b/docs/dev/debt/libSceContentSearch.md index 56c59f62ad..8ef6373ca4 100644 --- a/docs/dev/debt/libSceContentSearch.md +++ b/docs/dev/debt/libSceContentSearch.md @@ -6,4 +6,4 @@ ### Functional -- [libSceContentSearch](../../../core/libs/prx/libSceContentSearch/Export.cpp) searches a content library that is empty on the host, as nothing is exported to it. The signatures follow [AlexAltea/orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json#L555-L569) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Content/ContentSearch.cs#L255-L316), which agree; the filter and sort layouts and enums, the limit of 92 rows, `0x809D100A` for a larger limit and `0x809D1001` before `sceContentSearchInit` are SharpProspero's. `sceContentSearchGetNumOfContent`, `sceContentSearchGetTotalContentSize` and `sceContentSearchSearchContent` report 0 rows and 0 bytes without writing a content row, and the update id they and `sceContentSearchGetContentLastUpdateId` report is always 0; no reference gives the console's update id for a library that never changed. Filters and sort conditions are only checked against SharpProspero's enums, and a null output, a null array with a non-zero length, an empty column set, a null filter value or an unknown enum value throws, as the console's errors for them are unknown; a null column set with length 0 is taken as no filter. `sceContentSearchOpenMetadata` and `sceContentSearchOpenMetadataByContentId` throw, as no reference gives the error for a path or content id that is not in the library, so no metadata id is ever handed out and `sceContentSearchCloseMetadata`, `sceContentSearchGetMetadataFieldInfo` and `sceContentSearchGetMetadataValue` throw. Every title is taken to hold the content-search permission (SharpProspero's `0x809D1013` is never returned). The order of the initialization, argument and limit checks is unverified +- [libSceContentSearch](../../../core/libs/prx/libSceContentSearch/Export.cpp) searches a content library that is empty on the host, as nothing is exported to it. The signatures follow [AlexAltea/orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json#L555-L569) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Content/ContentSearch.cs#L255-L316), which agree; the filter and sort layouts and enums, the limit of 92 rows, `0x809D100A` for a larger limit and `0x809D1001` before `sceContentSearchInit` are SharpProspero's. `sceContentSearchGetNumOfContent`, `sceContentSearchGetTotalContentSize` and `sceContentSearchSearchContent` report 0 rows and 0 bytes without writing a content row, and the update id they and `sceContentSearchGetContentLastUpdateId` report is always 0; no reference gives the console's update id for a library that never changed. Filters and sort conditions are only checked against SharpProspero's enums, and an empty nested column set, a null filter value or an unknown enum value throws, as their validation is not yet matched to the native query builder; a null column set with length 0 is taken as no filter. `sceContentSearchOpenMetadata` and `sceContentSearchOpenMetadataByContentId` throw, as no reference gives the error for a path or content id that is not in the library, so no metadata id is ever handed out; metadata-field and value queries with valid pointers still throw because field-dependent errors remain unresolved. Every title is taken to hold the content-search permission (SharpProspero's `0x809D1013` is never returned). Initialization checks and outer query pointer/count/limit precedence follow the supplied module. Guest heap allocation, platform-dependent minimum heap size and nested query validation remain unimplemented From 41e386a49541e8902f9d291ccea852c4bca33d5b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:19:33 +0300 Subject: [PATCH 303/523] fix(fontft): restore library arguments and validate format queries --- core/libs/prx/libSceFontFt/Export.cpp | 90 +++++++------------ .../prx/libSceFontFt/include/FontFtDriver.hpp | 3 + core/libs/prx/libSceFontFt/src/Driver.cpp | 29 ++++++ core/libs/tests/GuestFont.cpp | 44 ++++++--- docs/dev/debt/libSceFontFt.md | 6 +- 5 files changed, 101 insertions(+), 71 deletions(-) diff --git a/core/libs/prx/libSceFontFt/Export.cpp b/core/libs/prx/libSceFontFt/Export.cpp index 0dcd8bcf90..b0a213d62f 100644 --- a/core/libs/prx/libSceFontFt/Export.cpp +++ b/core/libs/prx/libSceFontFt/Export.cpp @@ -1,40 +1,14 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include - -#include -#include FT_FREETYPE_H -#include FT_MODULE_H #include "prx/libSceFontFt/include/FontFtDriver.hpp" #include "prx/libc/include/General.hpp" #pragma GCC visibility push(default) -namespace { - -FT_Library ProbeLibrary() { - static const FT_Library library = [] { - FT_Library created = nullptr; - if (FT_Init_FreeType(&created) != 0) return static_cast(nullptr); - return created; - }(); - return library; -} - -int SupportFormat(const char* function, std::initializer_list modules) { - const FT_Library library = ProbeLibrary(); - if (library == nullptr) throw std::runtime_error(std::string(function) + ": FreeType failed to initialize"); - for (const char* module : modules) { - if (FT_Get_Module(library, module) == nullptr) throw std::runtime_error(std::string(function) + ": FreeType module " + module + " is not built in"); - } - return 0; -} - -} +struct FontFtAliases; +struct FontFtGlyphSelection; extern "C" { @@ -46,86 +20,86 @@ const Font::RendererSelection* APS5_VABI sceFontSelectRendererFt(int value) { return value == 0 ? FontFt::RendererTable() : nullptr; } -int APS5_VABI sceFontFtInitAliases() { +int APS5_VABI sceFontFtInitAliases(FontFtAliases** aliases) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontFtSetAliasFont() { +int APS5_VABI sceFontFtSetAliasFont(const char* alias, std::uint32_t fontId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontFtSetAliasPath() { +int APS5_VABI sceFontFtSetAliasPath(const char* alias, const char* path) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontFtSupportBdf() { - return SupportFormat(__func__, {"bdf"}); +int APS5_VABI sceFontFtSupportBdf(FontLibrary library) { + return FontFt::SupportFormats(library, {"bdf"}); } -int APS5_VABI sceFontFtSupportCid() { - return SupportFormat(__func__, {"t1cid"}); +int APS5_VABI sceFontFtSupportCid(FontLibrary library) { + return FontFt::SupportFormats(library, {"t1cid"}); } -int APS5_VABI sceFontFtSupportFontFormats() { +int APS5_VABI sceFontFtSupportFontFormats(FontLibrary library, std::uint32_t formats) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontFtSupportOpenType() { - return SupportFormat(__func__, {"cff", "truetype"}); +int APS5_VABI sceFontFtSupportOpenType(FontLibrary library) { + return FontFt::SupportFormats(library, {"cff", "truetype"}); } -int APS5_VABI sceFontFtSupportOpenTypeOtf() { - return SupportFormat(__func__, {"cff"}); +int APS5_VABI sceFontFtSupportOpenTypeOtf(FontLibrary library) { + return FontFt::SupportFormats(library, {"cff"}); } -int APS5_VABI sceFontFtSupportOpenTypeTtf() { - return SupportFormat(__func__, {"truetype"}); +int APS5_VABI sceFontFtSupportOpenTypeTtf(FontLibrary library) { + return FontFt::SupportFormats(library, {"truetype"}); } -int APS5_VABI sceFontFtSupportPcf() { - return SupportFormat(__func__, {"pcf"}); +int APS5_VABI sceFontFtSupportPcf(FontLibrary library) { + return FontFt::SupportFormats(library, {"pcf"}); } -int APS5_VABI sceFontFtSupportPfr() { - return SupportFormat(__func__, {"pfr"}); +int APS5_VABI sceFontFtSupportPfr(FontLibrary library) { + return FontFt::SupportFormats(library, {"pfr"}); } -int APS5_VABI sceFontFtSupportSystemFonts() { +int APS5_VABI sceFontFtSupportSystemFonts(FontLibrary library) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontFtSupportTrueType() { - return SupportFormat(__func__, {"truetype"}); +int APS5_VABI sceFontFtSupportTrueType(FontLibrary library) { + return FontFt::SupportFormats(library, {"truetype"}); } -int APS5_VABI sceFontFtSupportTrueTypeGx() { +int APS5_VABI sceFontFtSupportTrueTypeGx(FontLibrary library) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontFtSupportType1() { - return SupportFormat(__func__, {"type1"}); +int APS5_VABI sceFontFtSupportType1(FontLibrary library) { + return FontFt::SupportFormats(library, {"type1"}); } -int APS5_VABI sceFontFtSupportType42() { - return SupportFormat(__func__, {"type42"}); +int APS5_VABI sceFontFtSupportType42(FontLibrary library) { + return FontFt::SupportFormats(library, {"type42"}); } -int APS5_VABI sceFontFtSupportWinFonts() { - return SupportFormat(__func__, {"winfonts"}); +int APS5_VABI sceFontFtSupportWinFonts(FontLibrary library) { + return FontFt::SupportFormats(library, {"winfonts"}); } -int APS5_VABI sceFontFtTermAliases() { +int APS5_VABI sceFontFtTermAliases(FontFtAliases** aliases) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontSelectGlyphsFt() { +const FontFtGlyphSelection* APS5_VABI sceFontSelectGlyphsFt(std::int32_t selector) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp b/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp index 87e7e92931..35ba74ff66 100644 --- a/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp +++ b/core/libs/prx/libSceFontFt/include/FontFtDriver.hpp @@ -4,12 +4,15 @@ #ifndef CORE_LIBS_PRX_LIBSCEFONTFT_INCLUDE_FONTFTDRIVER_HPP #define CORE_LIBS_PRX_LIBSCEFONTFT_INCLUDE_FONTFTDRIVER_HPP +#include + #include "prx/libSceFont/include/FontDriver.hpp" namespace FontFt { const Font::SysDriver* DriverTable(); const Font::RendererSelection* RendererTable(); +int SupportFormats(FontLibrary library, std::initializer_list modules); } diff --git a/core/libs/prx/libSceFontFt/src/Driver.cpp b/core/libs/prx/libSceFontFt/src/Driver.cpp index 7f49954e0a..152a72f8d7 100644 --- a/core/libs/prx/libSceFontFt/src/Driver.cpp +++ b/core/libs/prx/libSceFontFt/src/Driver.cpp @@ -2,6 +2,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include +#include +#include +#include +#include #include #include #include @@ -768,3 +773,27 @@ const Font::SysDriver* FontFt::DriverTable() { const Font::RendererSelection* FontFt::RendererTable() { return &rendererTable; } + +int FontFt::SupportFormats(FontLibrary library, std::initializer_list modules) { + auto* native = static_cast(library); + if (!native) return SCE_FONT_ERROR_INVALID_LIBRARY; + auto lockWord = std::atomic_ref(native->lock_word); + std::uint32_t previous = lockWord.load(std::memory_order_acquire); + for (;;) { + if (native->magic != Font::LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; + if ((previous & Font::LOCK_BIT) == 0 && lockWord.compare_exchange_weak(previous, previous | Font::LOCK_BIT, std::memory_order_acq_rel)) break; + std::this_thread::sleep_for(std::chrono::microseconds(30)); + previous = lockWord.load(std::memory_order_acquire); + } + const auto unlock = [previous](Font::FontLibNative* locked) { + std::atomic_ref(locked->lock_word).store(previous, std::memory_order_release); + }; + const std::unique_ptr guard(native, unlock); + if (native->sys_driver != &driverTable) throw std::runtime_error("FontFt: unsupported library driver"); + const auto* context = static_cast(native->fontset_registry); + if (!context || !context->library) return SCE_FONT_ERROR_INVALID_LIBRARY; + for (const char* module : modules) { + if (!FT_Get_Module(context->library, module)) return SCE_FONT_ERROR_NO_SUPPORT_FORMAT; + } + return SCE_FONT_OK; +} diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index 80184223b8..8db5efd303 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -61,17 +61,17 @@ const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting*); FontTextCharacter* APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting*, const FontWritingLetterStep**); const void* APS5_VABI sceFontSelectLibraryFt(int); const void* APS5_VABI sceFontSelectRendererFt(int); -int APS5_VABI sceFontFtSupportBdf(); -int APS5_VABI sceFontFtSupportCid(); -int APS5_VABI sceFontFtSupportOpenType(); -int APS5_VABI sceFontFtSupportOpenTypeOtf(); -int APS5_VABI sceFontFtSupportOpenTypeTtf(); -int APS5_VABI sceFontFtSupportPcf(); -int APS5_VABI sceFontFtSupportPfr(); -int APS5_VABI sceFontFtSupportTrueType(); -int APS5_VABI sceFontFtSupportType1(); -int APS5_VABI sceFontFtSupportType42(); -int APS5_VABI sceFontFtSupportWinFonts(); +int APS5_VABI sceFontFtSupportBdf(FontLibrary library); +int APS5_VABI sceFontFtSupportCid(FontLibrary library); +int APS5_VABI sceFontFtSupportOpenType(FontLibrary library); +int APS5_VABI sceFontFtSupportOpenTypeOtf(FontLibrary library); +int APS5_VABI sceFontFtSupportOpenTypeTtf(FontLibrary library); +int APS5_VABI sceFontFtSupportPcf(FontLibrary library); +int APS5_VABI sceFontFtSupportPfr(FontLibrary library); +int APS5_VABI sceFontFtSupportTrueType(FontLibrary library); +int APS5_VABI sceFontFtSupportType1(FontLibrary library); +int APS5_VABI sceFontFtSupportType42(FontLibrary library); +int APS5_VABI sceFontFtSupportWinFonts(FontLibrary library); int APS5_VABI sceFontCharacterGetSyllableStringState(const FontTextCharacter*, int*); FontTextCodes* APS5_VABI sceFontCharactersRefersTextCodes(const FontTextCharacter*, const FontTextCharacter*, FontTextCodes*); FontTextCodes* APS5_VABI sceFontTextCodesStepNext(FontTextCodes*); @@ -209,11 +209,31 @@ int main() { Require(sceFontMemoryInit(&memory, nullptr, 0, &iface, nullptr, nullptr, nullptr) == SCE_FONT_OK); Require(sceFontSelectLibraryFt(0) != nullptr && sceFontSelectLibraryFt(1) == nullptr); Require(sceFontSelectRendererFt(0) != nullptr && sceFontSelectRendererFt(1) == nullptr); - Require(sceFontFtSupportBdf() == 0 && sceFontFtSupportCid() == 0 && sceFontFtSupportOpenType() == 0 && sceFontFtSupportOpenTypeOtf() == 0 && sceFontFtSupportOpenTypeTtf() == 0 && sceFontFtSupportPcf() == 0 && sceFontFtSupportPfr() == 0 && sceFontFtSupportTrueType() == 0 && sceFontFtSupportType1() == 0 && sceFontFtSupportType42() == 0 && sceFontFtSupportWinFonts() == 0); FontLibrary library = nullptr; Require(sceFontCreateLibrary(&memory, nullptr, &library) == SCE_FONT_ERROR_INVALID_PARAMETER && library == nullptr); Require(sceFontCreateLibrary(&memory, sceFontSelectLibraryFt(0), &library) == SCE_FONT_OK && library != nullptr); + Require(sceFontFtSupportBdf(library) == 0 && sceFontFtSupportCid(library) == 0 && sceFontFtSupportOpenType(library) == 0 && sceFontFtSupportOpenTypeOtf(library) == 0 && sceFontFtSupportOpenTypeTtf(library) == 0 && sceFontFtSupportPcf(library) == 0 && sceFontFtSupportPfr(library) == 0 && sceFontFtSupportTrueType(library) == 0 && sceFontFtSupportType1(library) == 0 && sceFontFtSupportType42(library) == 0 && sceFontFtSupportWinFonts(library) == 0); + const auto* nativeLibrary = static_cast(library); + const auto savedLock = nativeLibrary->lock_word; + for (auto support : {sceFontFtSupportBdf, sceFontFtSupportCid, sceFontFtSupportOpenType, sceFontFtSupportOpenTypeOtf, sceFontFtSupportOpenTypeTtf, sceFontFtSupportPcf, sceFontFtSupportPfr, sceFontFtSupportTrueType, sceFontFtSupportType1, sceFontFtSupportType42, sceFontFtSupportWinFonts}) { + Font::FontLibNative invalidLibrary{}; + Require(support(nullptr) == SCE_FONT_ERROR_INVALID_LIBRARY); + Require(support(&invalidLibrary) == SCE_FONT_ERROR_INVALID_LIBRARY); + Require(support(library) == SCE_FONT_OK); + Require(nativeLibrary->lock_word == savedLock); + } + + Font::FontLibNative foreignLibrary{}; + foreignLibrary.magic = Font::LIBRARY_MAGIC; + foreignLibrary.lock_word = 5; + bool foreignDriverRejected = false; + try { + static_cast(sceFontFtSupportTrueType(&foreignLibrary)); + } catch (const std::runtime_error&) { + foreignDriverRejected = true; + } + Require(foreignDriverRejected && foreignLibrary.lock_word == 5); std::uint32_t subPixelCount = 1; Require(sceFontGetPixelResolution(library, &subPixelCount) == SCE_FONT_OK && subPixelCount == 64); diff --git a/docs/dev/debt/libSceFontFt.md b/docs/dev/debt/libSceFontFt.md index cd35391b21..3aa05e385d 100644 --- a/docs/dev/debt/libSceFontFt.md +++ b/docs/dev/debt/libSceFontFt.md @@ -1,5 +1,9 @@ # libSceFontFt technical debt +### Functional + +- [Font format support](../../../core/libs/prx/libSceFontFt/Export.cpp): format queries now take the font library, validate its magic and lock it while checking its actual FreeType modules. The host registers its default modules at creation rather than enabling formats individually, so format-selection state and guest allocator failures during format registration are not reproduced. Unsupported foreign drivers throw. + ### Unknown function info -- [sceFontFtSupportBdf, sceFontFtSupportCid, sceFontFtSupportOpenType, sceFontFtSupportOpenTypeOtf, sceFontFtSupportOpenTypeTtf, sceFontFtSupportPcf, sceFontFtSupportPfr, sceFontFtSupportTrueType, sceFontFtSupportType1, sceFontFtSupportType42 and sceFontFtSupportWinFonts](../../../core/libs/prx/libSceFontFt/Export.cpp) (libSceFontFt) are declared without parameters, signatures unknown; [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/7da7a8e1f98eca9d8f01f84d20681f8faa0bd8fd/src/core/libraries/font/fontft.cpp), another emulator, declares them the same way and only logs. They are assumed to enable one font format in the FontFt library, which here always registers FreeType's default modules, so each call returns 0 when the host FreeType has the module (`bdf`, `t1cid`, `truetype`, `cff`, `pcf`, `pfr`, `type1`, `type42`, `winfonts`; OpenType needs `cff` and `truetype`) and throws when it does not. The mapping of Sony's format names to FreeType modules, the return value and the error codes of the console are unverified, and no title is known to call them. +- [Alias and glyph selection exports](../../../core/libs/prx/libSceFontFt/Export.cpp): alias initialization/termination take a handle pointer; alias registration takes a string and font ID or path. Glyph selection takes a 32-bit selector and returns a table pointer whose layout remains unresolved. Their protected implementations are unchanged. From 30736adb89ea4c974cbbb863b9c2cf9c6f2f2ab1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:21:48 +0300 Subject: [PATCH 304/523] fix(font): restore graphics lifecycle and metric signatures --- .../libs/prx/libSceFont/src/Unimplemented.cpp | 29 +++++++++++++------ docs/dev/debt/libSceFont.md | 2 ++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index bc79aed66e..3826e0dc48 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -2,6 +2,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "prx/libc/include/General.hpp" +#include "prx/libSceFont/include/FontTypes.hpp" + +struct FontGraphicsDevice; +struct FontGraphicsDeviceParam; +struct FontGraphicsService; +struct FontGraphicsServiceParam; +struct FontWords; +struct FontOutlineProfile; +struct FontMetrics; +struct FontStyleInformation; +struct FontGlyphExpandBufferState; #pragma GCC visibility push(default) @@ -12,12 +23,12 @@ int APS5_VABI sceFontControl() { return 0; } -int APS5_VABI sceFontCreateGraphicsDevice() { +int APS5_VABI sceFontCreateGraphicsDevice(const FontMemory* memory, const FontGraphicsDeviceParam* param, FontGraphicsDevice** device) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontCreateGraphicsService() { +int APS5_VABI sceFontCreateGraphicsService(const FontMemory* memory, const FontGraphicsServiceParam* param, FontGraphicsService** service) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -32,38 +43,38 @@ int APS5_VABI sceFontCreateWords() { return 0; } -int APS5_VABI sceFontDestroyGraphicsDevice() { +int APS5_VABI sceFontDestroyGraphicsDevice(FontGraphicsDevice** device) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontDestroyGraphicsService() { +int APS5_VABI sceFontDestroyGraphicsService(FontGraphicsService** service) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontDestroyWords() { +int APS5_VABI sceFontDestroyWords(FontWords** words) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGetFontGlyphsOutlineProfile() { +int APS5_VABI sceFontGetFontGlyphsOutlineProfile(FontHandle font, FontOutlineProfile* profile) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGetFontMetrics() { +int APS5_VABI sceFontGetFontMetrics(FontHandle font, std::uint32_t mode, FontMetrics* metrics) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGetFontStyleInformation() { +int APS5_VABI sceFontGetFontStyleInformation(FontHandle font, FontStyleInformation* information) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGetGlyphExpandBufferState() { +int APS5_VABI sceFontGetGlyphExpandBufferState(FontHandle font, FontGlyphExpandBufferState* state) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 5c3d5ad062..16c1c82488 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -22,3 +22,5 @@ - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans Arabic, Noto Sans CJK with its Korean face for Hangul) is loaded instead; its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [sceFontGetCharGlyphMetrics, sceFontGetRenderCharGlyphMetrics](../../../core/libs/prx/libSceFont/src/Layout.cpp) (libSceFont) report the vertical metrics the way the [FreeType driver](../../../core/libs/prx/libSceFontFt/src/Driver.cpp) reports them for a rendered glyph, not confirmed on a title: FreeType's vertical advance, and for a face without vertical metrics a bearing of half the horizontal advance left of the horizontal bearing and of half the advance left over by the glyph height. - [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics`, `sceFontWritingLineGetOrderingSpace` and `sceFontWritingLineClear` follow the PS4 11.00 `libSceFont.sprx` (firmware module, read from its disassembly and compared call for call on random sequences): runs are linked in visual order by embedding level, the space at each spaced join is summed for the line, steps walk the visual order from the left (form `0x12`) or from the right (form `0x11`) and the render metrics grow as steps are consumed. The PS5 library was not measured. Not from that module: form `0x10` is accepted and treated as left to right and a detail with id `0x0FD5` is accepted (the PS4 library rejects both); the memory argument of `sceFontCreateWritingLine` and the rest of the detail are not read (the PS4 library validates its library object there); an unknown writing form, another detail id, a null line pointer on creation, unknown and null handles (except in `sceFontWritingLineRefersRenderStep`, which returns null for a null line) and a null output of `sceFontWritingLineGetRenderMetrics` throw instead of returning the PS4 error codes, which are not confirmed for the PS5; `sceFontDestroyWritingLine` was not compared. Lines are allocated on the host, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. + +- [Font graphics and metric signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Metric, outline-profile, style-information and expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged. From e2f8adca1b42bde7a98a44eee80c0fc5730f3b3b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:27:05 +0300 Subject: [PATCH 305/523] fix(font): restore region and graphics transform signatures --- .../libs/prx/libSceFont/src/Unimplemented.cpp | 65 +++++++++++++------ docs/dev/debt/libSceFont.md | 2 + 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index 3826e0dc48..6c1116ceef 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -13,6 +13,33 @@ struct FontOutlineProfile; struct FontMetrics; struct FontStyleInformation; struct FontGlyphExpandBufferState; +struct FontGraphicsResource; +struct FontGraphicsFillMethod; +struct FontGraphicsFillPlot; +struct FontGraphicsFillRates; +struct FontGraphicsFill; + +struct FontGraphicsRegion { + float x; + float y; + float width; + float height; + std::uint32_t flags; + std::uint32_t tag; + float outerRadius; + float innerRadius; +}; + +static_assert(sizeof(FontGraphicsRegion) == 32); +static_assert(offsetof(FontGraphicsRegion, x) == 0); +static_assert(offsetof(FontGraphicsRegion, y) == 4); +static_assert(offsetof(FontGraphicsRegion, width) == 8); +static_assert(offsetof(FontGraphicsRegion, height) == 12); +static_assert(offsetof(FontGraphicsRegion, flags) == 16); +static_assert(offsetof(FontGraphicsRegion, tag) == 20); +static_assert(offsetof(FontGraphicsRegion, outerRadius) == 24); +static_assert(offsetof(FontGraphicsRegion, innerRadius) == 28); + #pragma GCC visibility push(default) @@ -119,42 +146,42 @@ int APS5_VABI sceFontGraphicsExchangeResource() { return 0; } -int APS5_VABI sceFontGraphicsFillMethodInit() { +int APS5_VABI sceFontGraphicsFillMethodInit(FontGraphicsFillMethod* method, std::int32_t mode, std::int32_t type) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillPlotInit() { +int APS5_VABI sceFontGraphicsFillPlotInit(FontGraphicsFillPlot* plot, const FontGraphicsRegion* region) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillPlotSetLayout() { +int APS5_VABI sceFontGraphicsFillPlotSetLayout(FontGraphicsFillPlot* plot, std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillPlotSetMapping() { +int APS5_VABI sceFontGraphicsFillPlotSetMapping(FontGraphicsFillPlot* plot, float first, float second, float third, float fourth) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillRatesInit() { +int APS5_VABI sceFontGraphicsFillRatesInit(FontGraphicsFillRates* rates, const FontGraphicsFill* fill, std::uint32_t effect) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillRatesSetFillEffect() { +int APS5_VABI sceFontGraphicsFillRatesSetFillEffect(FontGraphicsFillRates* rates, const FontGraphicsFill* fill, std::uint32_t effect) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillRatesSetLayout() { +int APS5_VABI sceFontGraphicsFillRatesSetLayout(FontGraphicsFillRates* rates, std::int32_t first, std::int32_t second) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsFillRatesSetMapping() { +int APS5_VABI sceFontGraphicsFillRatesSetMapping(FontGraphicsFillRates* rates, float first, float second, float third, float fourth) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -164,17 +191,17 @@ int APS5_VABI sceFontGraphicsGetDeviceUsage() { return 0; } -int APS5_VABI sceFontGraphicsRegionInit() { +int APS5_VABI sceFontGraphicsRegionInit(FontGraphicsRegion* region, float x, float y, float width, float height) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsRegionInitCircular() { +int APS5_VABI sceFontGraphicsRegionInitCircular(FontGraphicsRegion* region, float centerX, float centerY, float outerRadius, float innerRadius) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsRegionInitRoundish() { +int APS5_VABI sceFontGraphicsRegionInitRoundish(FontGraphicsRegion* region, float x, float y, float width, float height, float outerRadius, float innerRadius, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -199,7 +226,7 @@ int APS5_VABI sceFontGraphicsSetupClipping() { return 0; } -int APS5_VABI sceFontGraphicsSetupColorRates() { +int APS5_VABI sceFontGraphicsSetupColorRates(FontGraphicsResource* resource, float red, float green, float blue, float alpha) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -229,7 +256,7 @@ int APS5_VABI sceFontGraphicsSetupHandleDefault() { return 0; } -int APS5_VABI sceFontGraphicsSetupLocation() { +int APS5_VABI sceFontGraphicsSetupLocation(FontGraphicsResource* resource, float x, float y) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -239,12 +266,12 @@ int APS5_VABI sceFontGraphicsSetupPositioning() { return 0; } -int APS5_VABI sceFontGraphicsSetupRotation() { +int APS5_VABI sceFontGraphicsSetupRotation(FontGraphicsResource* resource, float angle, float pivotX, float pivotY) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupScaling() { +int APS5_VABI sceFontGraphicsSetupScaling(FontGraphicsResource* resource, float scaleX, float scaleY, float pivotX, float pivotY) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -289,7 +316,7 @@ int APS5_VABI sceFontGraphicsUpdateClipping() { return 0; } -int APS5_VABI sceFontGraphicsUpdateColorRates() { +int APS5_VABI sceFontGraphicsUpdateColorRates(FontGraphicsResource* resource, float red, float green, float blue, float alpha) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -314,7 +341,7 @@ int APS5_VABI sceFontGraphicsUpdateGlyphFillPlot() { return 0; } -int APS5_VABI sceFontGraphicsUpdateLocation() { +int APS5_VABI sceFontGraphicsUpdateLocation(FontGraphicsResource* resource, float x, float y) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -324,12 +351,12 @@ int APS5_VABI sceFontGraphicsUpdatePositioning() { return 0; } -int APS5_VABI sceFontGraphicsUpdateRotation() { +int APS5_VABI sceFontGraphicsUpdateRotation(FontGraphicsResource* resource, float angle, float pivotX, float pivotY) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsUpdateScaling() { +int APS5_VABI sceFontGraphicsUpdateScaling(FontGraphicsResource* resource, float scaleX, float scaleY, float pivotX, float pivotY) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 16c1c82488..02030240f1 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -24,3 +24,5 @@ - [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics`, `sceFontWritingLineGetOrderingSpace` and `sceFontWritingLineClear` follow the PS4 11.00 `libSceFont.sprx` (firmware module, read from its disassembly and compared call for call on random sequences): runs are linked in visual order by embedding level, the space at each spaced join is summed for the line, steps walk the visual order from the left (form `0x12`) or from the right (form `0x11`) and the render metrics grow as steps are consumed. The PS5 library was not measured. Not from that module: form `0x10` is accepted and treated as left to right and a detail with id `0x0FD5` is accepted (the PS4 library rejects both); the memory argument of `sceFontCreateWritingLine` and the rest of the detail are not read (the PS4 library validates its library object there); an unknown writing form, another detail id, a null line pointer on creation, unknown and null handles (except in `sceFontWritingLineRefersRenderStep`, which returns null for a null line) and a null output of `sceFontWritingLineGetRenderMetrics` throw instead of returning the PS4 error codes, which are not confirmed for the PS5; `sceFontDestroyWritingLine` was not compared. Lines are allocated on the host, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [Font graphics and metric signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Metric, outline-profile, style-information and expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged. + +- [Font graphics fill and transform signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): region layout and scalar float arguments are confirmed from assembly, including the extra 32-bit corner flags omitted by the roundish-region decompilation. Fill-method, plot and rates layouts, layout selector meanings and mapping parameter meanings remain unresolved. Protected bodies are unchanged. From 60281539f9a9ee026ff09c23e9e2aa57ff61e80c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:28:07 +0300 Subject: [PATCH 306/523] fix(font): restore drawing frame and fill argument types --- .../libs/prx/libSceFont/src/Unimplemented.cpp | 44 ++++++++++--------- docs/dev/debt/libSceFont.md | 2 + 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index 6c1116ceef..e4bb1c699f 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -14,6 +14,8 @@ struct FontMetrics; struct FontStyleInformation; struct FontGlyphExpandBufferState; struct FontGraphicsResource; +struct FontGraphicsFrame; +struct FontGraphicsDeviceUsage; struct FontGraphicsFillMethod; struct FontGraphicsFillPlot; struct FontGraphicsFillRates; @@ -121,22 +123,22 @@ int APS5_VABI sceFontGlyphRenderImageVertical() { return 0; } -int APS5_VABI sceFontGraphicsBeginFrame() { +int APS5_VABI sceFontGraphicsBeginFrame(FontGraphicsFrame* frame, std::uint64_t flags, FontGraphicsResource** resource) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsDrawingCancel() { +int APS5_VABI sceFontGraphicsDrawingCancel(FontGraphicsFrame** frame) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsDrawingFinish() { +int APS5_VABI sceFontGraphicsDrawingFinish(FontGraphicsFrame** frame, FontGraphicsDevice* device) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsEndFrame() { +int APS5_VABI sceFontGraphicsEndFrame(FontGraphicsFrame* frame) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -186,7 +188,7 @@ int APS5_VABI sceFontGraphicsFillRatesSetMapping(FontGraphicsFillRates* rates, f return 0; } -int APS5_VABI sceFontGraphicsGetDeviceUsage() { +int APS5_VABI sceFontGraphicsGetDeviceUsage(FontGraphicsDevice* device, FontGraphicsDeviceUsage* usage) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -216,12 +218,12 @@ int APS5_VABI sceFontGraphicsRenderResource() { return 0; } -int APS5_VABI sceFontGraphicsSetFramePolicy() { +int APS5_VABI sceFontGraphicsSetFramePolicy(FontGraphicsResource* resource, std::int32_t policy, std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupClipping() { +int APS5_VABI sceFontGraphicsSetupClipping(FontGraphicsResource* resource, const FontGraphicsRegion* region) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -231,27 +233,27 @@ int APS5_VABI sceFontGraphicsSetupColorRates(FontGraphicsResource* resource, flo return 0; } -int APS5_VABI sceFontGraphicsSetupFillMethod() { +int APS5_VABI sceFontGraphicsSetupFillMethod(FontGraphicsResource* resource, const FontGraphicsFillMethod* method) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupFillRates() { +int APS5_VABI sceFontGraphicsSetupFillRates(FontGraphicsResource* resource, const FontGraphicsFillRates* rates) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupGlyphFill() { +int APS5_VABI sceFontGraphicsSetupGlyphFill(FontGraphicsResource* resource, const FontGraphicsFill* fill) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupGlyphFillPlot() { +int APS5_VABI sceFontGraphicsSetupGlyphFillPlot(FontGraphicsResource* resource, const FontGraphicsFillPlot* plot) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupHandleDefault() { +int APS5_VABI sceFontGraphicsSetupHandleDefault(FontGraphicsResource* resource, std::uint64_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -276,12 +278,12 @@ int APS5_VABI sceFontGraphicsSetupScaling(FontGraphicsResource* resource, float return 0; } -int APS5_VABI sceFontGraphicsSetupShapeFill() { +int APS5_VABI sceFontGraphicsSetupShapeFill(FontGraphicsResource* resource, const FontGraphicsFill* fill) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsSetupShapeFillPlot() { +int APS5_VABI sceFontGraphicsSetupShapeFillPlot(FontGraphicsResource* resource, const FontGraphicsFillPlot* plot) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -311,7 +313,7 @@ int APS5_VABI sceFontGraphicsStructureSurfaceTexture() { return 0; } -int APS5_VABI sceFontGraphicsUpdateClipping() { +int APS5_VABI sceFontGraphicsUpdateClipping(FontGraphicsResource* resource, const FontGraphicsRegion* region) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -321,22 +323,22 @@ int APS5_VABI sceFontGraphicsUpdateColorRates(FontGraphicsResource* resource, fl return 0; } -int APS5_VABI sceFontGraphicsUpdateFillMethod() { +int APS5_VABI sceFontGraphicsUpdateFillMethod(FontGraphicsResource* resource, const FontGraphicsFillMethod* method) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsUpdateFillRates() { +int APS5_VABI sceFontGraphicsUpdateFillRates(FontGraphicsResource* resource, const FontGraphicsFillRates* rates) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsUpdateGlyphFill() { +int APS5_VABI sceFontGraphicsUpdateGlyphFill(FontGraphicsResource* resource, const FontGraphicsFill* fill) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsUpdateGlyphFillPlot() { +int APS5_VABI sceFontGraphicsUpdateGlyphFillPlot(FontGraphicsResource* resource, const FontGraphicsFillPlot* plot) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -361,12 +363,12 @@ int APS5_VABI sceFontGraphicsUpdateScaling(FontGraphicsResource* resource, float return 0; } -int APS5_VABI sceFontGraphicsUpdateShapeFill() { +int APS5_VABI sceFontGraphicsUpdateShapeFill(FontGraphicsResource* resource, const FontGraphicsFill* fill) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsUpdateShapeFillPlot() { +int APS5_VABI sceFontGraphicsUpdateShapeFillPlot(FontGraphicsResource* resource, const FontGraphicsFillPlot* plot) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 02030240f1..2ba6558989 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -26,3 +26,5 @@ - [Font graphics and metric signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Metric, outline-profile, style-information and expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged. - [Font graphics fill and transform signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): region layout and scalar float arguments are confirmed from assembly, including the extra 32-bit corner flags omitted by the roundish-region decompilation. Fill-method, plot and rates layouts, layout selector meanings and mapping parameter meanings remain unresolved. Protected bodies are unchanged. + +Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Device usage, frame and resource layouts remain opaque; their runtime implementation is still unavailable. From 340328ed04811f5cfc0a186e55934e7c9077bc4e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:29:24 +0300 Subject: [PATCH 307/523] fix(font): recover glyph rendering and resource signatures --- .../libs/prx/libSceFont/src/Unimplemented.cpp | 21 ++++++++++--------- docs/dev/debt/libSceFont.md | 2 ++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index e4bb1c699f..bc6c9d98d0 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -15,6 +15,7 @@ struct FontStyleInformation; struct FontGlyphExpandBufferState; struct FontGraphicsResource; struct FontGraphicsFrame; +struct FontGraphicsDesignResource; struct FontGraphicsDeviceUsage; struct FontGraphicsFillMethod; struct FontGraphicsFillPlot; @@ -47,7 +48,7 @@ static_assert(offsetof(FontGraphicsRegion, innerRadius) == 28); extern "C" { -int APS5_VABI sceFontControl() { +int APS5_VABI sceFontControl(std::int32_t command, void* object, std::uintptr_t argument) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -108,17 +109,17 @@ int APS5_VABI sceFontGetGlyphExpandBufferState(FontHandle font, FontGlyphExpandB return 0; } -int APS5_VABI sceFontGlyphRenderImage() { +int APS5_VABI sceFontGlyphRenderImage(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGlyphRenderImageHorizontal() { +int APS5_VABI sceFontGlyphRenderImageHorizontal(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGlyphRenderImageVertical() { +int APS5_VABI sceFontGlyphRenderImageVertical(FontGlyph glyph, const FontStyleFrame* styleFrame, FontRenderer renderer, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* output) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -143,7 +144,7 @@ int APS5_VABI sceFontGraphicsEndFrame(FontGraphicsFrame* frame) { return 0; } -int APS5_VABI sceFontGraphicsExchangeResource() { +int APS5_VABI sceFontGraphicsExchangeResource(FontGraphicsResource* resource, FontGraphicsDesignResource* replacement, FontGraphicsDesignResource** previous) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -208,12 +209,12 @@ int APS5_VABI sceFontGraphicsRegionInitRoundish(FontGraphicsRegion* region, floa return 0; } -int APS5_VABI sceFontGraphicsRelease() { +int APS5_VABI sceFontGraphicsRelease(void** object) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsRenderResource() { +int APS5_VABI sceFontGraphicsRenderResource(FontGraphicsFrame* frame, FontGraphicsDesignResource* design, float x, float y, FontGraphicsResource** resource) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -263,7 +264,7 @@ int APS5_VABI sceFontGraphicsSetupLocation(FontGraphicsResource* resource, float return 0; } -int APS5_VABI sceFontGraphicsSetupPositioning() { +int APS5_VABI sceFontGraphicsSetupPositioning(FontGraphicsResource* resource, std::uint32_t flags, float x, float y) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -348,7 +349,7 @@ int APS5_VABI sceFontGraphicsUpdateLocation(FontGraphicsResource* resource, floa return 0; } -int APS5_VABI sceFontGraphicsUpdatePositioning() { +int APS5_VABI sceFontGraphicsUpdatePositioning(FontGraphicsResource* resource, std::uint32_t flags, float x, float y) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -378,7 +379,7 @@ int APS5_VABI sceFontSupportGlyphs() { return 0; } -int APS5_VABI sceFontWordsFindWordCharacters() { +int APS5_VABI sceFontWordsFindWordCharacters(FontWords* words, FontTextCharacter* firstCharacter, FontTextCharacter* endCharacter, FontTextCharacter** lastCharacter, FontTextCharacter** nextCharacter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 2ba6558989..818b5183ab 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -28,3 +28,5 @@ - [Font graphics fill and transform signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): region layout and scalar float arguments are confirmed from assembly, including the extra 32-bit corner flags omitted by the roundish-region decompilation. Fill-method, plot and rates layouts, layout selector meanings and mapping parameter meanings remain unresolved. Protected bodies are unchanged. Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Device usage, frame and resource layouts remain opaque; their runtime implementation is still unavailable. + +Glyph image rendering uses six pointer arguments and two scalar coordinates, with existing style, surface, metrics and output layouts. Positioning takes a mask and two floats. Control, word-character search and resource release/exchange/render signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered; control payload types depend on the command and remain unmodeled. From 12eb05361afd24abda03d76d8a786ed8d2892fc1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:30:07 +0300 Subject: [PATCH 308/523] fix(font): recover canvas and texture construction signatures --- core/libs/prx/libSceFont/src/Unimplemented.cpp | 17 +++++++++++------ docs/dev/debt/libSceFont.md | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index bc6c9d98d0..1e3698f746 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -16,6 +16,11 @@ struct FontGlyphExpandBufferState; struct FontGraphicsResource; struct FontGraphicsFrame; struct FontGraphicsDesignResource; +struct FontGraphicsDesign; +struct FontGraphicsCanvas; +struct FontGraphicsCanvasSequence; +struct FontGraphicsSurface; +struct FontGraphicsSurfaceTexture; struct FontGraphicsDeviceUsage; struct FontGraphicsFillMethod; struct FontGraphicsFillPlot; @@ -289,27 +294,27 @@ int APS5_VABI sceFontGraphicsSetupShapeFillPlot(FontGraphicsResource* resource, return 0; } -int APS5_VABI sceFontGraphicsStructureCanvas() { +int APS5_VABI sceFontGraphicsStructureCanvas(FontGraphicsDevice* device, std::uint32_t width, std::uint32_t height, std::int32_t format, FontGraphicsCanvas** canvas) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsStructureCanvasSequence() { +int APS5_VABI sceFontGraphicsStructureCanvasSequence(FontGraphicsDevice* device, FontGraphicsCanvas* canvas, const FontGraphicsSurface* surface, std::uint64_t flags, FontGraphicsCanvasSequence** sequence) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsStructureDesign() { +int APS5_VABI sceFontGraphicsStructureDesign(FontGraphicsDevice* device, std::int32_t format, FontGraphicsDesign** design) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsStructureDesignResource() { +int APS5_VABI sceFontGraphicsStructureDesignResource(FontGraphicsDevice* device, FontGraphicsDesign* design, FontGraphicsDesignResource** resource) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontGraphicsStructureSurfaceTexture() { +int APS5_VABI sceFontGraphicsStructureSurfaceTexture(FontGraphicsDevice* device, const FontGraphicsSurface* surface, FontGraphicsSurfaceTexture** texture) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -389,7 +394,7 @@ int APS5_VABI sceFontGraphicsDrawupFillTextureImageObject(void) { return 0; } -int APS5_VABI sceFontGraphicsDrawupFillTexturePatternObject(void) { +int APS5_VABI sceFontGraphicsDrawupFillTexturePatternObject(FontGraphicsFrame* frame, const void* data, std::uint32_t dataSize, std::uint32_t firstMode, std::uint32_t secondMode, std::int32_t firstCount, std::int32_t firstLayout, std::int32_t secondLayout, std::int32_t secondCount, FontGraphicsFill** fill) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 818b5183ab..693d3c41e8 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -30,3 +30,5 @@ Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Device usage, frame and resource layouts remain opaque; their runtime implementation is still unavailable. Glyph image rendering uses six pointer arguments and two scalar coordinates, with existing style, surface, metrics and output layouts. Positioning takes a mask and two floats. Control, word-character search and resource release/exchange/render signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered; control payload types depend on the command and remain unmodeled. + +Canvas, design, surface texture and fill-pattern construction signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Pattern mode/count semantics and the graphics surface descriptor fields still require reconstruction; distinct opaque types preserve the confirmed object categories. From f775cecf378fff1ebb0c24c14993d0e96136c7e0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:30:49 +0300 Subject: [PATCH 309/523] fix(font): define fill descriptors and device usage layouts --- .../libs/prx/libSceFont/src/Unimplemented.cpp | 92 ++++++++++++++++++- docs/dev/debt/libSceFont.md | 4 +- 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index 1e3698f746..48854d5852 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -21,10 +21,6 @@ struct FontGraphicsCanvas; struct FontGraphicsCanvasSequence; struct FontGraphicsSurface; struct FontGraphicsSurfaceTexture; -struct FontGraphicsDeviceUsage; -struct FontGraphicsFillMethod; -struct FontGraphicsFillPlot; -struct FontGraphicsFillRates; struct FontGraphicsFill; struct FontGraphicsRegion { @@ -48,6 +44,94 @@ static_assert(offsetof(FontGraphicsRegion, tag) == 20); static_assert(offsetof(FontGraphicsRegion, outerRadius) == 24); static_assert(offsetof(FontGraphicsRegion, innerRadius) == 28); +struct FontGraphicsFillMethod { + std::int32_t mode; + std::int32_t type; + std::uint32_t flags; + std::uint32_t tag; + std::byte reserved[16]; +}; + +static_assert(sizeof(FontGraphicsFillMethod) == 32); +static_assert(offsetof(FontGraphicsFillMethod, mode) == 0); +static_assert(offsetof(FontGraphicsFillMethod, type) == 4); +static_assert(offsetof(FontGraphicsFillMethod, flags) == 8); +static_assert(offsetof(FontGraphicsFillMethod, tag) == 12); +static_assert(offsetof(FontGraphicsFillMethod, reserved) == 16); + +struct FontGraphicsFillPlot { + float mapping[4]; + const FontGraphicsRegion* region; + std::uint16_t tag; + std::uint16_t reserved26; + std::uint16_t layout; + std::uint8_t flags; + std::byte reserved[33]; +}; + +static_assert(sizeof(FontGraphicsFillPlot) == 64); +static_assert(offsetof(FontGraphicsFillPlot, mapping) == 0); +static_assert(offsetof(FontGraphicsFillPlot, region) == 16); +static_assert(offsetof(FontGraphicsFillPlot, tag) == 24); +static_assert(offsetof(FontGraphicsFillPlot, reserved26) == 26); +static_assert(offsetof(FontGraphicsFillPlot, layout) == 28); +static_assert(offsetof(FontGraphicsFillPlot, flags) == 30); +static_assert(offsetof(FontGraphicsFillPlot, reserved) == 31); + +struct FontGraphicsFillRates { + float mapping[4]; + const FontGraphicsFill* fill; + std::uint16_t tag; + std::uint16_t effect; + std::uint16_t layout; + std::uint8_t flags; + std::byte reserved[33]; +}; + +static_assert(sizeof(FontGraphicsFillRates) == 64); +static_assert(offsetof(FontGraphicsFillRates, mapping) == 0); +static_assert(offsetof(FontGraphicsFillRates, fill) == 16); +static_assert(offsetof(FontGraphicsFillRates, tag) == 24); +static_assert(offsetof(FontGraphicsFillRates, effect) == 26); +static_assert(offsetof(FontGraphicsFillRates, layout) == 28); +static_assert(offsetof(FontGraphicsFillRates, flags) == 30); +static_assert(offsetof(FontGraphicsFillRates, reserved) == 31); + +struct FontGraphicsPoolUsage { + std::uint32_t capacity; + std::uint32_t used; + std::uint32_t available; + std::uint32_t peak; + std::int32_t change; +}; + +static_assert(sizeof(FontGraphicsPoolUsage) == 20); +static_assert(offsetof(FontGraphicsPoolUsage, capacity) == 0); +static_assert(offsetof(FontGraphicsPoolUsage, used) == 4); +static_assert(offsetof(FontGraphicsPoolUsage, available) == 8); +static_assert(offsetof(FontGraphicsPoolUsage, peak) == 12); +static_assert(offsetof(FontGraphicsPoolUsage, change) == 16); + +struct FontGraphicsMemoryUsage { + FontGraphicsPoolUsage storage; + FontGraphicsPoolUsage allocations; + void* address; +}; + +static_assert(sizeof(FontGraphicsMemoryUsage) == 48); +static_assert(offsetof(FontGraphicsMemoryUsage, storage) == 0); +static_assert(offsetof(FontGraphicsMemoryUsage, allocations) == 20); +static_assert(offsetof(FontGraphicsMemoryUsage, address) == 40); + +struct FontGraphicsDeviceUsage { + FontGraphicsMemoryUsage first; + FontGraphicsMemoryUsage second; +}; + +static_assert(sizeof(FontGraphicsDeviceUsage) == 96); +static_assert(offsetof(FontGraphicsDeviceUsage, first) == 0); +static_assert(offsetof(FontGraphicsDeviceUsage, second) == 48); + #pragma GCC visibility push(default) diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 693d3c41e8..939b8a4dd2 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -27,8 +27,10 @@ - [Font graphics fill and transform signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): region layout and scalar float arguments are confirmed from assembly, including the extra 32-bit corner flags omitted by the roundish-region decompilation. Fill-method, plot and rates layouts, layout selector meanings and mapping parameter meanings remain unresolved. Protected bodies are unchanged. -Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Device usage, frame and resource layouts remain opaque; their runtime implementation is still unavailable. +Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Frame and resource layouts remain opaque; their runtime implementation is still unavailable. Glyph image rendering uses six pointer arguments and two scalar coordinates, with existing style, surface, metrics and output layouts. Positioning takes a mask and two floats. Control, word-character search and resource release/exchange/render signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered; control payload types depend on the command and remain unmodeled. Canvas, design, surface texture and fill-pattern construction signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Pattern mode/count semantics and the graphics surface descriptor fields still require reconstruction; distinct opaque types preserve the confirmed object categories. + +[Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) now defines the 32-byte fill method, 64-byte fill plot/rates and 96-byte device usage layouts, with size and field-offset assertions. Uninterpreted descriptor tails remain reserved; pool statistics expose capacity, current usage, available bytes, peak and signed change. From add513ee50822c51154d580dd083d5d6db95a53c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:31:36 +0300 Subject: [PATCH 310/523] fix(font): restore glyph support and image fill arguments --- core/libs/prx/libSceFont/src/Unimplemented.cpp | 5 +++-- docs/dev/debt/libSceFont.md | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index 48854d5852..49bdec210f 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -9,6 +9,7 @@ struct FontGraphicsDeviceParam; struct FontGraphicsService; struct FontGraphicsServiceParam; struct FontWords; +struct FontGlyphSupport; struct FontOutlineProfile; struct FontMetrics; struct FontStyleInformation; @@ -463,7 +464,7 @@ int APS5_VABI sceFontGraphicsUpdateShapeFillPlot(FontGraphicsResource* resource, return 0; } -int APS5_VABI sceFontSupportGlyphs() { +int APS5_VABI sceFontSupportGlyphs(const FontGlyphSupport* support) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -473,7 +474,7 @@ int APS5_VABI sceFontWordsFindWordCharacters(FontWords* words, FontTextCharacter return 0; } -int APS5_VABI sceFontGraphicsDrawupFillTextureImageObject(void) { +int APS5_VABI sceFontGraphicsDrawupFillTextureImageObject(FontGraphicsFrame* frame, const void* data, std::uint32_t dataSize, float x, float y, float width, float height, FontGraphicsFill** fill) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 939b8a4dd2..8d8ec0ff90 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -34,3 +34,5 @@ Glyph image rendering uses six pointer arguments and two scalar coordinates, wit Canvas, design, surface texture and fill-pattern construction signatures in [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered. Pattern mode/count semantics and the graphics surface descriptor fields still require reconstruction; distinct opaque types preserve the confirmed object categories. [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) now defines the 32-byte fill method, 64-byte fill plot/rates and 96-byte device usage layouts, with size and field-offset assertions. Uninterpreted descriptor tails remain reserved; pool statistics expose capacity, current usage, available bytes, peak and signed change. + +The [glyph support registration and texture-image fill signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered from native argument use. The image constructor takes a byte span and four scalar coordinates; the decompiler omits its successful path. `CreateWords` and `CreateGraphicsServiceWithEdition` consume the output pointer in the fourth general-purpose argument slot but do not read the third slot, whose source type is not established. From cfbff67da64abf2da5ae6e37dfcf4ec53ec39c86 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:32:14 +0300 Subject: [PATCH 311/523] fix(font): implement glyph attribute transitions and output --- core/libs/prx/libSceFont/src/Render.cpp | 19 +++++++--- core/libs/tests/GuestFont.cpp | 49 +++++++++++++------------ 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Render.cpp b/core/libs/prx/libSceFont/src/Render.cpp index 3f78fa635a..7ed1af5165 100644 --- a/core/libs/prx/libSceFont/src/Render.cpp +++ b/core/libs/prx/libSceFont/src/Render.cpp @@ -420,11 +420,18 @@ int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, FontGlyph* pGlyph) { return SCE_FONT_OK; } -int APS5_VABI sceFontGlyphDefineAttribute(FontGlyph glyph, int attribute, int* oldAttribute) { - auto* generated = TryGetGeneratedGlyph(glyph); - if (!generated) return SCE_FONT_ERROR_INVALID_GLYPH; - if (oldAttribute) *oldAttribute = generated->attribute; - generated->attribute = attribute; +int APS5_VABI sceFontGlyphDefineAttribute(FontGlyph glyph, std::int32_t attribute, std::int32_t* previousAttribute) { + if (!glyph || glyph->magic != GLYPH_MAGIC) { + if (previousAttribute) *previousAttribute = 0; + return SCE_FONT_ERROR_INVALID_GLYPH; + } + if (attribute != 0x10 && attribute != 0x11) { + if (previousAttribute) *previousAttribute = 0; + return SCE_FONT_ERROR_INVALID_PARAMETER; + } + const std::int32_t previous = (glyph->flags & 2) ? 0x11 : 0x10; + glyph->flags = static_cast((glyph->flags & ~2u) | ((attribute - 0x10) << 1)); + if (previousAttribute) *previousAttribute = previous; return SCE_FONT_OK; } @@ -433,7 +440,7 @@ int APS5_VABI sceFontGlyphGetAttribute(FontGlyph glyph, int attribute, int* nowA if (!nowAttribute) return SCE_FONT_ERROR_INVALID_PARAMETER; auto* generated = TryGetGeneratedGlyph(glyph); if (!generated) return SCE_FONT_ERROR_INVALID_GLYPH; - *nowAttribute = generated->attribute; + *nowAttribute = (glyph->flags & 2) ? 0x11 : 0x10; return SCE_FONT_OK; } diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index 8db5efd303..fc67507390 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -49,7 +49,7 @@ int APS5_VABI sceFontGetCharGlyphCode(FontHandle, std::uint32_t, std::uint32_t*) int APS5_VABI sceFontGetFontResolution(FontHandle, std::uint32_t*, float*); int APS5_VABI sceFontGetRenderScaledKerning(FontHandle, std::uint32_t, std::uint32_t, FontKerning*); int APS5_VABI sceFontGenerateCharGlyph(FontHandle, std::uint32_t, const FontGenerateGlyphDetail*, FontGlyph*); -int APS5_VABI sceFontGlyphDefineAttribute(FontGlyph, int, int*); +int APS5_VABI sceFontGlyphDefineAttribute(FontGlyph, std::int32_t, std::int32_t*); int APS5_VABI sceFontGlyphGetAttribute(FontGlyph, int, int*); int APS5_VABI sceFontDeleteGlyph(const FontMemory*, FontGlyph*); int APS5_VABI sceFontTextSourceInit(FontTextSource*, const void*, std::uint32_t, FontTextParseFunction, void*); @@ -393,30 +393,31 @@ int main() { Require(sceFontSetScalePixel(font, 100.0f, 100.0f) == SCE_FONT_OK); FontGlyph glyph = nullptr; Require(sceFontGenerateCharGlyph(font, 'A', nullptr, &glyph) == SCE_FONT_OK && glyph != nullptr); - int glyphAttribute = -1; - Require(sceFontGlyphDefineAttribute(glyph, 0x11, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 0); - Require(sceFontGlyphGetAttribute(glyph, 0x11, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 0x11); - glyphAttribute = -1; - Require(sceFontGlyphDefineAttribute(glyph, 0, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 0x11); - Require(sceFontGlyphDefineAttribute(glyph, 5, nullptr) == SCE_FONT_OK); - glyphAttribute = -1; - Require(sceFontGlyphGetAttribute(glyph, 0, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 5); - Require(sceFontGlyphGetAttribute(glyph, 0x11, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); - glyphAttribute = -1; - Require(sceFontGlyphDefineAttribute(nullptr, 0x11, &glyphAttribute) == SCE_FONT_ERROR_INVALID_GLYPH && glyphAttribute == -1); - Require(sceFontGlyphGetAttribute(nullptr, 0x11, &glyphAttribute) == SCE_FONT_ERROR_INVALID_GLYPH && glyphAttribute == -1); + const auto originalFlags = glyph->flags; + std::int32_t previousAttribute = -1; + Require(sceFontGlyphDefineAttribute(glyph, 0x11, &previousAttribute) == SCE_FONT_OK); + Require(previousAttribute == ((originalFlags & 2) ? 0x11 : 0x10)); + Require(glyph->flags == (originalFlags | 2)); + Require(sceFontGlyphDefineAttribute(glyph, 0x10, &previousAttribute) == SCE_FONT_OK && previousAttribute == 0x11); + Require(glyph->flags == (originalFlags & ~2u)); + Require(sceFontGlyphDefineAttribute(glyph, 0x10, &previousAttribute) == SCE_FONT_OK && previousAttribute == 0x10); + Require(sceFontGlyphDefineAttribute(glyph, 0x11, nullptr) == SCE_FONT_OK); + for (std::int32_t attribute = -1; attribute <= 256; ++attribute) { + if (attribute == 0x10 || attribute == 0x11) continue; + previousAttribute = -1; + Require(sceFontGlyphDefineAttribute(glyph, attribute, &previousAttribute) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(previousAttribute == 0 && glyph->flags == (originalFlags | 2)); + } + previousAttribute = -1; + Require(sceFontGlyphDefineAttribute(nullptr, 0x11, &previousAttribute) == SCE_FONT_ERROR_INVALID_GLYPH && previousAttribute == 0); + Require(sceFontGlyphDefineAttribute(nullptr, 0x11, nullptr) == SCE_FONT_ERROR_INVALID_GLYPH); FontGlyphOpaque notAGlyph{}; - Require(sceFontGlyphDefineAttribute(¬AGlyph, 0x11, &glyphAttribute) == SCE_FONT_ERROR_INVALID_GLYPH); - Require(sceFontGlyphGetAttribute(¬AGlyph, 0x11, &glyphAttribute) == SCE_FONT_ERROR_INVALID_GLYPH); - FontGlyph otherGlyph = nullptr; - Require(sceFontGenerateCharGlyph(font, 'V', nullptr, &otherGlyph) == SCE_FONT_OK && otherGlyph != nullptr); - glyphAttribute = -1; - Require(sceFontGlyphGetAttribute(otherGlyph, 0, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 0); - Require(sceFontGlyphDefineAttribute(otherGlyph, 9, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 0); - glyphAttribute = -1; - Require(sceFontGlyphGetAttribute(otherGlyph, 0, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 9); - Require(sceFontGlyphGetAttribute(glyph, 0, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 5); - Require(sceFontDeleteGlyph(&memory, &otherGlyph) == SCE_FONT_OK && otherGlyph == nullptr); + previousAttribute = -1; + Require(sceFontGlyphDefineAttribute(¬AGlyph, 0x11, &previousAttribute) == SCE_FONT_ERROR_INVALID_GLYPH && previousAttribute == 0); + int glyphAttribute = -1; + Require(sceFontGlyphGetAttribute(glyph, 0x11, &glyphAttribute) == SCE_FONT_OK && glyphAttribute == 0x11); + Require(sceFontGlyphGetAttribute(glyph, 0x11, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontGlyphGetAttribute(nullptr, 0x11, &glyphAttribute) == SCE_FONT_ERROR_INVALID_GLYPH); Require(sceFontDeleteGlyph(&memory, &glyph) == SCE_FONT_OK && glyph == nullptr); FontKerning kerning{1.0f, 1.0f, 1.0f, 1.0f}; Require(sceFontGetKerning(font, 'A', 'V', &kerning) == SCE_FONT_OK && KerningIs(kerning, -20.0f)); From 86c1dadddeaf7cfcaef85bd820f3e9c7e0be3dc2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:33:19 +0300 Subject: [PATCH 312/523] fix(font): match pixel resolution validation and locking --- core/libs/prx/libSceFont/src/Library.cpp | 16 +++++++++++++--- core/libs/tests/GuestFont.cpp | 5 ++++- docs/dev/debt/libSceFont.md | 1 - 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Library.cpp b/core/libs/prx/libSceFont/src/Library.cpp index e872a3fa66..a3e01002c3 100644 --- a/core/libs/prx/libSceFont/src/Library.cpp +++ b/core/libs/prx/libSceFont/src/Library.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "prx/libSceFont/include/FontInternal.hpp" @@ -298,10 +299,19 @@ int APS5_VABI sceFontRendererSetOutlineBufferPolicy(FontRenderer fontRenderer, s } int APS5_VABI sceFontGetPixelResolution(FontLibrary library, std::uint32_t* subPixelCount) { + auto* lib = static_cast(library); + std::uint32_t previous = 0; + if (!lib || !AcquireLibraryLock(lib, previous)) { + if (subPixelCount) *subPixelCount = 0; + return SCE_FONT_ERROR_INVALID_LIBRARY; + } + const auto unlock = [previous](FontLibNative* locked) { ReleaseLibraryLock(locked, previous); }; + const std::unique_ptr lock(lib, unlock); + if (!lib->sys_driver || !lib->sys_driver->pixel_resolution) { + if (subPixelCount) *subPixelCount = 0; + return SCE_FONT_ERROR_INVALID_LIBRARY; + } if (!subPixelCount) return SCE_FONT_ERROR_INVALID_PARAMETER; - *subPixelCount = 0; - const auto* lib = static_cast(library); - if (!lib || lib->magic != LIBRARY_MAGIC || !lib->sys_driver || !lib->sys_driver->pixel_resolution) return SCE_FONT_ERROR_INVALID_LIBRARY; *subPixelCount = lib->sys_driver->pixel_resolution(); return SCE_FONT_OK; } diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index fc67507390..ac87c2de70 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -238,7 +238,7 @@ int main() { std::uint32_t subPixelCount = 1; Require(sceFontGetPixelResolution(library, &subPixelCount) == SCE_FONT_OK && subPixelCount == 64); Require(sceFontGetPixelResolution(library, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); - Require(sceFontGetPixelResolution(nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontGetPixelResolution(nullptr, nullptr) == SCE_FONT_ERROR_INVALID_LIBRARY); subPixelCount = 1; Require(sceFontGetPixelResolution(nullptr, &subPixelCount) == SCE_FONT_ERROR_INVALID_LIBRARY && subPixelCount == 0); FontHandleOpaque notALibrary{}; @@ -250,8 +250,11 @@ int main() { Require(sceFontCreateLibrary(&memory, &coarseDriver, &coarseLibrary) == SCE_FONT_OK && coarseLibrary != nullptr); Require(sceFontGetPixelResolution(coarseLibrary, &subPixelCount) == SCE_FONT_OK && subPixelCount == 16); Require(sceFontGetPixelResolution(library, &subPixelCount) == SCE_FONT_OK && subPixelCount == 64); + Require(static_cast(coarseLibrary)->lock_word == 0); coarseDriver.pixel_resolution = nullptr; Require(sceFontGetPixelResolution(coarseLibrary, &subPixelCount) == SCE_FONT_ERROR_INVALID_LIBRARY && subPixelCount == 0); + Require(sceFontGetPixelResolution(coarseLibrary, nullptr) == SCE_FONT_ERROR_INVALID_LIBRARY); + Require(static_cast(coarseLibrary)->lock_word == 0); Require(sceFontDestroyLibrary(&coarseLibrary) == SCE_FONT_OK && coarseLibrary == nullptr); void* cacheBuffer = nullptr; diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 8d8ec0ff90..919ecce40a 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -4,7 +4,6 @@ - [sceFontSetScriptLanguage, sceFontGetScriptLanguage, sceFontSetTypographicDesign, sceFontGetTypographicDesign](../../../core/libs/prx/libSceFont/src/Style.cpp) (libSceFont) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.cs), not confirmed for the PS5. The handle keeps the language set for each script and the feature set for each typographic design and returns them, with 0 for one never set; the console default and its valid ranges are unknown, so any value is accepted. Neither setting changes shaping or rendering yet - [sceFontGetRenderScaledKerning](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale -- [sceFontGetPixelResolution](../../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. The value is the one the library's driver reports (64 for FreeType); the argument checks and the cleared output follow `sceFontGetLibrary` - [sceFontDettachDeviceCacheBuffer](../../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. It detaches the cache buffer attached by `sceFontAttachDeviceCacheBuffer`, returns the buffer and its size, and reports `SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER` when no cache buffer is attached - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` - [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title; the outputs are inferred from the names. The resolution is the face's units per EM and the scale pixel the driver's face scale (units per EM / 64 for FreeType), the pixel scale a font has when opened. Either output may be null, as in `sceFontGetResolutionDpi` From 581bbc18228dc6f37fe65961e6414ca75a2844d5 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 19:33:19 +0300 Subject: [PATCH 313/523] docs(contentsearch): condense remaining query and metadata debt --- docs/dev/debt/libSceContentSearch.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceContentSearch.md b/docs/dev/debt/libSceContentSearch.md index 8ef6373ca4..6df6de62ff 100644 --- a/docs/dev/debt/libSceContentSearch.md +++ b/docs/dev/debt/libSceContentSearch.md @@ -6,4 +6,5 @@ ### Functional -- [libSceContentSearch](../../../core/libs/prx/libSceContentSearch/Export.cpp) searches a content library that is empty on the host, as nothing is exported to it. The signatures follow [AlexAltea/orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json#L555-L569) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Content/ContentSearch.cs#L255-L316), which agree; the filter and sort layouts and enums, the limit of 92 rows, `0x809D100A` for a larger limit and `0x809D1001` before `sceContentSearchInit` are SharpProspero's. `sceContentSearchGetNumOfContent`, `sceContentSearchGetTotalContentSize` and `sceContentSearchSearchContent` report 0 rows and 0 bytes without writing a content row, and the update id they and `sceContentSearchGetContentLastUpdateId` report is always 0; no reference gives the console's update id for a library that never changed. Filters and sort conditions are only checked against SharpProspero's enums, and an empty nested column set, a null filter value or an unknown enum value throws, as their validation is not yet matched to the native query builder; a null column set with length 0 is taken as no filter. `sceContentSearchOpenMetadata` and `sceContentSearchOpenMetadataByContentId` throw, as no reference gives the error for a path or content id that is not in the library, so no metadata id is ever handed out; metadata-field and value queries with valid pointers still throw because field-dependent errors remain unresolved. Every title is taken to hold the content-search permission (SharpProspero's `0x809D1013` is never returned). Initialization checks and outer query pointer/count/limit precedence follow the supplied module. Guest heap allocation, platform-dependent minimum heap size and nested query validation remain unimplemented +- [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. +- [Metadata access](../../../core/libs/prx/libSceContentSearch/Export.cpp) cannot open metadata by path or content id because missing-content errors remain unresolved. No metadata handles are issued. Queries reject invalid outer pointers but still throw for valid pointers because errors depend on the requested field; closing an unopened handle returns the confirmed invalid-argument error. From e544f181d1b4713a23951cf4a1594a19e5c147b7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:12:44 +0300 Subject: [PATCH 314/523] fix(ult): recover synchronization and ulthread signatures --- core/libs/prx/libSceUlt/Export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 10ba814b81..2d71ab6749 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -912,7 +912,7 @@ int APS5_VABI _sceUltUlthreadCreate(void* ulthread, const char* name, UltUlthrea return sceUltUlthreadCreate(ulthread, name, entry, arg, context, sizeContext, runtime, optParam, buildVersion); } -int APS5_VABI sceUltConditionVariableSignalAll() { +int APS5_VABI sceUltConditionVariableSignalAll(void* conditionVariable) { NotImplemented_nid_no_patch(__func__); return 0; } From 7d14ba91a49b75157c5149bc0cf62257d1cbb9aa Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:14:49 +0300 Subject: [PATCH 315/523] fix(ult): define diagnostic layouts and output arguments --- core/libs/prx/libSceUlt/Export.cpp | 18 +-- core/libs/prx/libSceUlt/SceUltTypes.hpp | 199 ++++++++++++++++++++++++ core/libs/tests/GuestUltThread.cpp | 9 +- docs/dev/debt/libSceUlt.md | 3 +- 4 files changed, 217 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 2d71ab6749..8f15feec36 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -917,47 +917,47 @@ int APS5_VABI sceUltConditionVariableSignalAll(void* conditionVariable) { return 0; } -int APS5_VABI sceUltGetConditionVariableInfo() { +int APS5_VABI sceUltGetConditionVariableInfo(void* object, UltConditionVariableInfo* info, UltWaitingThreadInfo* waitingThreads, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetMutexInfo() { +int APS5_VABI sceUltGetMutexInfo(void* object, UltMutexInfo* info, UltWaitingThreadInfo* waitingThreads, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetQueueDataResourcePoolInfo() { +int APS5_VABI sceUltGetQueueDataResourcePoolInfo(void* object, UltQueueDataResourcePoolInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetQueueInfo() { +int APS5_VABI sceUltGetQueueInfo(void* object, UltQueueInfo* info, UltWaitingThreadInfo* waitingThreads, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetReaderWriterLockInfo() { +int APS5_VABI sceUltGetReaderWriterLockInfo(void* object, UltReaderWriterLockInfo* info, UltWaitingThreadInfo* waitingThreads, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetSemaphoreInfo() { +int APS5_VABI sceUltGetSemaphoreInfo(void* object, UltSemaphoreInfo* info, UltWaitingThreadInfo* waitingThreads, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetUlthreadInfo() { +int APS5_VABI sceUltGetUlthreadInfo(void* object, UltUlthreadInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetUlthreadRuntimeInfo() { +int APS5_VABI sceUltGetUlthreadRuntimeInfo(void* runtime, UltUlthreadRuntimeInfo* info, void** ulthreads, std::uint32_t ulthreadCapacity, Pthread* workerThreads, std::uint32_t workerCapacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceUltGetWaitingQueueResourcePoolInfo() { +int APS5_VABI sceUltGetWaitingQueueResourcePoolInfo(void* object, UltWaitingQueueResourcePoolInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/prx/libSceUlt/SceUltTypes.hpp b/core/libs/prx/libSceUlt/SceUltTypes.hpp index efe53ec5a8..e02d67f2b5 100644 --- a/core/libs/prx/libSceUlt/SceUltTypes.hpp +++ b/core/libs/prx/libSceUlt/SceUltTypes.hpp @@ -5,6 +5,8 @@ #include #include #include + +#include #include #include #include @@ -33,6 +35,203 @@ inline int InitializeOptParam(UltOptParam* optParam) { return ULT_OK; } +struct UltWaitingThreadInfo { + std::uint64_t size; + void* thread; + std::uint32_t threadType; + std::int32_t waitValue; + std::byte reserved[40]; +}; + +static_assert(sizeof(UltWaitingThreadInfo) == 64); +static_assert(offsetof(UltWaitingThreadInfo, size) == 0); +static_assert(offsetof(UltWaitingThreadInfo, thread) == 8); +static_assert(offsetof(UltWaitingThreadInfo, threadType) == 16); +static_assert(offsetof(UltWaitingThreadInfo, waitValue) == 20); +static_assert(offsetof(UltWaitingThreadInfo, reserved) == 24); + +struct UltConditionVariableInfo { + std::uint64_t size; + void* object; + char name[32]; + void* mutex; + std::uint32_t waitingThreads; + std::byte reserved[68]; +}; + +static_assert(sizeof(UltConditionVariableInfo) == 128); +static_assert(offsetof(UltConditionVariableInfo, size) == 0); +static_assert(offsetof(UltConditionVariableInfo, object) == 8); +static_assert(offsetof(UltConditionVariableInfo, name) == 16); +static_assert(offsetof(UltConditionVariableInfo, mutex) == 48); +static_assert(offsetof(UltConditionVariableInfo, waitingThreads) == 56); +static_assert(offsetof(UltConditionVariableInfo, reserved) == 60); + +struct UltMutexInfo { + std::uint64_t size; + void* object; + char name[32]; + void* waitingQueuePool; + void* owner; + std::uint32_t ownerType; + std::uint32_t attributes; + std::uint32_t lockCount; + std::uint32_t waitingThreads; + std::byte reserved[48]; +}; + +static_assert(sizeof(UltMutexInfo) == 128); +static_assert(offsetof(UltMutexInfo, size) == 0); +static_assert(offsetof(UltMutexInfo, object) == 8); +static_assert(offsetof(UltMutexInfo, name) == 16); +static_assert(offsetof(UltMutexInfo, waitingQueuePool) == 48); +static_assert(offsetof(UltMutexInfo, owner) == 56); +static_assert(offsetof(UltMutexInfo, ownerType) == 64); +static_assert(offsetof(UltMutexInfo, attributes) == 68); +static_assert(offsetof(UltMutexInfo, lockCount) == 72); +static_assert(offsetof(UltMutexInfo, waitingThreads) == 76); +static_assert(offsetof(UltMutexInfo, reserved) == 80); + +struct UltSemaphoreInfo { + std::uint64_t size; + void* object; + char name[32]; + void* waitingQueuePool; + std::uint32_t resources; + std::uint32_t waitingThreads; + std::byte reserved[64]; +}; + +static_assert(sizeof(UltSemaphoreInfo) == 128); +static_assert(offsetof(UltSemaphoreInfo, size) == 0); +static_assert(offsetof(UltSemaphoreInfo, object) == 8); +static_assert(offsetof(UltSemaphoreInfo, name) == 16); +static_assert(offsetof(UltSemaphoreInfo, waitingQueuePool) == 48); +static_assert(offsetof(UltSemaphoreInfo, resources) == 56); +static_assert(offsetof(UltSemaphoreInfo, waitingThreads) == 60); +static_assert(offsetof(UltSemaphoreInfo, reserved) == 64); + +struct UltReaderWriterLockInfo { + std::uint64_t size; + void* object; + char name[32]; + void* waitingQueuePool; + std::uint32_t lockType; + std::uint32_t readers; + void* owner; + std::uint32_t ownerType; + std::uint32_t waitingThreads; + std::byte reserved[48]; +}; + +static_assert(sizeof(UltReaderWriterLockInfo) == 128); +static_assert(offsetof(UltReaderWriterLockInfo, size) == 0); +static_assert(offsetof(UltReaderWriterLockInfo, object) == 8); +static_assert(offsetof(UltReaderWriterLockInfo, name) == 16); +static_assert(offsetof(UltReaderWriterLockInfo, waitingQueuePool) == 48); +static_assert(offsetof(UltReaderWriterLockInfo, lockType) == 56); +static_assert(offsetof(UltReaderWriterLockInfo, readers) == 60); +static_assert(offsetof(UltReaderWriterLockInfo, owner) == 64); +static_assert(offsetof(UltReaderWriterLockInfo, ownerType) == 72); +static_assert(offsetof(UltReaderWriterLockInfo, waitingThreads) == 76); +static_assert(offsetof(UltReaderWriterLockInfo, reserved) == 80); + +struct UltQueueInfo { + std::uint64_t size; + void* object; + char name[32]; + void* waitingQueuePool; + void* dataPool; + std::uint64_t dataSize; + std::uint32_t queuedItems; + std::uint32_t waitingPushers; + std::uint32_t waitingPoppers; + std::byte reserved[172]; +}; + +static_assert(sizeof(UltQueueInfo) == 256); +static_assert(offsetof(UltQueueInfo, size) == 0); +static_assert(offsetof(UltQueueInfo, object) == 8); +static_assert(offsetof(UltQueueInfo, name) == 16); +static_assert(offsetof(UltQueueInfo, waitingQueuePool) == 48); +static_assert(offsetof(UltQueueInfo, dataPool) == 56); +static_assert(offsetof(UltQueueInfo, dataSize) == 64); +static_assert(offsetof(UltQueueInfo, queuedItems) == 72); +static_assert(offsetof(UltQueueInfo, waitingPushers) == 76); +static_assert(offsetof(UltQueueInfo, waitingPoppers) == 80); +static_assert(offsetof(UltQueueInfo, reserved) == 84); + +struct UltWaitingQueueResourcePoolInfo { + std::uint64_t size; + void* object; + char name[32]; + std::uint32_t availableThreads; + std::uint32_t maxThreads; + std::uint32_t availableSyncObjects; + std::uint32_t maxSyncObjects; + std::byte reserved[64]; +}; + +static_assert(sizeof(UltWaitingQueueResourcePoolInfo) == 128); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, size) == 0); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, object) == 8); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, name) == 16); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, availableThreads) == 48); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, maxThreads) == 52); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, availableSyncObjects) == 56); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, maxSyncObjects) == 60); +static_assert(offsetof(UltWaitingQueueResourcePoolInfo, reserved) == 64); + +struct UltQueueDataResourcePoolInfo { + std::uint64_t size; + void* object; + char name[32]; + void* waitingQueuePool; + std::uint64_t dataSize; + std::uint32_t availableItems; + std::uint32_t maxItems; + std::uint32_t availableQueues; + std::uint32_t maxQueues; + std::byte reserved[48]; +}; + +static_assert(sizeof(UltQueueDataResourcePoolInfo) == 128); +static_assert(offsetof(UltQueueDataResourcePoolInfo, size) == 0); +static_assert(offsetof(UltQueueDataResourcePoolInfo, object) == 8); +static_assert(offsetof(UltQueueDataResourcePoolInfo, name) == 16); +static_assert(offsetof(UltQueueDataResourcePoolInfo, waitingQueuePool) == 48); +static_assert(offsetof(UltQueueDataResourcePoolInfo, dataSize) == 56); +static_assert(offsetof(UltQueueDataResourcePoolInfo, availableItems) == 64); +static_assert(offsetof(UltQueueDataResourcePoolInfo, maxItems) == 68); +static_assert(offsetof(UltQueueDataResourcePoolInfo, availableQueues) == 72); +static_assert(offsetof(UltQueueDataResourcePoolInfo, maxQueues) == 76); +static_assert(offsetof(UltQueueDataResourcePoolInfo, reserved) == 80); + +struct UltUlthreadRuntimeInfo { + std::uint64_t size; + void* object; + char name[32]; + std::uint32_t activeUlthreads; + std::uint32_t maxUlthreads; + std::uint32_t workerThreads; + std::uint32_t reserved60; + std::uint64_t workerStackSize; + std::byte reserved[56]; +}; + +static_assert(sizeof(UltUlthreadRuntimeInfo) == 128); +static_assert(offsetof(UltUlthreadRuntimeInfo, size) == 0); +static_assert(offsetof(UltUlthreadRuntimeInfo, object) == 8); +static_assert(offsetof(UltUlthreadRuntimeInfo, name) == 16); +static_assert(offsetof(UltUlthreadRuntimeInfo, maxUlthreads) == 52); +static_assert(offsetof(UltUlthreadRuntimeInfo, activeUlthreads) == 48); +static_assert(offsetof(UltUlthreadRuntimeInfo, workerThreads) == 56); +static_assert(offsetof(UltUlthreadRuntimeInfo, reserved60) == 60); +static_assert(offsetof(UltUlthreadRuntimeInfo, workerStackSize) == 64); +static_assert(offsetof(UltUlthreadRuntimeInfo, reserved) == 72); + +struct UltUlthreadInfo; + struct UltMutexState { std::mutex _mutex; std::condition_variable _available; diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 0d074c1d20..8a77a7e890 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -8,6 +8,9 @@ #include #include +struct UltConditionVariableInfo; +struct UltWaitingThreadInfo; + extern "C" { int APS5_VABI sceUltInitialize(); int APS5_VABI sceUltFinalize(); @@ -38,7 +41,8 @@ int APS5_VABI sceUltSemaphoreDestroy(void*); int APS5_VABI sceUltQueueDestroy(void*); int APS5_VABI sceUltQueueDataResourcePoolDestroy(void*); int APS5_VABI sceUltConditionVariableDestroy(void*); -int APS5_VABI sceUltGetConditionVariableInfo(); +int APS5_VABI sceUltReaderWriterLockDestroy(void*); +int APS5_VABI sceUltGetConditionVariableInfo(void*, UltConditionVariableInfo*, UltWaitingThreadInfo*, std::uint32_t); } static constexpr int Ok = 0; @@ -110,9 +114,10 @@ static void Validation() { CHECK(sceUltFinalize() == Ok); CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(1, 1) == 256u + 16u * 1024u); CHECK(sceUltConditionVariableDestroy(nullptr) == -2139029503); + CHECK(sceUltReaderWriterLockDestroy(nullptr) == ULT_ERROR_NULL); bool threwInfo = false; try { - sceUltGetConditionVariableInfo(); + sceUltGetConditionVariableInfo(nullptr, nullptr, nullptr, 0); } catch (const std::runtime_error&) { threwInfo = true; } diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index b55da9ee7f..7f17598f56 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -8,7 +8,8 @@ - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - the added `sceUltQueuePop`, `sceUltQueueTryPush`, `sceUltQueueDestroy` and `sceUltQueueDataResourcePoolDestroy` signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with `ULT_ERROR_BUSY` and otherwise discard queued data; pool exhaustion returns `ULT_ERROR_AGAIN`. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. - [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) - a non-null option parameter is accepted and ignored, as the other Ult create calls already do. [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/00d087b2323bf377fd39b7163a256977383cb232/include/orbis/Ult.h) declares `sceUltQueueOptParamInitialize` and `sceUltQueueDataResourcePoolOptParamInitialize` without their structures; the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.h#L25-L78) defines both as a 16-byte reserved header followed by reserved fields, 128 bytes, with no settings, and [fpPS4](https://github.com/red-prig/fpPS4/blob/5ffbdbbd76b4da6e4b8721743ea491f494b28913/src/ps4_libsceult.pas#L142-L172) keeps them as 128 opaque bytes. That the PS5 structures carry no settings either is assumed from these, not verified - [_sceUltWaitingQueueResourcePoolCreate, _sceUltSemaphoreCreate, _sceUltMutexOptParamInitialize, _sceUltMutexCreate, _sceUltQueueDataResourcePoolCreate and _sceUltQueueCreate](../../../core/libs/prx/libSceUlt/Export.cpp) (libSceUlt) forward to the exports without the underscore; that their argument lists match is inferred from the other underscore Ult exports, not measured -- [sceUltConditionVariableSignalAll, sceUltGetConditionVariableInfo, sceUltGetMutexInfo, sceUltGetQueueDataResourcePoolInfo, sceUltGetQueueInfo, sceUltGetReaderWriterLockInfo, sceUltGetSemaphoreInfo, sceUltGetUlthreadInfo, sceUltGetUlthreadRuntimeInfo, sceUltGetWaitingQueueResourcePoolInfo, sceUltAbstractThreadFactoryCreateThread, sceUltAbstractThreadFactoryGetNumMaxThreads, sceUltAbstractThreadFactoryJoinThread, sceUltKernelThreadFactoryFinalize, sceUltKernelThreadFactoryGetFactoryPointer, sceUltKernelThreadFactoryInitialize, sceUltModuleFinalize, sceUltModuleInitialize, sceUltUlthreadFactoryFinalize, sceUltUlthreadFactoryGetFactoryPointer, sceUltUlthreadFactoryGetWorkAreaSize, sceUltUlthreadFactoryInitialize, sceUltConditionVariableCreate, sceUltConditionVariableOptParamInitialize](../../../core/libs/prx/libSceUlt/Export.cpp) (libSceUlt) - throwing stubs declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware +- [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. The nested context in `UltUlthreadInfo` still requires reconstruction. + - [sceUltMutexTryLock, the reader-writer lock and sceUltUlthreadGetSelf, sceUltUlthreadYield and sceUltUlthreadExit](../../../core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp) (libSceUlt) - the signatures follow the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.cpp) (a 256-byte lock and 128-byte option structures), not a PS5 binary. The rules are assumed: readers may take the lock while a writer waits, a thread that holds the write lock gets `ULT_ERROR_STATE` for any further lock instead of waiting on itself, a reader asking for the write lock waits on its own read lock, and the try forms return `ULT_ERROR_BUSY`. Outside an ulthread, `sceUltUlthreadGetSelf`, `sceUltUlthreadYield` and `sceUltUlthreadExit` return `ULT_ERROR_STATE`; inside one they act on the guest thread it runs on, since ulthreads are not scheduled cooperatively here ### Functional From b469744334b5df035295fc14c59b9a3d2e031f00 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:15:19 +0300 Subject: [PATCH 316/523] fix(ult): match running thread and runtime destruction errors --- core/libs/prx/libSceUlt/Export.cpp | 4 ++-- core/libs/tests/GuestUltThread.cpp | 7 ++++--- docs/dev/debt/libSceUlt.md | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 8f15feec36..f449f06dba 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -824,7 +824,7 @@ int APS5_VABI sceUltUlthreadTryJoin(void* ulthread, std::int32_t* status) { return ULT_ERROR_STATE; } if (!it->second->_exited.load(std::memory_order_acquire)) { - return ULT_ERROR_BUSY; + return ULT_ERROR_AGAIN; } } return sceUltUlthreadJoin(ulthread, status); @@ -869,7 +869,7 @@ int APS5_VABI sceUltUlthreadRuntimeDestroy(void* runtime) { } for (const auto& entry : gUlthreads) { if (entry.second->_runtime == runtime) { - return ULT_ERROR_BUSY; + return ULT_ERROR_STATE; } } gRuntimes.erase(it); diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 8a77a7e890..9f4a146251 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -49,6 +49,7 @@ static constexpr int Ok = 0; static constexpr int Null = -2139029503; static constexpr int State = -2139029498; static constexpr int Busy = -2139029497; +static constexpr int Again = -2139029496; static void Require(bool value, int line) { if (!value) { @@ -139,9 +140,9 @@ static void TryJoin() { const int running = sceUltUlthreadTryJoin(&thread, &status); gRelease.store(true, std::memory_order_release); int joined; - while ((joined = sceUltUlthreadTryJoin(&thread, &status)) == Busy) std::this_thread::yield(); + while ((joined = sceUltUlthreadTryJoin(&thread, &status)) == Again) std::this_thread::yield(); - CHECK(running == Busy); + CHECK(running == Again); CHECK(joined == Ok && status == 0x3C); CHECK(sceUltUlthreadTryJoin(&thread, &status) == State); CHECK(sceUltUlthreadTryJoin(nullptr, &status) == Null); @@ -158,7 +159,7 @@ static void Destroy() { CHECK(_sceUltUlthreadRuntimeCreate(&other, "other", 1, 1, nullptr, nullptr, 0) == Ok); CHECK(_sceUltUlthreadCreate(&thread, "thread", Immediate, 0x11, nullptr, 0, &runtime, nullptr, 0) == Ok); CHECK(sceUltUlthreadRuntimeDestroy(&other) == Ok); - CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == Busy); + CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == State); std::int32_t status = 0; CHECK(sceUltUlthreadJoin(&thread, &status) == Ok && status == 0x11); CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == Ok); diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index 7f17598f56..69ec383a4a 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -4,7 +4,7 @@ - [Bound condition variables](../../../core/libs/prx/libSceUlt/Export.cpp) use the creation-time mutex, inferred from PPSA28061 callers of `_sceUltConditionVariableCreate`; the wait caller reloads only the condition argument. Error precedence, busy/state errors, FIFO signal selection and unsupported recursive waits have not been measured on PS5 hardware. Non-null options and waits at recursion depth above one throw. A live condition prevents destroying its bound mutex. Finalization follows the existing host mutex lifecycle: it invalidates conditions and wakes queued or reacquiring waiters with a state error. Scheduling priority and build-version validation are not implemented. -- [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - `_sceUltUlthreadRuntimeOptParamInitialize`, `_sceUltUlthreadRuntimeCreate` and `_sceUltUlthreadCreate` are assumed to take the same arguments as the existing exports without the underscore, including the trailing build version. `sceUltUlthreadTryJoin` is assumed to return `ULT_ERROR_BUSY` while the ulthread runs, and `sceUltUlthreadRuntimeDestroy` to return `ULT_ERROR_BUSY` while an ulthread created on the runtime is not joined. Neither error has been confirmed on PS5 hardware +- [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - `_sceUltUlthreadRuntimeOptParamInitialize`, `_sceUltUlthreadRuntimeCreate` and `_sceUltUlthreadCreate` are assumed to take the same arguments as the existing exports without the underscore, including the trailing build version. Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - the added `sceUltQueuePop`, `sceUltQueueTryPush`, `sceUltQueueDestroy` and `sceUltQueueDataResourcePoolDestroy` signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with `ULT_ERROR_BUSY` and otherwise discard queued data; pool exhaustion returns `ULT_ERROR_AGAIN`. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. - [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) - a non-null option parameter is accepted and ignored, as the other Ult create calls already do. [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/00d087b2323bf377fd39b7163a256977383cb232/include/orbis/Ult.h) declares `sceUltQueueOptParamInitialize` and `sceUltQueueDataResourcePoolOptParamInitialize` without their structures; the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.h#L25-L78) defines both as a 16-byte reserved header followed by reserved fields, 128 bytes, with no settings, and [fpPS4](https://github.com/red-prig/fpPS4/blob/5ffbdbbd76b4da6e4b8721743ea491f494b28913/src/ps4_libsceult.pas#L142-L172) keeps them as 128 opaque bytes. That the PS5 structures carry no settings either is assumed from these, not verified - [_sceUltWaitingQueueResourcePoolCreate, _sceUltSemaphoreCreate, _sceUltMutexOptParamInitialize, _sceUltMutexCreate, _sceUltQueueDataResourcePoolCreate and _sceUltQueueCreate](../../../core/libs/prx/libSceUlt/Export.cpp) (libSceUlt) forward to the exports without the underscore; that their argument lists match is inferred from the other underscore Ult exports, not measured From eb0ff1e5de73e01716e9bef786d3eb7124f0dafd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:16:46 +0300 Subject: [PATCH 317/523] fix(audiopropagation): recover diagnostics and report callback abi --- .../AudioPropagation.hpp | 40 +++++++++++++++++++ .../prx/libSceAudioPropagation/Export.cpp | 20 +++++----- docs/dev/debt/libSceAudioPropagation.md | 2 +- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index 602d531cfa..d9351c0489 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -23,6 +23,46 @@ inline constexpr std::size_t PortalParamsSize = 0x60; inline constexpr std::uint32_t PathEntryId = 0x010107d9; inline constexpr std::size_t PathEntrySize = 0x28; +struct SourceInfo; +struct SystemInfo; +struct ReportRegistration; + +struct ReportArguments { + std::uint32_t generalOffset; + std::uint32_t floatingOffset; + void* overflowArguments; + void* savedRegisters; +}; + +struct ReportInfo { + std::uint64_t size; + AudioPropagationHandle handle; + std::uint32_t category; + std::int32_t result; + std::int32_t operation; + std::uint32_t reserved; + const char* function; + const char* format; + ReportArguments arguments; +}; + +using ReportHandler = void (APS5_VABI*)(const ReportInfo* report, void* userData); + +static_assert(sizeof(ReportArguments) == 24); +static_assert(offsetof(ReportArguments, generalOffset) == 0); +static_assert(offsetof(ReportArguments, floatingOffset) == 4); +static_assert(offsetof(ReportArguments, overflowArguments) == 8); +static_assert(offsetof(ReportArguments, savedRegisters) == 16); +static_assert(sizeof(ReportInfo) == 72); +static_assert(offsetof(ReportInfo, size) == 0); +static_assert(offsetof(ReportInfo, handle) == 8); +static_assert(offsetof(ReportInfo, category) == 16); +static_assert(offsetof(ReportInfo, result) == 20); +static_assert(offsetof(ReportInfo, operation) == 24); +static_assert(offsetof(ReportInfo, function) == 32); +static_assert(offsetof(ReportInfo, format) == 40); +static_assert(offsetof(ReportInfo, arguments) == 48); + struct Attribute { std::uint32_t id; std::uint32_t reserved0; diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index 474117fe6f..a5534a4e45 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -315,32 +315,32 @@ std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle sy return 0; } -int APS5_VABI sceAudioPropagationPathGetNumPoints() { +int APS5_VABI sceAudioPropagationPathGetNumPoints(AudioPropagationHandle path, std::uint32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationReportApi() { +int APS5_VABI sceAudioPropagationReportApi(std::uint32_t category, AudioPropagationHandle handle, std::int32_t result, std::int32_t operation, const char* function, const char* format, ...) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationResetAttributes() { +int APS5_VABI sceAudioPropagationResetAttributes(Attribute* attributes, std::uint32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationSourceQueryInfo() { +int APS5_VABI sceAudioPropagationSourceQueryInfo(AudioPropagationHandle source, SourceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationSystemLock() { +int APS5_VABI sceAudioPropagationSystemLock(AudioPropagationHandle system) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationSystemQueryInfo() { +int APS5_VABI sceAudioPropagationSystemQueryInfo(AudioPropagationHandle system, SystemInfo* info, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -395,22 +395,22 @@ int APS5_VABI sceAudioPropagationSystemOptionInit() { return 0; } -int APS5_VABI sceAudioPropagationReportRegisterHandler() { +int APS5_VABI sceAudioPropagationReportRegisterHandler(std::uint32_t category, ReportHandler handler, void* userData, ReportRegistration** registration) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationReportUnregisterHandler() { +int APS5_VABI sceAudioPropagationReportUnregisterHandler(ReportRegistration* registration) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationSystemEnumHandles() { +int APS5_VABI sceAudioPropagationSystemEnumHandles(AudioPropagationHandle* handles, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAudioPropagationSystemUnlock() { +int APS5_VABI sceAudioPropagationSystemUnlock(AudioPropagationHandle system) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 30802ce4c7..8fefea085b 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -8,4 +8,4 @@ - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation, NID `tKSmk2JsMAA`) - `(uint64_t path, const void* data, float gain)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs) and SAROS's call, which passes a handle from `sceAudioPropagationSourceGetAudioPath`, path data and gain 1.0. Path updates are unsupported and every call throws without reading or writing caller memory. The no-propagation model yields no path handles; path allocation, ownership, rendering and console error codes remain unknown. - [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` is unknown; `sceAudioPropagationSourceGetAudioPath` is assumed to output an 8-byte path handle. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. No error code is known, so invalid input throws instead of returning one -- [libSceAudioPropagation firmware exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - `sceAudioPropagationPathGetNumPoints`, `sceAudioPropagationReportApi`, `sceAudioPropagationResetAttributes`, `sceAudioPropagationSourceQueryInfo`, `sceAudioPropagationSystemLock`, `sceAudioPropagationSystemQueryInfo`, `sceAudioPropagationAudioPathInit`, `sceAudioPropagationAudioPathPointInit`, `sceAudioPropagationMaterialInit`, `sceAudioPropagationPortalSettingsInit`, `sceAudioPropagationRayInit`, `sceAudioPropagationSourceRenderInfoInit`, `sceAudioPropagationSourceSetAudioPathsParamInit`, `sceAudioPropagationSystem`, `sceAudioPropagationSystemMemoryInit`, `sceAudioPropagationSystemOptionInit`, `sceAudioPropagationReportRegisterHandler`, `sceAudioPropagationReportUnregisterHandler`, `sceAudioPropagationSystemEnumHandles`, `sceAudioPropagationSystemUnlock` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws +- [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From db3affa05b54989cae354b1bce510dbaaacb7e8a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:17:50 +0300 Subject: [PATCH 318/523] fix(audiopropagation): match audio path lookup errors --- core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp | 4 ++++ core/libs/prx/libSceAudioPropagation/Export.cpp | 8 +++++--- core/libs/tests/GuestAudioPropagation.cpp | 6 +++++- docs/dev/debt/libSceAudioPropagation.md | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index d9351c0489..6c5f9232f0 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -9,6 +9,10 @@ namespace AudioPropagation { +inline constexpr std::int32_t ErrorInvalidArgument = static_cast(0x8A700001); +inline constexpr std::int32_t ErrorInvalidHandle = static_cast(0x8A700002); +inline constexpr std::int32_t ErrorNullPointer = static_cast(0x8A700003); + inline constexpr std::uint32_t MaterialId = 0x010107d1; inline constexpr std::size_t MaterialSize = 0x40; inline constexpr std::uint32_t SystemMemoryId = 0x010107d4; diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index a5534a4e45..1a00395331 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -258,9 +258,11 @@ std::int32_t APS5_VABI sceAudioPropagationSourceGetAudioPathCount(AudioPropagati std::int32_t APS5_VABI sceAudioPropagationSourceGetAudioPath(AudioPropagationHandle source, std::uint32_t index, AudioPropagationHandle* outPath) { auto& state = registry(); std::lock_guard lock(state.mutex); - requireObject(state, source, Kind::Source, __func__); - static_cast(outPath); - throw std::invalid_argument(std::string(__func__) + ": index " + std::to_string(index) + " out of range (0 audio paths)"); + const auto found = state.objects.find(source); + if (found == state.objects.end() || found->second.kind != Kind::Source || !liveSystem(state, found->second.system)) return ErrorInvalidHandle; + if (!outPath) return ErrorNullPointer; + static_cast(index); + return ErrorInvalidArgument; } std::int32_t APS5_VABI sceAudioPropagationSourceGetRays(AudioPropagationHandle source, void* rays, std::uint32_t* count) { diff --git a/core/libs/tests/GuestAudioPropagation.cpp b/core/libs/tests/GuestAudioPropagation.cpp index c4d112d342..80cb31d799 100644 --- a/core/libs/tests/GuestAudioPropagation.cpp +++ b/core/libs/tests/GuestAudioPropagation.cpp @@ -122,7 +122,11 @@ int main() { std::uint32_t count = 7; Require(sceAudioPropagationSourceGetAudioPathCount(source, &count) == 0 && count == 0, "source reports audio paths"); AudioPropagationHandle path = 0; - RequireThrows([&] { sceAudioPropagationSourceGetAudioPath(source, 0, &path); }, "audio path without paths"); + path = 0x1234; + Require(sceAudioPropagationSourceGetAudioPath(source, 0, &path) == AudioPropagation::ErrorInvalidArgument && path == 0x1234, "audio path without paths"); + Require(sceAudioPropagationSourceGetAudioPath(source, UINT32_MAX, &path) == AudioPropagation::ErrorInvalidArgument && path == 0x1234, "audio path index range"); + Require(sceAudioPropagationSourceGetAudioPath(source, 0, nullptr) == AudioPropagation::ErrorNullPointer, "audio path null output"); + Require(sceAudioPropagationSourceGetAudioPath(0, 0, nullptr) == AudioPropagation::ErrorInvalidHandle, "audio path handle precedence"); std::uint8_t rays[AudioPropagation::RaySize * 2]{}; count = 7; diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 8fefea085b..3e3dd69e2a 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -7,5 +7,5 @@ ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation, NID `tKSmk2JsMAA`) - `(uint64_t path, const void* data, float gain)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs) and SAROS's call, which passes a handle from `sceAudioPropagationSourceGetAudioPath`, path data and gain 1.0. Path updates are unsupported and every call throws without reading or writing caller memory. The no-propagation model yields no path handles; path allocation, ownership, rendering and console error codes remain unknown. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` is unknown; `sceAudioPropagationSourceGetAudioPath` is assumed to output an 8-byte path handle. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. No error code is known, so invalid input throws instead of returning one +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` is unknown; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. Other functions still throw for invalid input; their native validation precedence is not yet implemented - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From c0e20df906bb22a241e16ba8191254c3629e0423 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:20:35 +0300 Subject: [PATCH 319/523] fix(audiopropagation): reject invalid path calculation inputs --- .../prx/libSceAudioPropagation/AudioPropagation.hpp | 2 +- core/libs/prx/libSceAudioPropagation/Export.cpp | 13 ++++++------- core/libs/tests/GuestAudioPropagation.cpp | 8 ++++++-- docs/dev/debt/libSceAudioPropagation.md | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index 6c5f9232f0..538b6a867f 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -123,7 +123,7 @@ std::int32_t APS5_VABI sceAudioPropagationSourceSetAttributes(AudioPropagationHa std::int32_t APS5_VABI sceAudioPropagationSourceGetAudioPathCount(AudioPropagationHandle source, std::uint32_t* count); std::int32_t APS5_VABI sceAudioPropagationSourceGetAudioPath(AudioPropagationHandle source, std::uint32_t index, AudioPropagationHandle* outPath); std::int32_t APS5_VABI sceAudioPropagationSourceGetRays(AudioPropagationHandle source, void* rays, std::uint32_t* count); -std::int32_t APS5_VABI sceAudioPropagationSourceCalculateAudioPaths(AudioPropagationHandle source, const void* rays, std::uint32_t rayCount, std::uint32_t flags, void* paths, std::uint32_t pathCount); +std::int32_t APS5_VABI sceAudioPropagationSourceCalculateAudioPaths(AudioPropagationHandle source, const void* rays, std::uint32_t rayCount, std::uint32_t reflectionOrder, void* paths, std::uint32_t pathCount); std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPaths(AudioPropagationHandle source, const void* entries, std::uint32_t count); std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHandle path, const void* data, float gain); std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle system, const AudioPropagation::RenderInfo* infos, std::uint32_t count); diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index 1a00395331..d1af51030e 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -274,15 +274,14 @@ std::int32_t APS5_VABI sceAudioPropagationSourceGetRays(AudioPropagationHandle s return 0; } -std::int32_t APS5_VABI sceAudioPropagationSourceCalculateAudioPaths(AudioPropagationHandle source, const void* rays, std::uint32_t rayCount, std::uint32_t flags, void* paths, std::uint32_t pathCount) { +std::int32_t APS5_VABI sceAudioPropagationSourceCalculateAudioPaths(AudioPropagationHandle source, const void* rays, std::uint32_t rayCount, std::uint32_t reflectionOrder, void* paths, std::uint32_t pathCount) { auto& state = registry(); std::lock_guard lock(state.mutex); - requireObject(state, source, Kind::Source, __func__); - static_cast(rays); - static_cast(flags); - static_cast(paths); - requireNoPropagation(rayCount == 0 && pathCount == 0, __func__); - return 0; + const auto found = state.objects.find(source); + if (found == state.objects.end() || found->second.kind != Kind::Source || !liveSystem(state, found->second.system)) return ErrorInvalidHandle; + if (!rays || !paths) return ErrorNullPointer; + if (rayCount != 6 || reflectionOrder == 0 || pathCount == 0) return ErrorInvalidArgument; + throw std::runtime_error(__func__); } std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPaths(AudioPropagationHandle source, const void* entries, std::uint32_t count) { diff --git a/core/libs/tests/GuestAudioPropagation.cpp b/core/libs/tests/GuestAudioPropagation.cpp index 80cb31d799..ebb42f21d9 100644 --- a/core/libs/tests/GuestAudioPropagation.cpp +++ b/core/libs/tests/GuestAudioPropagation.cpp @@ -136,8 +136,12 @@ int main() { RequireThrows([&] { sceAudioPropagationSourceGetRays(source, rays, nullptr); }, "rays without count"); Require(sceAudioPropagationSystemSetRays(system, rays, 0) == 0, "empty ray results failed"); RequireThrows([&] { sceAudioPropagationSystemSetRays(system, rays, 1); }, "unrequested ray results"); - Require(sceAudioPropagationSourceCalculateAudioPaths(source, rays, 0, 1, nullptr, 0) == 0, "empty path calculation failed"); - RequireThrows([&] { sceAudioPropagationSourceCalculateAudioPaths(source, rays, 1, 1, nullptr, 0); }, "path calculation with rays"); + Require(sceAudioPropagationSourceCalculateAudioPaths(source, rays, 0, 1, nullptr, 0) == AudioPropagation::ErrorNullPointer, "path calculation null output"); + Require(sceAudioPropagationSourceCalculateAudioPaths(0, nullptr, 0, 0, nullptr, 0) == AudioPropagation::ErrorInvalidHandle, "path calculation handle precedence"); + Require(sceAudioPropagationSourceCalculateAudioPaths(source, rays, 0, 1, rays, 6) == AudioPropagation::ErrorInvalidArgument, "path calculation empty rays"); + Require(sceAudioPropagationSourceCalculateAudioPaths(source, rays, 6, 0, rays, 6) == AudioPropagation::ErrorInvalidArgument, "path calculation zero reflection order"); + Require(sceAudioPropagationSourceCalculateAudioPaths(source, rays, 6, 1, rays, 0) == AudioPropagation::ErrorInvalidArgument, "path calculation zero output capacity"); + RequireThrows([&] { sceAudioPropagationSourceCalculateAudioPaths(source, rays, 6, 1, rays, 6); }, "unsupported path calculation"); Require(sceAudioPropagationSourceSetAudioPaths(source, nullptr, 0) == 0, "empty audio paths failed"); RequireThrows([&] { sceAudioPropagationSourceSetAudioPaths(source, rays, 1); }, "set audio paths"); diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 3e3dd69e2a..b1d100771e 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -2,10 +2,10 @@ ### Silent stubs -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) implements only the no-acoustic-propagation path: the system keeps a 16-byte header in the title's CPU memory (no GPU memory), rooms, portals, sources and materials are tracked handles, attributes are validated and have no effect, no rays are requested and sources report no audio paths. Ray results, path calculation and audio paths are accepted only when empty and throw otherwise; `sceAudioPropagationSourceRender` preserves each destination mix because there are no path contributions, following the additive-output contract in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs); console behaviour has not been measured. It adds no dry signal. Objects still alive when their system is destroyed become unusable, and their later destroy or unregister is a no-op: PPSA21567 releases its reference-counted room, portal and source owners just before `sceAudioPropagationSystemDestroy`, so the order for every object is not proven. Occlusion, reflections and reverb from level geometry are absent; in-game behaviour remains unverified. +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) implements only the no-acoustic-propagation path: the system keeps a 16-byte header in the title's CPU memory (no GPU memory), rooms, portals, sources and materials are tracked handles, attributes are validated and have no effect, no rays are requested and sources report no audio paths. Ray results and audio paths are accepted only when empty and throw otherwise; path calculation returns the confirmed initial validation errors and throws for unsupported propagation; `sceAudioPropagationSourceRender` preserves each destination mix because there are no path contributions, following the additive-output contract in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs); console behaviour has not been measured. It adds no dry signal. Objects still alive when their system is destroyed become unusable, and their later destroy or unregister is a no-op: PPSA21567 releases its reference-counted room, portal and source owners just before `sceAudioPropagationSystemDestroy`, so the order for every object is not proven. Occlusion, reflections and reverb from level geometry are absent; in-game behaviour remains unverified. ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation, NID `tKSmk2JsMAA`) - `(uint64_t path, const void* data, float gain)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs) and SAROS's call, which passes a handle from `sceAudioPropagationSourceGetAudioPath`, path data and gain 1.0. Path updates are unsupported and every call throws without reading or writing caller memory. The no-propagation model yields no path handles; path allocation, ownership, rendering and console error codes remain unknown. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` is unknown; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. Other functions still throw for invalid input; their native validation precedence is not yet implemented +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` selects reflection order; descriptor validation, ray ownership and path generation remain unimplemented; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. Other functions still throw for invalid input; their native validation precedence is not yet implemented - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From 3251852138eb1027ca51a634336cb1a24671ac39 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:22:08 +0300 Subject: [PATCH 320/523] fix(rudp): recover poll signatures and event layout --- core/libs/SceTypes.hpp | 10 ++++++++++ core/libs/prx/libSceRudp/Export.cpp | 12 +++--------- docs/dev/debt/libSceRudp.md | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index f7b002c4d9..978123ba23 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1929,6 +1929,16 @@ struct PlayGoDialogResult { std::int32_t reserved[9]; }; +struct RudpPollEvent { + std::int32_t contextId; + std::uint16_t events; + std::uint16_t returnedEvents; +}; + +static_assert(sizeof(RudpPollEvent) == 8); +static_assert(offsetof(RudpPollEvent, events) == 4); +static_assert(offsetof(RudpPollEvent, returnedEvents) == 6); + using RudpEventHandler = void (*)(int ctx_id, int event_id, int error_code, void* arg); struct SystemServiceStatus { diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 923b4e9a76..2b17a8740b 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -89,16 +89,10 @@ GuestRudpEventHandler g_handler = nullptr; void* g_handler_arg = nullptr; struct RudpPoll { - uint32_t capacity; + std::size_t capacity; std::map entries; }; -struct RudpPollEvent { - int ctxId; - uint16_t reqEvents; - uint16_t rtnEvents; -}; -static_assert(sizeof(RudpPollEvent) == 8); int g_next_poll = 1; std::map g_polls; @@ -386,7 +380,7 @@ int APS5_VABI sceRudpGetMaxSegmentSize(uint16_t* size) { return 0; } -int APS5_VABI sceRudpPollCreate(uint32_t size) { +int APS5_VABI sceRudpPollCreate(std::size_t size) { std::lock_guard lk(g_mutex); if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; @@ -440,7 +434,7 @@ int APS5_VABI sceRudpPollControl(int pollId, int op, int ctxId, uint16_t events) return 0; } -int APS5_VABI sceRudpPollWait(int pollId, RudpPollEvent* events, uint32_t eventLen, uint64_t timeout) { +int APS5_VABI sceRudpPollWait(int pollId, RudpPollEvent* events, std::size_t eventLen, uint64_t timeout) { std::unique_lock lk(g_mutex); if (!g_inited) { return RUDP_ERROR_NOT_INITIALIZED; diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index d6337281b5..48f8b5bc68 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -11,5 +11,5 @@ - [sceRudpGetStatus](../../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`. Measured on a PS5 (firmware 13.60): the status is 0xF8 bytes; before `sceRudpInit` every call returns `RUDP_ERROR_NOT_INITIALIZED`, and after it a null pointer or a byte count of 0 or above 0xF8 returns `RUDP_ERROR_INVALID_ARGUMENT`. The layout matches `SceRudpStatus` in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp) with `currentContexts` at 0x64 and `allocs`, `frees`, `memCurrent` and `memPeak` at 0xC8 to 0xD4. Only `currentContexts` is reported: the traffic counters stay 0 because nothing is sent, and the allocator counters stay 0 because the library does not allocate from the title's pool (the console reports 6 allocations and 0x1260 bytes right after `sceRudpInit`, and 4 more allocations and 0x3F0 bytes per context). The console also writes `currentContexts` and the allocator counters when the byte count is smaller than their offset; only the requested bytes are written here. - [sceRudpGetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - measured on a PS5 (firmware 13.60) in #3073: before `sceRudpInit` it returns `RUDP_ERROR_NOT_INITIALIZED` and leaves the value untouched, also for a null pointer; after it a null pointer returns `RUDP_ERROR_INVALID_ARGUMENT` and a valid one gets 1410 with result 0. The library keeps no configurable segment size (there is no `sceRudpSetMaxSegmentSize`), so 1410 is always reported. - [sceRudpGetOption, sceRudpSetOption](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - full PS5 option contracts are unverified; defaults and validation follow [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/c86ac732bc811057e690bed280c5076bdf95385e/vita3k/modules/SceRudp/SceLibRudp.cpp). Options 12 and 13 have unknown meanings; last-error option 14 is not maintained. Their setters store all 32 bits per context and return 0 for valid four-byte buffers, preserving acceptance on `main`; their getters throw. Static inspection of PPSA06888 v01.001.008's bundled Unity transport shows its context-set constructor configuring IDs 2, 3, 5 and 6; runtime usage and other paths remain unverified. The plugin wrappers pass four-byte values. No context can connect, so stored settings cannot affect traffic. Closes when: PS5 option contracts are verified and the remaining behavior is implemented. -- [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. The signatures, the 8-byte `SceRudpPollEvent`, the operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once +- [sceRudpPollCreate, sceRudpPollControl, sceRudpPollWait, sceRudpPollDestroy](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - unverified on a PS5; imported by PPSA31327 and not called before the main menu. Signatures and the 8-byte event layout are recovered from the supplied module. The operations (1 add, 2 modify, 3 remove) and the argument checks follow the PS Vita library in [Vita3K](https://github.com/AndnixSH/Vita3K-PS-Vita-OS-Fork/blob/master/vita3k/modules/SceRudp/SceLibRudp.cpp), and the error codes match [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellRudp.h). Vita3K leaves `sceRudpPollDestroy` unimplemented, so `int sceRudpPollDestroy(int pollId)` and its checks are inferred. No context can connect, so no event is ever reported: `sceRudpPollWait` returns 0 when its timeout elapses, also for an empty poll, where Vita3K returns at once - [sceRudpAccept, sceRudpFlush, sceRudpGetLocalInfo, sceRudpListen, sceRudpPollCancel and sceRudpSetMaxSegmentSize](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceRudp.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim. From 08fa5d3578dd8a84c1d168ed0806582c2bcaf994 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:24:09 +0300 Subject: [PATCH 321/523] fix(rudp): validate activation and reject unsupported transport --- core/libs/prx/libSceRudp/Export.cpp | 8 ++++++-- core/libs/tests/GuestOfflineImports.cpp | 6 +++--- core/libs/tests/GuestRudpContextLifecycle.cpp | 17 ++++++++++++++--- docs/dev/debt/libSceRudp.md | 2 +- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 2b17a8740b..68afe5a022 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -126,8 +126,12 @@ int APS5_VABI sceRudpInit_nid_postfix(void* mem_pool, int mem_pool_size) { return 0; } -int APS5_VABI sceRudpActivate() { - return 0; +int APS5_VABI sceRudpActivate(std::int32_t contextId, const void* address, std::uint32_t addressLength) { + std::lock_guard lock(g_mutex); + if (!g_inited) return RUDP_ERROR_NOT_INITIALIZED; + if (contextId < 0 || !address) return RUDP_ERROR_INVALID_ARGUMENT; + static_cast(addressLength); + throw std::runtime_error(__func__); } int APS5_VABI sceRudpGetStatus(void* status, std::size_t size) { diff --git a/core/libs/tests/GuestOfflineImports.cpp b/core/libs/tests/GuestOfflineImports.cpp index 32dcdd9acf..fc8be8f7f4 100644 --- a/core/libs/tests/GuestOfflineImports.cpp +++ b/core/libs/tests/GuestOfflineImports.cpp @@ -24,7 +24,7 @@ int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfo(); int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfo(); int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo(); int APS5_VABI sceRudpInit_nid_postfix(void*, int); -int APS5_VABI sceRudpActivate(); +int APS5_VABI sceRudpActivate(std::int32_t, const void*, std::uint32_t); int APS5_VABI sceRudpGetStatus(void*, std::size_t); int APS5_VABI sceRudpEnd(); } @@ -88,7 +88,7 @@ int main() { Require(sceNpEntitlementAccessRequestServiceEntitlementInfo() == signedOut); Require(sceNpEntitlementAccessPollServiceEntitlementInfo() == signedOut); - Require(sceRudpActivate() == 0); + Require(sceRudpActivate(-1, nullptr, 0) == static_cast(0x80770001)); std::array status; status.fill(0x5a); const auto originalStatus = status; @@ -97,7 +97,7 @@ int main() { Require(status == originalStatus); alignas(8) static unsigned char rudpPool[0xC58]; Require(sceRudpInit_nid_postfix(rudpPool, sizeof(rudpPool)) == 0); - Require(sceRudpActivate() == 0); + Require(sceRudpActivate(-1, nullptr, 0) == static_cast(0x80770004)); Require(sceRudpGetStatus(status.data(), status.size()) == 0); for (unsigned char byte : status) Require(byte == 0); Require(sceRudpGetStatus(nullptr, 0) == static_cast(0x80770004)); diff --git a/core/libs/tests/GuestRudpContextLifecycle.cpp b/core/libs/tests/GuestRudpContextLifecycle.cpp index c3045181fb..51a119e0d8 100644 --- a/core/libs/tests/GuestRudpContextLifecycle.cpp +++ b/core/libs/tests/GuestRudpContextLifecycle.cpp @@ -13,7 +13,7 @@ extern "C" { using GuestRudpEventHandler = void(APS5_VABI*)(int, int, int, void*); int APS5_VABI sceRudpInit_nid_postfix(void*, int); int APS5_VABI sceRudpEnd(); -int APS5_VABI sceRudpActivate(); +int APS5_VABI sceRudpActivate(std::int32_t, const void*, std::uint32_t); int APS5_VABI sceRudpEnableInternalIOThread(std::uint32_t, std::uint32_t); int APS5_VABI sceRudpSetEventHandler(GuestRudpEventHandler, void*); int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler, void*, int*); @@ -113,7 +113,7 @@ int main() { constexpr int connReset = static_cast(0x80770009u); alignas(16) static unsigned char pool[0x10000]; - Require(sceRudpActivate() == 0); + Require(sceRudpActivate(-1, nullptr, 0) == static_cast(0x80770001)); Require(sceRudpEnableInternalIOThread(0, 0) == notInitialized); @@ -143,7 +143,7 @@ int main() { Require(sceRudpInit_nid_postfix(pool, sizeof(pool)) == 0); - Require(sceRudpActivate() == 0); + Require(sceRudpActivate(-1, nullptr, 0) == static_cast(0x80770004)); Require(sceRudpEnableInternalIOThread(0, 0) == 0); Require(sceRudpEnableInternalIOThread(1024, 1) == 0); @@ -170,6 +170,17 @@ int main() { Require(sceRudpCreateContext(Handler, nullptr, &ctx) == 0); Require(ctx >= 0); + Require(sceRudpActivate(ctx, nullptr, 0) == static_cast(0x80770004)); + unsigned char address[34]{}; + Require(sceRudpActivate(-1, address, sizeof(address)) == static_cast(0x80770004)); + bool activationUnsupported = false; + try { + sceRudpActivate(ctx, address, sizeof(address)); + } catch (const std::runtime_error&) { + activationUnsupported = true; + } + Require(activationUnsupported); + Require(sceRudpBind(ctx, 0, 0, 0) == 0); Require(sceRudpSetOption(ctx, 0, nullptr, 0) == invalidOption); diff --git a/docs/dev/debt/libSceRudp.md b/docs/dev/debt/libSceRudp.md index 48f8b5bc68..6b667bd48d 100644 --- a/docs/dev/debt/libSceRudp.md +++ b/docs/dev/debt/libSceRudp.md @@ -4,7 +4,7 @@ - [sceRudpProcessEvents, sceRudpNetFlush and sceRudpNetReceived](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host, and every `sceRudpWrite`, `sceRudpRead` and `sceRudpGetStatus` call here reports a reset or idle context without queueing data, so no context ever holds pending data to process, send or receive; the three calls return success after `sceRudpInit` and `RUDP_ERROR_NOT_INITIALIZED` before it, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) returns OK from its stubs. What the console's `sceRudpNetReceived` returns is unverified. PPSA03490 calls `sceRudpProcessEvents(0)` followed by `sceRudpNetFlush()` from its P2P service thread and ignores the first result. - [sceRudpEnableInternalIOThread2](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) takes `(uint32_t stack_size, uint32_t priority, uint64_t affinity)`, taken from the managed `SceRudp.Rudp.EnableInternalIOThread(uint stackSize, uint priority, ulong affinity)` in PPSA06888's Unity metadata; the first two match `sceRudpEnableInternalIOThread`. It returns success after `sceRudpInit` and `RUDP_ERROR_NOT_INITIALIZED` before it, as `sceRudpEnableInternalIOThread` does, without starting any thread. Whether the console reports other errors, and whether it applies the affinity, is unverified. -- [sceRudpActivate](../../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host to activate, so it returns success without doing anything, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) does for its own `sceRudpActivate`. PPSA01325 calls it with no arguments during start-up; what the console activates, and whether it reports an error before `sceRudpInit`, is unverified +- [sceRudpActivate](../../../core/libs/prx/libSceRudp/Export.cpp) takes a context ID, socket address and 32-bit address length. Initialization and initial argument checks match the native implementation; transport mode checks, address conversion and connection activation remain unimplemented and throw. ### Unknown function info From 932e8a6c75845b1677d1eedfd92f2b50a6f4b723 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:24:18 +0300 Subject: [PATCH 322/523] fix(npwebapi2): restore push context and trace signatures --- core/libs/prx/libSceNpWebApi2/Export.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceNpWebApi2/Export.cpp b/core/libs/prx/libSceNpWebApi2/Export.cpp index 125c7fb846..9a2ebe62dd 100644 --- a/core/libs/prx/libSceNpWebApi2/Export.cpp +++ b/core/libs/prx/libSceNpWebApi2/Export.cpp @@ -12,6 +12,12 @@ static constexpr int SCE_NP_WEBAPI2_ERROR_INVALID_ARGUMENT = static_cast(0x static constexpr int SCE_NP_WEBAPI2_ERROR_UNAVAILABLE = static_cast(0x80553406); static std::atomic g_nextHandle{1}; +struct NpWebApi2PushContextId { + char value[37]; +}; + +static_assert(sizeof(NpWebApi2PushContextId) == 37); + struct NpWebApi2MemoryPoolStats { size_t pool_size; size_t max_inuse_size; @@ -43,8 +49,8 @@ int APS5_VABI sceNpWebApi2AddHttpRequestHeader(int64_t request_id, const char* f return 0; } -int APS5_VABI sceNpWebApi2AddWebTraceTag(int64_t request_id, const char* value) { - (void)request_id; +int APS5_VABI sceNpWebApi2AddWebTraceTag(std::int64_t requestId, const char* value) { + (void)requestId; if (!value) return SCE_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; return 0; } @@ -171,11 +177,11 @@ int APS5_VABI sceNpWebApi2Terminate(int lib_ctx_id) { return 0; } -int APS5_VABI sceNpWebApi2PushEventCreatePushContext() { +int APS5_VABI sceNpWebApi2PushEventCreatePushContext(std::int32_t userContextId, NpWebApi2PushContextId* pushContextId) { return SCE_NP_WEBAPI2_ERROR_UNAVAILABLE; } -int APS5_VABI sceNpWebApi2PushEventDeleteFilter() { +int APS5_VABI sceNpWebApi2PushEventDeleteFilter(std::int32_t libraryContextId, std::int32_t filterId) { return 0; } @@ -191,15 +197,15 @@ int APS5_VABI sceNpWebApi2PushEventRegisterPushContextCallback() { return g_nextHandle.fetch_add(1, std::memory_order_relaxed); } -int APS5_VABI sceNpWebApi2PushEventStartPushContextCallback() { +int APS5_VABI sceNpWebApi2PushEventStartPushContextCallback(std::int32_t userContextId, const NpWebApi2PushContextId* pushContextId) { return 0; } -int APS5_VABI sceNpWebApi2PushEventUnregisterCallback() { +int APS5_VABI sceNpWebApi2PushEventUnregisterCallback(std::int32_t userContextId, std::int32_t callbackId) { return 0; } -int APS5_VABI sceNpWebApi2PushEventUnregisterPushContextCallback() { +int APS5_VABI sceNpWebApi2PushEventUnregisterPushContextCallback(std::int32_t userContextId, std::int32_t callbackId) { return 0; } From 4c436012787dbc9e9efb50a12fbbcbca2e56d721 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:25:38 +0300 Subject: [PATCH 323/523] fix(vrtracker2): restore named export signatures and play area layouts --- core/libs/prx/libSceVrTracker2/Export.cpp | 43 +++++++++++++++++------ core/libs/tests/GuestVrTracker2.cpp | 5 +-- docs/dev/debt/libSceVrTracker2.md | 2 +- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index e323b3c719..b850d89f71 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -5,6 +5,29 @@ #include #include "prx/libc/include/General.hpp" +struct VrTracker2InitializeParameter; +struct VrTracker2ResultParameter; +struct VrTracker2Result; +struct VrTracker2CoordinateLocation; + +struct VrTracker2BoundaryGeometry { + std::uint32_t size; + std::uint32_t pointCount; + float points[256][3]; +}; + +struct VrTracker2OrientedBoundingBox { + std::uint32_t size; + float transform[16]; + float extents[3]; +}; + +static_assert(sizeof(VrTracker2BoundaryGeometry) == 3080); +static_assert(offsetof(VrTracker2BoundaryGeometry, points) == 8); +static_assert(sizeof(VrTracker2OrientedBoundingBox) == 80); +static_assert(offsetof(VrTracker2OrientedBoundingBox, transform) == 4); +static_assert(offsetof(VrTracker2OrientedBoundingBox, extents) == 68); + namespace { constexpr std::int32_t SCE_OK = 0; @@ -25,39 +48,39 @@ std::int32_t APS5_VABI sceVrTracker2Finalize() { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2GetCoordinateSystem() { +std::int32_t APS5_VABI sceVrTracker2GetCoordinateSystem(std::uint32_t* coordinateSystem) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2GetPlayAreaBoundaryGeometry() { +std::int32_t APS5_VABI sceVrTracker2GetPlayAreaBoundaryGeometry(VrTracker2BoundaryGeometry* geometry) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2GetPlayAreaOrientedBoundingBox() { +std::int32_t APS5_VABI sceVrTracker2GetPlayAreaOrientedBoundingBox(VrTracker2OrientedBoundingBox* box) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2GetResult() { +std::int32_t APS5_VABI sceVrTracker2GetResult(const VrTracker2ResultParameter* parameter, VrTracker2Result* result) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2LocateCoordinateSystem() { +std::int32_t APS5_VABI sceVrTracker2LocateCoordinateSystem(std::uint32_t coordinateSystem, VrTracker2CoordinateLocation* location) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2RegisterDevice() { +std::int32_t APS5_VABI sceVrTracker2RegisterDevice(std::uint32_t deviceType, std::int32_t deviceHandle) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2ResetLocalCoordinate() { +std::int32_t APS5_VABI sceVrTracker2ResetLocalCoordinate(std::int32_t deviceHandle) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2SetCoordinateSystem() { +std::int32_t APS5_VABI sceVrTracker2SetCoordinateSystem(std::uint32_t coordinateSystem) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2UnregisterDevice() { +std::int32_t APS5_VABI sceVrTracker2UnregisterDevice(std::int32_t deviceHandle) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } @@ -75,7 +98,7 @@ std::int32_t APS5_VABI sceVrTracker2QueryMemory(const void* parameter, void* req return SCE_OK; } -std::int32_t APS5_VABI sceVrTracker2Initialize() { +std::int32_t APS5_VABI sceVrTracker2Initialize(const VrTracker2InitializeParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestVrTracker2.cpp b/core/libs/tests/GuestVrTracker2.cpp index f8d5899a66..7b43bc31d0 100644 --- a/core/libs/tests/GuestVrTracker2.cpp +++ b/core/libs/tests/GuestVrTracker2.cpp @@ -17,7 +17,8 @@ std::int32_t APS5_VABI VrTracker2Accept(); std::int32_t APS5_VABI VrTracker2FillDefaults(void* output); std::int32_t APS5_VABI VrTracker2ClearWord(void* output); std::int32_t APS5_VABI sceVrTracker2QueryMemory(const void* parameter, void* requirement); -std::int32_t APS5_VABI sceVrTracker2Initialize(); +struct VrTracker2InitializeParameter; +std::int32_t APS5_VABI sceVrTracker2Initialize(const VrTracker2InitializeParameter* parameter); } namespace { @@ -110,6 +111,6 @@ int main() { parameter[0] = 0x21; Require(sceVrTracker2QueryMemory(parameter.data(), requirement.data()) == InvalidArgument, "query size"); - Require(Throws([] { sceVrTracker2Initialize(); }), "unknown export"); + Require(Throws([] { sceVrTracker2Initialize(nullptr); }), "unknown export"); return 0; } diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 42938b9e61..0aa0c7b4d8 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes, so its exports answer as the console's module does before initialization, measured on a retail PS5 (firmware 13.60) with no PS VR2 headset by calling each of the 115 NIDs with null arguments, zeroed buffers and buffers filled with 0xAA and 0x01: 99 return 0x8A740001 and write nothing; `XknY0IDcOwU` and `ark13GkiskA` return 0x8A740006 for a null first argument and otherwise clear its first 24 and 16 bytes and return 0x8A740001; `-YvjOUph8B0` returns 0x8A740006 for a null second argument and otherwise 0x8A740001; `QyZvFyf-QEk` and `fTYZOZepkqE` return 0 and write nothing; `TZIUIk9id3c` writes 0xC000, 0, 0x40 and 0x40 as `uint32`, `uint32`, `uint16` and `uint16` at offsets 4 to 15 of its first argument and returns 0; `IB0qcDVu8vU` clears the `uint32` at its first argument and returns 0. The last two crash on the console for a null first argument and throw here. Signatures and what the tracker returns once initialized are unknown; the twelve exports with aerolib names keep them and the others carry this library's own names +- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes, so its exports answer as the console's module does before initialization, measured on a retail PS5 (firmware 13.60) with no PS VR2 headset by calling each of the 115 NIDs with null arguments, zeroed buffers and buffers filled with 0xAA and 0x01: 99 return 0x8A740001 and write nothing; `XknY0IDcOwU` and `ark13GkiskA` return 0x8A740006 for a null first argument and otherwise clear its first 24 and 16 bytes and return 0x8A740001; `-YvjOUph8B0` returns 0x8A740006 for a null second argument and otherwise 0x8A740001; `QyZvFyf-QEk` and `fTYZOZepkqE` return 0 and write nothing; `TZIUIk9id3c` writes 0xC000, 0, 0x40 and 0x40 as `uint32`, `uint32`, `uint16` and `uint16` at offsets 4 to 15 of its first argument and returns 0; `IB0qcDVu8vU` clears the `uint32` at its first argument and returns 0. The last two crash on the console for a null first argument and throw here. The named export signatures and play-area output layouts are recovered; initialization, result parameter and result fields still need reconstruction. What the tracker returns once initialized is not implemented; the twelve exports with aerolib names keep them and the others carry this library's own names ### Unknown function info From a24a5524b0e191d97da4affc58b7b857366d14d8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:26:39 +0300 Subject: [PATCH 324/523] fix(tools): pass duplicate export patterns through stdin --- tools/tests/test_check_conventions_comments.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/tests/test_check_conventions_comments.py b/tools/tests/test_check_conventions_comments.py index f0d3d223fb..c86fcdd05a 100644 --- a/tools/tests/test_check_conventions_comments.py +++ b/tools/tests/test_check_conventions_comments.py @@ -706,6 +706,19 @@ class RepositoryChecksTests(unittest.TestCase): check.code() self.assertEqual(check.findings, [("comment", name, 2, "")]) + def test_large_export_set_checks_duplicates_without_command_line_limit(self): + first = self.root / "core/libs/prx/libSceA/Export.cpp" + first.parent.mkdir(parents=True) + first.write_text("int APS5_VABI sceLargeExportSet1999(int value) { return value; }\n", encoding="utf-8") + self.commit() + second = self.root / "core/libs/prx/libSceB/Export.cpp" + second.parent.mkdir(parents=True) + second.write_text("".join(f"int APS5_VABI sceLargeExportSet{index}(int value) {{ return value; }}\n" for index in range(2000)), encoding="utf-8") + self.commit() + check = Check("HEAD~1", "HEAD") + check.code() + self.assertEqual(check.findings, [("duplicate-export", "core/libs/prx/libSceB/Export.cpp", 2000, "sceLargeExportSet1999 is also in libSceA")]) + def test_checks_from_a_subdirectory_see_the_whole_repository(self): definition = "int APS5_VABI sceFooBar(int a, int b) { return a + b; }\n" first = self.root / "core/libs/prx/libSceA/Export.cpp" From e2f139c5499da52968e8a395b1a8960c833887d4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:28:21 +0300 Subject: [PATCH 325/523] fix(s3da): restore port and transfer signatures --- core/libs/prx/libSceS3da/Export.cpp | 10 ++++++---- docs/dev/debt/libSceS3da.md | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceS3da/Export.cpp b/core/libs/prx/libSceS3da/Export.cpp index 34b8330de5..79ca9b7758 100644 --- a/core/libs/prx/libSceS3da/Export.cpp +++ b/core/libs/prx/libSceS3da/Export.cpp @@ -3,24 +3,26 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct S3daInfo; + extern "C" { -int APS5_VABI sceS3daClose() { +int APS5_VABI sceS3daClose(std::uint32_t port) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceS3daGetInfo() { +int APS5_VABI sceS3daGetInfo(std::uint32_t port, S3daInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceS3daOpen() { +int APS5_VABI sceS3daOpen(std::uint32_t port) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceS3daSendData() { +int APS5_VABI sceS3daSendData(std::uint32_t port, const void* data, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceS3da.md b/docs/dev/debt/libSceS3da.md index c941424735..cc6345ecaa 100644 --- a/docs/dev/debt/libSceS3da.md +++ b/docs/dev/debt/libSceS3da.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceS3daClose, sceS3daGetInfo, sceS3daOpen and sceS3daSendData](../../../core/libs/prx/libSceS3da/Export.cpp) (libSceS3da) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceS3da.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [sceS3daClose, sceS3daGetInfo, sceS3daOpen and sceS3daSendData](../../../core/libs/prx/libSceS3da/Export.cpp) remain unimplemented. Port arguments and transfer buffer/length are recovered from the native module. `GetInfo` forwards its output pointer to a kernel ioctl without accessing its fields; the information layout requires the kernel handler or a caller that interprets its output. From bbadd9bda400b2e0890b5842f70d32343faa5124 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:28:39 +0300 Subject: [PATCH 326/523] fix(shelluiutil): recover launch and output buffer signatures --- core/libs/prx/libSceShellUIUtil/Export.cpp | 8 +++++--- docs/dev/debt/libSceShellUIUtil.md | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceShellUIUtil/Export.cpp b/core/libs/prx/libSceShellUIUtil/Export.cpp index 4ff433cad7..2837093a4f 100644 --- a/core/libs/prx/libSceShellUIUtil/Export.cpp +++ b/core/libs/prx/libSceShellUIUtil/Export.cpp @@ -3,6 +3,8 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct ShellUIUtilLaunchParameter; + extern "C" { int APS5_VABI sceShellUIUtilCreateTaskToken() { @@ -10,7 +12,7 @@ int APS5_VABI sceShellUIUtilCreateTaskToken() { return 0; } -int APS5_VABI sceShellUIUtilGetAppUrl() { +int APS5_VABI sceShellUIUtilGetAppUrl(const char* application, const char* parameter, char* output, std::uint32_t capacity, std::uint32_t* outputSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -20,12 +22,12 @@ int APS5_VABI sceShellUIUtilInitialize() { return 0; } -int APS5_VABI sceShellUIUtilLaunchByUri() { +int APS5_VABI sceShellUIUtilLaunchByUri(const char* uri, const ShellUIUtilLaunchParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceShellUIUtilLaunchTask() { +int APS5_VABI sceShellUIUtilLaunchTask(std::int32_t taskToken, const char* uri, char* output, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceShellUIUtil.md b/docs/dev/debt/libSceShellUIUtil.md index f07f584e14..12414a8ba7 100644 --- a/docs/dev/debt/libSceShellUIUtil.md +++ b/docs/dev/debt/libSceShellUIUtil.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceShellUIUtilCreateTaskToken, sceShellUIUtilGetAppUrl, sceShellUIUtilInitialize, sceShellUIUtilLaunchByUri, sceShellUIUtilLaunchTask and sceShellUIUtilTerminate](../../../core/libs/prx/libSceShellUIUtil/Export.cpp) (libSceShellUIUtil) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceShellUIUtil.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [libSceShellUIUtil](../../../core/libs/prx/libSceShellUIUtil/Export.cpp) remains unimplemented. Native wrappers confirm the export signatures, including two input strings, output buffer, 32-bit capacity and optional 32-bit output size for `GetAppUrl`. The precise string meanings and the second word of the 8-byte `LaunchByUri` parameter require the receiving IPC service; the wrapper only checks its leading size. Task completion, IPC errors and lifecycle behavior are not implemented. From 3025b1d393e451bbd2ad3ec8292c8a36e172fc8d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:28:40 +0300 Subject: [PATCH 327/523] docs(debt): condense remaining audio and tracker gaps --- docs/dev/debt/libSceAudioPropagation.md | 2 +- docs/dev/debt/libSceVrTracker2.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index b1d100771e..1800ba874d 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) implements only the no-acoustic-propagation path: the system keeps a 16-byte header in the title's CPU memory (no GPU memory), rooms, portals, sources and materials are tracked handles, attributes are validated and have no effect, no rays are requested and sources report no audio paths. Ray results and audio paths are accepted only when empty and throw otherwise; path calculation returns the confirmed initial validation errors and throws for unsupported propagation; `sceAudioPropagationSourceRender` preserves each destination mix because there are no path contributions, following the additive-output contract in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs); console behaviour has not been measured. It adds no dry signal. Objects still alive when their system is destroyed become unusable, and their later destroy or unregister is a no-op: PPSA21567 releases its reference-counted room, portal and source owners just before `sceAudioPropagationSystemDestroy`, so the order for every object is not proven. Occlusion, reflections and reverb from level geometry are absent; in-game behaviour remains unverified. +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) lacks acoustic propagation: attributes have no effect, sources expose no paths or rays, rendering preserves the destination mix without adding path contributions, and memory queries return only a 16-byte CPU header. Ray results and audio paths accept empty lists and throw otherwise; calculation returns initial native validation errors and throws for unsupported propagation. Room, portal, source and material handles are tracked, but destruction after their system is destroyed silently succeeds without native lifetime semantics being confirmed. Occlusion, reflections and reverb are absent. ### Unknown function info diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 0aa0c7b4d8..72d3209092 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes, so its exports answer as the console's module does before initialization, measured on a retail PS5 (firmware 13.60) with no PS VR2 headset by calling each of the 115 NIDs with null arguments, zeroed buffers and buffers filled with 0xAA and 0x01: 99 return 0x8A740001 and write nothing; `XknY0IDcOwU` and `ark13GkiskA` return 0x8A740006 for a null first argument and otherwise clear its first 24 and 16 bytes and return 0x8A740001; `-YvjOUph8B0` returns 0x8A740006 for a null second argument and otherwise 0x8A740001; `QyZvFyf-QEk` and `fTYZOZepkqE` return 0 and write nothing; `TZIUIk9id3c` writes 0xC000, 0, 0x40 and 0x40 as `uint32`, `uint32`, `uint16` and `uint16` at offsets 4 to 15 of its first argument and returns 0; `IB0qcDVu8vU` clears the `uint32` at its first argument and returns 0. The last two crash on the console for a null first argument and throw here. The named export signatures and play-area output layouts are recovered; initialization, result parameter and result fields still need reconstruction. What the tracker returns once initialized is not implemented; the twelve exports with aerolib names keep them and the others carry this library's own names +- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes; initialized tracking behavior is absent. Its uninitialized responses were measured on firmware 13.60 without a headset: most exports return 0x8A740001 without writes, while specific output/reset helpers clear buffers or write defaults. These helpers still lack complete signatures. Named export signatures and play-area output layouts are recovered; initialization and result fields need reconstruction. Anonymous exports remain grouped by their observed response rather than their individual contracts. ### Unknown function info From 25d8f40d885cb5cff3c60763f517313874f9d157 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:30:35 +0300 Subject: [PATCH 328/523] fix(movetracker): restore calibration capture and state signatures --- core/libs/prx/libSceMoveTracker/Export.cpp | 21 +++++++++++++-------- docs/dev/debt/libSceMoveTracker.md | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceMoveTracker/Export.cpp b/core/libs/prx/libSceMoveTracker/Export.cpp index 8b2c497e09..fb002273b5 100644 --- a/core/libs/prx/libSceMoveTracker/Export.cpp +++ b/core/libs/prx/libSceMoveTracker/Export.cpp @@ -3,6 +3,11 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct MoveTrackerCameraFrame; +struct MoveTrackerControllerInput; +struct MoveTrackerState; +struct MoveTrackerPlaybackInfo; + extern "C" { int APS5_VABI sceMoveTrackerCalibrateReset() { @@ -10,17 +15,17 @@ int APS5_VABI sceMoveTrackerCalibrateReset() { return 0; } -int APS5_VABI sceMoveTrackerCalibrateYaw() { +int APS5_VABI sceMoveTrackerCalibrateYaw(std::int32_t handle, const float* direction) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMoveTrackerCameraUpdate() { +int APS5_VABI sceMoveTrackerCameraUpdate(const MoveTrackerCameraFrame* frames) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMoveTrackerCaptureInit() { +int APS5_VABI sceMoveTrackerCaptureInit(void* imageMemory, std::int32_t capacity, std::int32_t interval, const char* path, std::int32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -30,12 +35,12 @@ int APS5_VABI sceMoveTrackerCaptureTerm() { return 0; } -int APS5_VABI sceMoveTrackerControllersUpdate() { +int APS5_VABI sceMoveTrackerControllersUpdate(const MoveTrackerControllerInput* controllers) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMoveTrackerGetState() { +int APS5_VABI sceMoveTrackerGetState(std::int32_t handle, std::uint64_t timestamp, MoveTrackerState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -44,7 +49,7 @@ int APS5_VABI sceMoveTrackerGetVersion() { return 2; } -int APS5_VABI sceMoveTrackerGetWorkingMemorySize() { +int APS5_VABI sceMoveTrackerGetWorkingMemorySize(std::uint32_t* totalSize, std::uint32_t* processingSize) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -54,12 +59,12 @@ int APS5_VABI sceMoveTrackerInit() { return 0; } -int APS5_VABI sceMoveTrackerPlayGetImages() { +int APS5_VABI sceMoveTrackerPlayGetImages(MoveTrackerCameraFrame* frames) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceMoveTrackerPlayGetStates() { +int APS5_VABI sceMoveTrackerPlayGetStates(MoveTrackerPlaybackInfo* info, MoveTrackerState* states) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMoveTracker.md b/docs/dev/debt/libSceMoveTracker.md index 156c1dfb0a..b6ddbd43a0 100644 --- a/docs/dev/debt/libSceMoveTracker.md +++ b/docs/dev/debt/libSceMoveTracker.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceMoveTrackerCalibrateReset, sceMoveTrackerCalibrateYaw, sceMoveTrackerCameraUpdate, sceMoveTrackerCaptureInit, sceMoveTrackerCaptureTerm, sceMoveTrackerControllersUpdate, sceMoveTrackerGetState, sceMoveTrackerGetVersion, sceMoveTrackerGetWorkingMemorySize, sceMoveTrackerInit, sceMoveTrackerPlayGetImages, sceMoveTrackerPlayGetStates, sceMoveTrackerPlayStart, sceMoveTrackerPlayStep, sceMoveTrackerRecordSave, sceMoveTrackerRecordStart, sceMoveTrackerRecordStop and sceMoveTrackerTerm](../../../core/libs/prx/libSceMoveTracker/Export.cpp) (libSceMoveTracker) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceMoveTracker.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [libSceMoveTracker](../../../core/libs/prx/libSceMoveTracker/Export.cpp) remains unimplemented. Calibration direction, camera/controller input, capture initialization, state query, memory-size query and playback output signatures are recovered. Camera and controller records occupy 24 bytes, state records 160 bytes, and playback metadata 16 bytes; their complete field meanings still require reconstruction. Initialization arguments and capture/playback helper signatures need further checking against caller register use. From b24f69664cc8e782e88138bcf388b64ee670ef45 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:32:26 +0300 Subject: [PATCH 329/523] fix(net): recover resolver connection and interface query signatures --- core/libs/prx/libSceNet/Export.cpp | 36 ++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 39a828ebe0..594a8dcbcb 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -46,6 +46,24 @@ // Guest socket descriptors retain PS5 semantics while their transport is backed by host sockets. +struct NetResolverConnection; +struct NetThreadParameter; +struct NetInitializeParameter; +struct NetInterfaceInfo; +struct NetInterfaceStatistics; + +struct NetResolverConnectOption { + std::int32_t connectionTimeout; + std::int32_t resolverTimeout; + std::int32_t resolverRetryCount; + std::uint32_t reserved; +}; + +static_assert(sizeof(NetResolverConnectOption) == 16); +static_assert(offsetof(NetResolverConnectOption, resolverTimeout) == 4); +static_assert(offsetof(NetResolverConnectOption, resolverRetryCount) == 8); +static_assert(offsetof(NetResolverConnectOption, reserved) == 12); + struct NetSyncObject { void* mutex; void* condition; @@ -2096,22 +2114,22 @@ int APS5_VABI sceNetShowRouteWithMemory(std::int32_t poolId) { return 0; } -int APS5_VABI sceNetResolverConnectAbort(void) { +int APS5_VABI sceNetResolverConnectAbort(NetResolverConnection* connection, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetResolverConnectCreate(void) { +int APS5_VABI sceNetResolverConnectCreate(const char* name, std::int32_t socketType, NetResolverConnection* connection, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetResolverConnect(void) { +int APS5_VABI sceNetResolverConnect(NetResolverConnection* connection, const char* hostname, std::uint16_t port, const NetResolverConnectOption* option) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetResolverConnectDestroy(void) { +int APS5_VABI sceNetResolverConnectDestroy(NetResolverConnection* connection) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2199,17 +2217,17 @@ int APS5_VABI sceNetGetDnsInfo(NetDnsInfo* info, std::uint32_t flags) { return static_cast(count); } -int APS5_VABI sceNetGetIfList(void) { +int APS5_VABI sceNetGetIfList(std::int32_t interfaceId, NetInterfaceInfo* interfaces, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetGetIfListEx(void) { +int APS5_VABI sceNetGetIfListEx(std::int32_t interfaceId, NetInterfaceInfo* interfaces, std::uint32_t capacity, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetGetIfListOnce(void) { +int APS5_VABI sceNetGetIfListOnce(std::int32_t interfaceId, NetInterfaceStatistics* statistics, std::uint32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2263,7 +2281,7 @@ int APS5_VABI sceNetGetSystemMemInfo(void) { return 0; } -int APS5_VABI sceNetInitParam(void) { +int APS5_VABI sceNetInitParam(const NetInitializeParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2356,7 +2374,7 @@ int APS5_VABI sceNetSysctl(const int* name, std::uint32_t nameLength, void* oldV return 0; } -int APS5_VABI sceNetThreadCreate(void) { +int APS5_VABI sceNetThreadCreate(Pthread* thread, const NetThreadParameter* parameter, const char* name) { NotImplemented_nid_no_patch(__func__); return 0; } From c90fa12732d294067e53530d0920b5d422b63573 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:34:08 +0300 Subject: [PATCH 330/523] fix(net): restore diagnostic buffers and route pointer returns --- core/libs/prx/libSceNet/Export.cpp | 44 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 594a8dcbcb..ad52371f2b 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -46,6 +46,18 @@ // Guest socket descriptors retain PS5 semantics while their transport is backed by host sockets. +struct NetSockInfo6; +struct NetStatisticsInfo; +struct NetInterfaceStats; +struct NetRouteInfo; +struct NetDebugParameter; +struct NetArpInfo; +struct NetAppMemoryInfo; +struct NetSystemMemoryInfo; +struct NetBandwidthParameter; +struct NetDataTraffic; +struct NetEmulationParameter; + struct NetResolverConnection; struct NetThreadParameter; struct NetInitializeParameter; @@ -2075,12 +2087,12 @@ int APS5_VABI sceNetClearDnsCache(int flags) { return 0; } -int APS5_VABI sceNetGetStatisticsInfo(void) { +int APS5_VABI sceNetGetStatisticsInfo(NetStatisticsInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetGetInterfaceStats(void) { +int APS5_VABI sceNetGetInterfaceStats(NetInterfaceStats* stats, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2134,7 +2146,7 @@ int APS5_VABI sceNetResolverConnectDestroy(NetResolverConnection* connection) { return 0; } -int APS5_VABI sceNetGetSockInfo6(void) { +int APS5_VABI sceNetGetSockInfo6(std::int32_t socket, NetSockInfo6* info, std::uint32_t capacity, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2153,12 +2165,12 @@ int APS5_VABI sceNetShowRoute6WithMemory(std::int32_t poolId) { return 0; } -int APS5_VABI sceNetAllocateAllRouteInfo(void) { +NetRouteInfo* APS5_VABI sceNetAllocateAllRouteInfo(std::int32_t interfaceId, std::int32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetAllocateAllRouteInfo6(void) { +NetRouteInfo* APS5_VABI sceNetAllocateAllRouteInfo6(std::int32_t interfaceId, std::int32_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2168,7 +2180,7 @@ int APS5_VABI sceNetControl(std::uint32_t interfaceId, std::int32_t interfaceInd return 0; } -int APS5_VABI sceNetDbgInit(void) { +int APS5_VABI sceNetDbgInit(NetDebugParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2198,12 +2210,12 @@ int APS5_VABI sceNetFreeAllRouteInfo(void* routes) { return 0; } -int APS5_VABI sceNetGetArpInfo(void) { +int APS5_VABI sceNetGetArpInfo(NetArpInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetGetAppMemInfo(void) { +int APS5_VABI sceNetGetAppMemInfo(NetAppMemoryInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2276,7 +2288,7 @@ void APS5_VABI sceNetGetSystemTime(std::uint64_t* output) { if (output != nullptr) *output = sceKernelGetProcessTime(); } -int APS5_VABI sceNetGetSystemMemInfo(void) { +int APS5_VABI sceNetGetSystemMemInfo(NetSystemMemoryInfo* info, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2862,17 +2874,17 @@ int APS5_VABI sceNetPppoeStop(void) { return 0; } -int APS5_VABI sceNetBandwidthControlSetDefaultParam(void) { +int APS5_VABI sceNetBandwidthControlSetDefaultParam(const NetBandwidthParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetBandwidthControlGetDefaultParam(void) { +int APS5_VABI sceNetBandwidthControlGetDefaultParam(NetBandwidthParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetBandwidthControlGetDataTraffic(void) { +int APS5_VABI sceNetBandwidthControlGetDataTraffic(std::int32_t interfaceId, NetDataTraffic* traffic, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2932,22 +2944,22 @@ int APS5_VABI sceNetDumpRead(std::int32_t dumpId, void* buffer, std::int32_t buf return 0; } -int APS5_VABI sceNetEmulationDebugSettingsSet(void) { +int APS5_VABI sceNetEmulationDebugSettingsSet(const NetEmulationParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetEmulationDebugSettingsGet(void) { +int APS5_VABI sceNetEmulationDebugSettingsGet(NetEmulationParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetEmulationSet(void) { +int APS5_VABI sceNetEmulationSet(const NetEmulationParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetEmulationGet(void) { +int APS5_VABI sceNetEmulationGet(NetEmulationParameter* parameter, std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } From 77f3c97478b20bc6491aa07a08f40f3654a08a8b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:35:22 +0300 Subject: [PATCH 331/523] fix(net): restore wlan and dhcp query signatures --- core/libs/prx/libSceNet/Export.cpp | 59 ++++++++++++++++++++++-------- docs/dev/debt/libSceNet.md | 44 +--------------------- 2 files changed, 45 insertions(+), 58 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index ad52371f2b..931deb1696 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -46,6 +46,33 @@ // Guest socket descriptors retain PS5 semantics while their transport is backed by host sockets. +struct NetWlanAdhocParameter; +struct NetWlanWakeInfo; +struct NetWlanPspWakeInfo; +struct NetWlanPspWakeParameter; +struct NetWlanApParameter; +struct NetWlanDeviceInfo; +struct NetWlanAntennaParameter; +struct NetWlanRateParameter; +struct NetWlanDeviceConfiguration; +struct NetWlanRssiInfo; +struct NetDhcpInfo; +struct NetAutoIpInfo; +struct NetAddress6Info; + +struct NetDhcpOptionInfo { + std::uint32_t option; + std::uint32_t capacity; + void* data; + std::uint32_t size; + std::uint32_t reserved; +}; + +static_assert(sizeof(NetDhcpOptionInfo) == 24); +static_assert(offsetof(NetDhcpOptionInfo, capacity) == 4); +static_assert(offsetof(NetDhcpOptionInfo, data) == 8); +static_assert(offsetof(NetDhcpOptionInfo, size) == 16); + struct NetSockInfo6; struct NetStatisticsInfo; struct NetInterfaceStats; @@ -2604,17 +2631,17 @@ int APS5_VABI sceNetConfigGetDefaultRoute(std::uint32_t* gateway) { return 0; } -int APS5_VABI sceNetDhcpGetAutoipInfo(void) { +int APS5_VABI sceNetDhcpGetAutoipInfo(std::int32_t interfaceId, NetAutoIpInfo* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetDhcpGetInfo(void) { +int APS5_VABI sceNetDhcpGetInfo(std::int32_t interfaceId, NetDhcpInfo* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetDhcpGetInfoEx(void) { +int APS5_VABI sceNetDhcpGetInfoEx(std::int32_t interfaceId, std::int32_t type, NetDhcpOptionInfo* info, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2689,7 +2716,7 @@ int APS5_VABI sceNetAddrConfig6Stop(int interfaceId) { return 0; } -int APS5_VABI sceNetAddrConfig6GetInfo(void) { +int APS5_VABI sceNetAddrConfig6GetInfo(std::int32_t interfaceId, NetAddress6Info* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2729,17 +2756,17 @@ int APS5_VABI sceNetConfigWlanAdhocClearWakeOnWlan(std::uint32_t interfaceId) { return 0; } -int APS5_VABI sceNetConfigWlanAdhocCreate(void) { +int APS5_VABI sceNetConfigWlanAdhocCreate(std::int32_t interfaceId, NetWlanAdhocParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanAdhocGetWakeOnWlanInfo(void) { +int APS5_VABI sceNetConfigWlanAdhocGetWakeOnWlanInfo(std::int32_t interfaceId, NetWlanWakeInfo* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanAdhocJoin(void) { +int APS5_VABI sceNetConfigWlanAdhocJoin(std::int32_t interfaceId, NetWlanAdhocParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2754,12 +2781,12 @@ int APS5_VABI sceNetConfigWlanAdhocPspEmuClearWakeOnWlan(std::uint32_t interface return 0; } -int APS5_VABI sceNetConfigWlanAdhocPspEmuGetWakeOnWlanInfo(void) { +int APS5_VABI sceNetConfigWlanAdhocPspEmuGetWakeOnWlanInfo(std::int32_t interfaceId, NetWlanPspWakeInfo* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanAdhocPspEmuSetWakeOnWlan(void) { +int APS5_VABI sceNetConfigWlanAdhocPspEmuSetWakeOnWlan(std::int32_t interfaceId, NetWlanPspWakeParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2779,7 +2806,7 @@ int APS5_VABI sceNetConfigWlanAdhocSetWakeOnWlan(void) { return 0; } -int APS5_VABI sceNetConfigWlanApStart(void) { +int APS5_VABI sceNetConfigWlanApStart(std::int32_t interfaceId, NetWlanApParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2804,27 +2831,27 @@ int APS5_VABI sceNetConfigWlanBackgroundScanStop(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigWlanDiagGetDeviceInfo(void) { +int APS5_VABI sceNetConfigWlanDiagGetDeviceInfo(std::int32_t interfaceId, NetWlanDeviceInfo* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanDiagSetAntenna(void) { +int APS5_VABI sceNetConfigWlanDiagSetAntenna(std::int32_t interfaceId, NetWlanAntennaParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanDiagSetTxFixedRate(void) { +int APS5_VABI sceNetConfigWlanDiagSetTxFixedRate(std::int32_t interfaceId, NetWlanRateParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanGetDeviceConfig(void) { +int APS5_VABI sceNetConfigWlanGetDeviceConfig(std::int32_t interfaceId, NetWlanDeviceConfiguration* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanInfraGetRssiInfo(void) { +int APS5_VABI sceNetConfigWlanInfraGetRssiInfo(std::int32_t interfaceId, NetWlanRssiInfo* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2844,7 +2871,7 @@ int APS5_VABI sceNetConfigWlanScan(void) { return 0; } -int APS5_VABI sceNetConfigWlanSetDeviceConfig(void) { +int APS5_VABI sceNetConfigWlanSetDeviceConfig(std::int32_t interfaceId, NetWlanDeviceConfiguration* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 391136c6b2..d64641d764 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -6,76 +6,36 @@ ### Unknown function info -- [sceNetEtherStrton](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - its signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L737-L913) and its [Net bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs#L19), not measured on a console; the throwing declaration only resolves the import. +- [Resolver-connect, thread creation, interface and diagnostic queries, WLAN configuration and DHCP queries](../../../core/libs/prx/libSceNet/Export.cpp) retain unsupported implementations. Their restored argument lists still reference incomplete connection, thread, interface, route, WLAN and statistics types. Kernel ioctl payloads need reconstruction from their handlers or consumers; buffer lengths alone do not establish field types. - [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - the signatures `(int id, stats*)`, the layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) and the error codes were measured on a PS5 (firmware 13.60). The pool size is the one given at init or pool creation. Http2 and Net allocate nothing from these pools. Ssl counts its certificate backend allocations and returned data against its pool; C++ bookkeeping is outside that budget. All three initially report zero use, where the console already reports 0x15C0 bytes for Ssl, 0x1640 for Http2 and 4 for a Net pool. Ssl's allocation sizes and peak usage do not reproduce the console allocator - [sceNetClearDnsCache](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the library has no DNS cache to clear because lookups use the host resolver. The host resolver cache is left unchanged. -- [sceNetGetStatisticsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetInterfaceStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetResolverConnectAbort](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetResolverConnectCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetResolverConnect](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetResolverConnectDestroy](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetSockInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetAllocateAllRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetAllocateAllRouteInfo6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDbgInit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetArpInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetAppMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature +- [sceNetEtherStrton](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - its signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L737-L913) and its [Net bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs#L19), not measured on a console; the throwing declaration only resolves the import. - [sceNetGetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(info, int flags)` signature, the two IPv4 server slots in network byte order and the server count as the return value follow the PS4 [OpenOrbis musl resolver](https://github.com/OpenOrbis/musl/blob/2b0bb8ce2fa64ac255cb656785729d159a5f074c/src/network/resolvconf.c) and [OpenOrbis Net.h](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/Net.h); unverified on PS5 hardware. It reports the host DNS servers of the interface `sceNetCtlGetInfo` reports, never servers set through `sceNetSetDnsInfo`. Zeroing the unused slots and returning 0 without a host IPv4 interface are unverified. Nonzero flags and a null info throw because their console results are unknown -- [sceNetGetIfList](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetIfListEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetIfListOnce](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - generates values with the host random device; the system library maintains its own seeded PRNG state. - [sceNetGetRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetSystemMemInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetInitParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. - [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced. Allocation flag 4 bypasses the system pool reserve threshold; it throws here because the pool reserve is not implemented. Flag 2 zero-fills and other bits are ignored, matching the system allocator. -- [sceNetThreadCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDhcpGetAutoipInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDhcpGetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDhcpGetInfoEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetAddrConfig6GetInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocCreate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocGetWakeOnWlanInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocPspEmuGetWakeOnWlanInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocPspEmuSetWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocSetExtInfoElement](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanAdhocSetWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanApStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanBackgroundScanQuery](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanBackgroundScanStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanDiagGetDeviceInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanDiagSetAntenna](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanDiagSetTxFixedRate](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanGetDeviceConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanInfraGetRssiInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanInfraScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigWlanScan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanSetDeviceConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetBandwidthControlSetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetBandwidthControlGetDefaultParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetBandwidthControlGetDataTraffic](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetStatisticsInfoInternal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDumpSetFilterParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEmulationDebugSettingsSet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEmulationDebugSettingsGet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEmulationSet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetEmulationGet](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature ### Functional From 60ca71bd1f8916905cd3169e3cf2c594c661841e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:36:44 +0300 Subject: [PATCH 332/523] fix(net): restore wlan scan buffers and result outputs --- core/libs/prx/libSceNet/Export.cpp | 25 +++++++++++++++++-------- docs/dev/debt/libSceNet.md | 8 -------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 931deb1696..fe8faee481 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -46,6 +46,15 @@ // Guest socket descriptors retain PS5 semantics while their transport is backed by host sockets. +struct NetWlanInformationElement; +struct NetWlanWakeParameter; +struct NetWlanScanParameter; +struct NetWlanScanResult; +struct NetWlanAdhocScanParameter; +struct NetWlanInfraScanParameter; +struct NetWlanJoinResult; +struct NetDumpFilterParameter; + struct NetWlanAdhocParameter; struct NetWlanWakeInfo; struct NetWlanPspWakeInfo; @@ -2791,17 +2800,17 @@ int APS5_VABI sceNetConfigWlanAdhocPspEmuSetWakeOnWlan(std::int32_t interfaceId, return 0; } -int APS5_VABI sceNetConfigWlanAdhocScanJoin(void) { +int APS5_VABI sceNetConfigWlanAdhocScanJoin(std::int32_t interfaceId, const NetWlanAdhocScanParameter* parameter, void* buffer, std::size_t bufferSize, NetWlanJoinResult** result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanAdhocSetExtInfoElement(void) { +int APS5_VABI sceNetConfigWlanAdhocSetExtInfoElement(std::int32_t interfaceId, NetWlanInformationElement* parameter, std::int32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanAdhocSetWakeOnWlan(void) { +int APS5_VABI sceNetConfigWlanAdhocSetWakeOnWlan(std::int32_t interfaceId, NetWlanWakeParameter* parameter, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2816,12 +2825,12 @@ int APS5_VABI sceNetConfigWlanApStop(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigWlanBackgroundScanQuery(void) { +int APS5_VABI sceNetConfigWlanBackgroundScanQuery(std::int32_t interfaceId, void* buffer, std::int32_t size, NetWlanScanResult** results, std::size_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanBackgroundScanStart(void) { +int APS5_VABI sceNetConfigWlanBackgroundScanStart(std::int32_t interfaceId, NetWlanScanParameter* parameter, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2861,12 +2870,12 @@ int APS5_VABI sceNetConfigWlanInfraLeave(int interfaceId) { return 0; } -int APS5_VABI sceNetConfigWlanInfraScanJoin(void) { +int APS5_VABI sceNetConfigWlanInfraScanJoin(std::int32_t interfaceId, const NetWlanInfraScanParameter* parameter, void* buffer, std::size_t bufferSize, NetWlanJoinResult** result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigWlanScan(void) { +int APS5_VABI sceNetConfigWlanScan(std::int32_t interfaceId, const NetWlanScanParameter* parameter, std::size_t parameterSize, void* buffer, std::size_t bufferSize, NetWlanScanResult** results, std::size_t* count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2956,7 +2965,7 @@ int APS5_VABI sceNetDumpCreate(const char* name, std::int32_t bufferSize, std::u return 0; } -int APS5_VABI sceNetDumpSetFilterParam(void) { +int APS5_VABI sceNetDumpSetFilterParam(std::int32_t socket, const NetDumpFilterParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index d64641d764..1d9c7a4914 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -24,18 +24,10 @@ - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocSetExtInfoElement](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanAdhocSetWakeOnWlan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanBackgroundScanQuery](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanBackgroundScanStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanInfraScanJoin](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigWlanScan](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetGetStatisticsInfoInternal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDumpSetFilterParam](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature ### Functional From 04f2d9a60f37e5096c252aeed3cafdf78502ce3b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:37:24 +0300 Subject: [PATCH 333/523] fix(net): reject unsupported diagnostic output after initialization --- core/libs/prx/libSceNet/Export.cpp | 8 ++++++-- core/libs/tests/GuestSceNet.cpp | 13 +++++++++++-- docs/dev/debt/libSceNet.md | 3 ++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index fe8faee481..bf95cdd988 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -724,7 +724,9 @@ int* APS5_VABI sceNetErrnoLoc(void) { } int APS5_VABI sceNetShowNetstat(void) { - return 0; + std::lock_guard lock(g_mutex); + if (!g_net_inited) return fail(NET_ENOTINIT); + throw std::runtime_error(__func__); } int PollSockets(KernelSocketPoll::Entry* entries, int count, int timeoutMilliseconds) { @@ -2110,7 +2112,9 @@ int APS5_VABI sceNetResolverAbort(int rid, int flags) { } int APS5_VABI sceNetShowIfconfig() { - return 0; + std::lock_guard lock(g_mutex); + if (!g_net_inited) return fail(NET_ENOTINIT); + throw std::runtime_error(__func__); } extern const std::uint8_t in6addr_any_nid_postfix[16] = {}; diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 6db4c99913..3f775bd82e 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -239,8 +239,15 @@ static std::string CaptureNetstat(int pool) { } static void CheckNetstat() { - Require(sceNetShowNetstat() == 0); - Require(sceNetShowIfconfig() == 0); + for (const auto diagnostic : {sceNetShowNetstat, sceNetShowIfconfig}) { + bool unsupported = false; + try { + diagnostic(); + } catch (const std::runtime_error&) { + unsupported = true; + } + Require(unsupported); + } Require(sceNetShowRoute() == 0); Require(sceNetShowRoute6() == 0); Require(Failed(sceNetShowNetstatWithMemory(-1), 9)); @@ -1457,6 +1464,8 @@ int main() { Require(Failed(sceNetGetRandom(nullptr), 200)); Require(Failed(sceNetUsleep(0), 200)); Require(Failed(sceNetUsleep(-1), 200)); + Require(Failed(sceNetShowNetstat(), 200)); + Require(Failed(sceNetShowIfconfig(), 200)); Require(sceNetInit_nid_postfix() == 0); CheckEpollWritable(); CheckSocketHostErrors(); diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 1d9c7a4914..eb17a8b8d3 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -2,7 +2,8 @@ ### Silent stubs -- [sceNetShowIfconfig, sceNetShowNetstat, sceNetShowRoute, sceNetShowRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - return success without printing anything: the `()` signatures and the success return follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/net.cpp), which stubs each the same way. PPSA01530 fails to load without the `sceNetShowIfconfig` export (#3177); PPSA11386 imports `sceNetShowNetstat` but does not call it during start-up. What the console prints and where is unverified +- [sceNetShowIfconfig, sceNetShowNetstat](../../../core/libs/prx/libSceNet/Export.cpp) return the native not-initialized error and set errno before initialization. Initialized calls throw because native diagnostic collection and formatting are not implemented. +- [sceNetShowRoute, sceNetShowRoute6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - return success without printing anything: the `()` signatures and the success return follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/net.cpp), which stubs each the same way. PPSA01530 fails to load without the `sceNetShowIfconfig` export (#3177); PPSA11386 imports `sceNetShowNetstat` but does not call it during start-up. What the console prints and where is unverified ### Unknown function info From ce8ee3f598d0c520429a6447fc5ac6f7daf12184 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:41:23 +0300 Subject: [PATCH 334/523] fix(netctl): restore scan and network query signatures --- core/libs/prx/libSceNetCtl/Export.cpp | 39 +++++++++++++++++---------- docs/dev/debt/libSceNetCtl.md | 18 +++---------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index 03167d8863..c3a558cda9 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -34,6 +34,17 @@ #include // Network-control state reflects host interface addressing, not PSN sign-in status. +union NetCtlInfoV6; +struct NetCtlConnectionConfiguration; +struct NetCtlScanOptions; +struct NetCtlScanBssidList; +struct NetCtlScanInfo; +struct NetCtlNatInfoInternal; +struct NetCtlBandwidthInfo; +struct NetCtlNetEvConfiguration; +struct NetBweInfo; +struct NetCtlWifiInfo; + static constexpr int SCE_NET_CTL_ERROR_CALLBACK_MAX = static_cast(0x80412103); static constexpr int SCE_NET_CTL_ERROR_INVALID_ID = static_cast(0x80412105); static constexpr int SCE_NET_CTL_ERROR_INVALID_CODE = static_cast(0x80412106); @@ -592,7 +603,7 @@ int APS5_VABI sceNetCtlGetState2IpcInt(int* state) { return 0; } -int APS5_VABI sceNetCtlGetInfoIpcInt(void) { +int APS5_VABI sceNetCtlGetInfoIpcInt(std::uint32_t code, NetCtlInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -617,7 +628,7 @@ int APS5_VABI sceNetCtlConnectWithRetryIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlConnectConfIpcInt(void) { +int APS5_VABI sceNetCtlConnectConfIpcInt(const NetCtlConnectionConfiguration* configuration) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -627,32 +638,32 @@ int APS5_VABI sceNetCtlDisconnectIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlScanIpcInt(void) { +int APS5_VABI sceNetCtlScanIpcInt(const char* const* ssids, std::uint32_t count, const NetCtlScanOptions* options) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetScanInfoBssidIpcInt(void) { +int APS5_VABI sceNetCtlGetScanInfoBssidIpcInt(NetCtlScanBssidList* bssids, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetScanInfoByBssidIpcInt(void) { +int APS5_VABI sceNetCtlGetScanInfoByBssidIpcInt(const std::uint8_t* bssid, NetCtlScanInfo* info, void* buffer, std::size_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetScanInfoForSsidScanIpcInt(void) { +int APS5_VABI sceNetCtlGetScanInfoForSsidScanIpcInt(NetCtlScanInfo* info, void* buffer, std::size_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetScanInfoBssidForSsidListScanIpcInt(void) { +int APS5_VABI sceNetCtlGetScanInfoBssidForSsidListScanIpcInt(NetCtlScanBssidList* bssids, std::uint32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetScanInfoForSsidListScanIpcInt(void) { +int APS5_VABI sceNetCtlGetScanInfoForSsidListScanIpcInt(const std::uint8_t* bssid, NetCtlScanInfo* info, void* buffer, std::size_t* size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -691,12 +702,12 @@ int APS5_VABI sceNetCtlGetStateV6IpcInt(int* state) { return 0; } -int APS5_VABI sceNetCtlGetInfoV6IpcInt(void) { +int APS5_VABI sceNetCtlGetInfoV6IpcInt(std::uint32_t code, NetCtlInfoV6* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetNatInfoIpcInt(void) { +int APS5_VABI sceNetCtlGetNatInfoIpcInt(NetCtlNatInfoInternal* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -726,12 +737,12 @@ int APS5_VABI sceNetCtlEnableBandwidthManagementIpcInt(void) { return 0; } -int APS5_VABI sceNetCtlGetBandwidthInfoIpcInt(void) { +int APS5_VABI sceNetCtlGetBandwidthInfoIpcInt(NetCtlBandwidthInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetCtlGetNetEvConfigInfoIpcInt(void) { +int APS5_VABI sceNetCtlGetNetEvConfigInfoIpcInt(NetCtlNetEvConfiguration* configuration) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -840,7 +851,7 @@ int APS5_VABI sceNetBweUnregisterCallbackIpcInt(int callbackId) { return 0; } -int APS5_VABI sceNetBweGetInfoIpcInt(void) { +int APS5_VABI sceNetBweGetInfoIpcInt(NetBweInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -865,7 +876,7 @@ int APS5_VABI sceNetBweSetInternetConnectionTestResultIpcInt(std::int32_t firstR return 0; } -int APS5_VABI sceNetCtlGetWifiInfoIpcInt(void) { +int APS5_VABI sceNetCtlGetWifiInfoIpcInt(NetCtlWifiInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index dbb3c48613..5c9e32df16 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -6,21 +6,11 @@ ### Unknown function info -- [sceNetCtlGetInfoV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `Jy1EO5GdlcM`) - imported by PPSA08587. `(int code, SceNetCtlInfoV6* info)` with codes 1-5 (IP address, link-local address, default route, primary and secondary DNS) comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero), whose source is unknown; shadPS4 and fpPS4 only stub it. A null `info` answers `INVALID_ADDR`, a code outside 1-5 `INVALID_CODE`, and a host without an IPv6 address `NOT_CONNECTED`, as `sceNetCtlGetStateV6` reports it. The layout of each answer is unverified, so every code throws on a host with an IPv6 address +- [sceNetCtlScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - argument registers and IPC transfer directions are recovered for the info, connection, scan, NAT, bandwidth and Wi-Fi queries. The connection configuration is 1464 bytes, scan options 88, BSSID output 96, scan info 112, IPv6 info 256, internal NAT info 196, bandwidth info 32, bandwidth-estimation info 28 and Wi-Fi info 48. Their field semantics still require the receiving service; incomplete declarations preserve this gap. Scan queries transfer a six-byte BSSID and use a 64-bit in/out buffer size. + - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - answers from the host interface that holds an IPv4 address, preferring the one that carries the default route: its address, netmask, gateway, MTU and first two IPv4 DNS servers. The device is reported as wired, the link as connected, the IP configuration as DHCP and the HTTP proxy as off, whatever the host uses, and the Ethernet address is the one `sceNetGetMacAddress` reports. A gateway or DNS server the interface does not have is reported as `0.0.0.0`; what a console reports then is unknown. The DHCP host name (12), the PPPoE user name (13), the HTTP proxy server (20) and the Wi-Fi BSSID and SSID (5, 6) are empty strings, as that configuration sets none of them; the remaining Wi-Fi codes (7-10) and the HTTP proxy port (21) report 0; this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/network.cpp) and is not measured on hardware -- [sceNetCtlGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlConnectConfIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetScanInfoBssidIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetScanInfoByBssidIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetScanInfoForSsidScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetScanInfoBssidForSsidListScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetScanInfoForSsidListScanIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature +- [sceNetCtlGetInfoV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `Jy1EO5GdlcM`) - imported by PPSA08587. `(int code, SceNetCtlInfoV6* info)` with codes 1-5 (IP address, link-local address, default route, primary and secondary DNS) comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero), whose source is unknown; shadPS4 and fpPS4 only stub it. A null `info` answers `INVALID_ADDR`, a code outside 1-5 `INVALID_CODE`, and a host without an IPv6 address `NOT_CONNECTED`, as `sceNetCtlGetStateV6` reports it. The layout of each answer is unverified, so every code throws on a host with an IPv6 address - [sceNetCtlGetResultV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `H5yARg37U5g`) - `(int eventType, int* errorCode)` comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/NetCtl.cs#L246-L251), whose source is unknown; shadPS4 only stubs it. Callbacks registered with `sceNetCtlRegisterCallbackV6` receive no event, so event types 1-3 write 0 to `errorCode`; what a console writes before the first event is unmeasured. A null `errorCode` answers `INVALID_ADDR`, as `sceNetCtlGetResult` does, and other event types throw, as their error code on a console is unknown -- [sceNetCtlGetInfoV6IpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetNatInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetBandwidthInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetNetEvConfigInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpClearEvent](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpRegisterCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature @@ -32,8 +22,6 @@ - [sceNetCtlApRpStartWithRetry](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStartConf](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature - [sceNetCtlApRpStop](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetBweGetInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlGetWifiInfoIpcInt](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature ### Functional From d7606dc9fe4f4684659bde5769ab07f620f7b908 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:42:58 +0300 Subject: [PATCH 335/523] fix(ime): restore text validation and dictionary signatures --- core/libs/prx/libSceIme/Export.cpp | 31 +++++++++++++++++------------- docs/dev/debt/libSceIme.md | 15 ++------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/core/libs/prx/libSceIme/Export.cpp b/core/libs/prx/libSceIme/Export.cpp index 5ab0ae9f52..29d014c627 100644 --- a/core/libs/prx/libSceIme/Export.cpp +++ b/core/libs/prx/libSceIme/Export.cpp @@ -16,6 +16,9 @@ #include "prx/libSceIme/include/ImeInput.hpp" struct ImeVshContext; +struct ImeVshParameter; +struct ImePreeditMetaInfo; +struct ImeDictionaryWord; struct ImeVshCaretGeometry { std::int32_t x; @@ -31,6 +34,8 @@ struct ImeVshTextGeometry { std::uint32_t height; }; +static_assert(sizeof(ImeEditText) == 64); +static_assert(offsetof(ImeEvent, param) == 8); static_assert(sizeof(ImeVshCaretGeometry) == 16); static_assert(sizeof(ImeVshTextGeometry) == 16); @@ -600,7 +605,7 @@ int APS5_VABI sceImeUpdate(EventHandler handler) { } } -int APS5_VABI sceImeVshOpen(void) { +int APS5_VABI sceImeVshOpen(ImeVshContext** context, const ImeVshParameter* parameter, const char16_t* text) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -615,7 +620,7 @@ int APS5_VABI sceImeVshUpdate(ImeVshContext* context) { return 0; } -int APS5_VABI sceImeVshUpdateContext(void) { +int APS5_VABI sceImeVshUpdateContext(ImeVshContext* context, const char16_t* text, std::uint32_t caretIndex) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -625,7 +630,7 @@ int APS5_VABI sceImeVshClearPreedit(ImeVshContext* context) { return 0; } -int APS5_VABI sceImeVshNotifySetText(void) { +int APS5_VABI sceImeVshNotifySetText(ImeVshContext* context, const char16_t* text, std::uint32_t length) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -704,12 +709,12 @@ int APS5_VABI sceImeVshSetSelectionText(ImeVshContext* context, const char16_t* return 0; } -int APS5_VABI sceImeCheckUpdateTextInfo(void) { +int APS5_VABI sceImeCheckUpdateTextInfo(ImeEditText* info, std::uint8_t repair) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeCheckRemoteEventParam(void) { +int APS5_VABI sceImeCheckRemoteEventParam(ImeEvent* event, std::uint8_t repair) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -719,17 +724,17 @@ int APS5_VABI sceImeKeyboardOpenInternal(std::int32_t userId, EventHandler handl return 0; } -int APS5_VABI sceImeVshSetParam(void) { +int APS5_VABI sceImeVshSetParam(ImeVshContext* context, const ImeVshParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshUpdateContext2(void) { +int APS5_VABI sceImeVshUpdateContext2(ImeVshContext* context, const char16_t* text, std::uint32_t caretIndex) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshSendTextInfo(void) { +int APS5_VABI sceImeVshSendTextInfo(ImeVshContext* context, const void* info, std::uint32_t size) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -769,7 +774,7 @@ int APS5_VABI sceImeVshSetPanelPosition(ImeVshContext* context, float x, float y return 0; } -int APS5_VABI sceImeVshNotifyPreeditMetaInfo(void) { +int APS5_VABI sceImeVshNotifyPreeditMetaInfo(ImeVshContext* context, const ImePreeditMetaInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -784,22 +789,22 @@ int APS5_VABI sceImeConfigGet(void) { return 0; } -int APS5_VABI sceImeDicGetWords(void) { +int APS5_VABI sceImeDicGetWords(std::int32_t userId, std::uint64_t dictionary, ImeDictionaryWord* words) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeDicAddWord(void) { +int APS5_VABI sceImeDicAddWord(std::int32_t userId, std::uint64_t dictionary, const ImeDictionaryWord* word) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeDicDeleteWord(void) { +int APS5_VABI sceImeDicDeleteWord(std::int32_t userId, std::uint64_t dictionary, const ImeDictionaryWord* word) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeDicReplaceWord(void) { +int APS5_VABI sceImeDicReplaceWord(std::int32_t userId, std::uint64_t dictionary, const ImeDictionaryWord* oldWord, const ImeDictionaryWord* newWord) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceIme.md b/docs/dev/debt/libSceIme.md index 4e6a62319d..f4689b4ab8 100644 --- a/docs/dev/debt/libSceIme.md +++ b/docs/dev/debt/libSceIme.md @@ -6,27 +6,16 @@ ### Unknown function info +- [sceImeVshOpen](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - VSH context, UTF-16 text and dictionary argument registers are recovered. The VSH parameter is copied as 256 bytes; preedit metadata is eight bytes. Dictionary operations transfer 224-byte records, with a fixed 11200-byte output for GetWords; the 64-bit dictionary selector and record fields still need receiver-side identification. These payload types remain incomplete. UpdateTextInfo and RemoteEventParam use the existing 64-byte ImeEditText and event payload at offset eight; their byte-sized repair flag permits correcting invalid text ranges and caret positions. + - [sceImeGetPanelPositionAndForm](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the output is taken to be the `PositionAndForm` that `sceImeDialogGetPanelPositionAndForm` fills. While a panel is open it reports the type, position and alignments passed to `sceImeOpen` and the size `sceImeGetPanelSize` gives; the console's values for a panel moved by its own position adjustment are unknown. - [sceImeFilterText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the behaviour follows the disassembly of `libSceIme.sprx` from firmware 13.60 and is differential-tested against that function in an emulator, not measured on a console. [ImeFilterCodepages.hpp](../../../core/libs/prx/libSceIme/include/ImeFilterCodepages.hpp) holds the two codepage checks (helpers at 0x4850 and 0x48f0), rebuilt from Unicode's public [CP932](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT) and [CP936](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT) tables plus the Latin-1, CJK Extension A and a few other units the firmware also accepts (listed in the PR). The parameter layout beyond `type` is inferred: `mask` is the allowed-class bitmask, and `version` and `variant` derive it when it is 0. [sceImeCheckFilterText](../../../core/libs/prx/libSceIme/Export.cpp) throws, as its contract is unknown - [sceImeOpen, sceImeUpdate, sceImeSetText, sceImeSetCaret, sceImeClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the event ids (Open 0, UpdateText 1, UpdateCaret 2, PressClose 4, PressEnter 5), the caret directions (left 1, right 2), the error codes and the text event payload follow the PS4 reimplementation in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/b72489d8c24e63fc56a29fe2c90120ab768a210d/src/core/libraries/ime/ime.cpp); none is measured on a PS5. Events are queued and delivered by `sceImeUpdate` only when it is passed the handler given to `sceImeOpen`, with that call's `arg`. A text event points `str` at a copy of the text kept in `work`, and `inputTextBuffer` holds the same text; both are terminated, `caret_index` is the caret after the edit and the one edit area has the edit position and the change in UTF-16 units (0 for Enter and Close). An edit sends one UpdateText; a caret move sends one UpdateCaret per UTF-16 unit, as shadPS4 does. Enter inserts a line break with the multiline option and sends PressEnter otherwise; Escape sends PressClose. Typed text is cut to `maxTextLength` without splitting a surrogate pair, line breaks are dropped without the multiline option, and the BasicLatin, URL, mail and number types do not filter characters. `sceImeOpen` checks, in shadPS4's order, the user, type, enter label, input method, option bits (the `sceImeGetPanelSize` mask), `maxTextLength` (1 to 2048), the text buffer, the position (below 1920x1080, or 3840x2160 with the over-2K option), the alignments, the extended priority and reserved bytes, `work` (non-null, 4-byte aligned), the handler, the reserved bytes and the initial text (no unpaired surrogate, no line break without the multiline option); shadPS4's SDK-dependent option, language and extended-option masks and its work-overlap check are not applied, and only the text copy is written to `work`. `sceImeSetText` keeps the caret unless the text became shorter, `sceImeSetCaret` rejects an index past the text with `0x80bc0030`, and neither sends an event. `sceImeSetTextGeometry` accepts any geometry while a panel is open, since no panel is drawn -- [sceImeVshOpen](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshUpdateContext](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshNotifySetText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeForTestFunction](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeCheckUpdateTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeCheckRemoteEventParam](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSetParam](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshUpdateContext2](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshSendTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyCurrentTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeVshNotifyCurrentTextInfoForReconvertText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshNotifyPreeditMetaInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigSet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigGet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDicGetWords](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDicAddWord](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDicDeleteWord](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeDicReplaceWord](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [libSceIme pending exports](../../../core/libs/prx/libSceIme/Export.cpp) - 2 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceIme.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`. From 73fcc2f8674938e8e3dece6c94300cfe11b33d1c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:44:32 +0300 Subject: [PATCH 336/523] fix(hmd2): restore gaze and calibration signatures --- core/libs/prx/libSceHmd2/Export.cpp | 128 ++++++++++++++++++++++------ docs/dev/debt/libSceHmd2.md | 2 + 2 files changed, 106 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index f6f53d804c..e78ebbbe10 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -10,6 +10,31 @@ #include #include +namespace Hmd2 { + +struct GazeResultParameter; +struct GazeResult; +struct FoveatedRenderingResult; +struct GazeCalibrationData; +struct GazeUserCalibrationData; +struct GazeUserCalibrationStatus; +struct GazeRawData; +struct GazeObjectSelectionParameter; +struct GazeObjectSelectionResult; +struct GazeObjectSelectionInitializeParameter; +struct GazeObjectSelectionMemoryParameter; +struct GazeObjectSelectionMemoryInfo; + +struct CalibrationPoint { + float x; + float y; + float z; +}; + +static_assert(sizeof(CalibrationPoint) == 12); + +} + namespace { constexpr std::int32_t DeviceHandle = 0x0e010000; @@ -94,12 +119,12 @@ std::int32_t APS5_VABI sceHmd2Close(std::int32_t handle) { return 0; } -int APS5_VABI sceHmd2GazeGetResult() { +int APS5_VABI sceHmd2GazeGetResult(const Hmd2::GazeResultParameter* parameter, Hmd2::GazeResult* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2GazeGetResultForFoveatedRendering() { +int APS5_VABI sceHmd2GazeGetResultForFoveatedRendering(const Hmd2::GazeResultParameter* parameter, Hmd2::FoveatedRenderingResult* result) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -292,7 +317,9 @@ APS5_EXPORT("9KnNj-dyASg", Hmd2BeforeInitializeZero); APS5_EXPORT("Qta2bkstbFk", Hmd2BeforeInitializeZero); APS5_EXPORT("5ZcObnDRBXE", Hmd2BeforeInitializeZero); APS5_EXPORT("AvPNn0qx4WQ", Hmd2BeforeInitializeZero); -APS5_EXPORT("jp0apqNGgi8", Hmd2BeforeInitializeZero); +int APS5_VABI sceHmd2SetIpdOffset(std::uint8_t enabled, float offset) { + return BeforeInitialize(0, __func__); +} APS5_EXPORT("-kZT7X7OMg0", Hmd2BeforeInitializeZero); APS5_EXPORT("1ewLtng-SVk", Hmd2BeforeInitializeZero); APS5_EXPORT("B0b8DeIUGes", Hmd2BeforeInitializeZero); @@ -310,7 +337,9 @@ APS5_EXPORT("MjY1gBSaed0", Hmd2BeforeInitializeZero); APS5_EXPORT("evAMjJYHqW4", Hmd2BeforeInitializeZero); APS5_EXPORT("r93FLEZ3BBk", Hmd2BeforeInitializeZero); APS5_EXPORT("IdYURdEdNFo", Hmd2BeforeInitializeZero); -APS5_EXPORT("CVcL2Cu2AEI", Hmd2BeforeInitializeZero); +int APS5_VABI sceHmd2GazeActivateGazeTracking(std::int32_t mode) { + return BeforeInitialize(0, __func__); +} APS5_EXPORT("NmKIfVxr2s0", Hmd2BeforeInitializeZero); APS5_EXPORT("KeDLkkp62Gg", Hmd2BeforeInitializeZero); APS5_EXPORT("+aadu6-ra+A", Hmd2BeforeInitializeZero); @@ -347,14 +376,22 @@ APS5_EXPORT("TJ8lyRsjFY4", Hmd2BeforeInitializeError0004); APS5_EXPORT("-XQVcLnyX68", Hmd2BeforeInitializeError0004); APS5_EXPORT("giQwQoHhYYA", Hmd2BeforeInitializeError0004); APS5_EXPORT("zNc9IkgawMI", Hmd2BeforeInitializeError0004); -APS5_EXPORT("-tnG5UJPvAc", Hmd2BeforeInitializeError0004); -APS5_EXPORT("FPhFmG+jXuQ", Hmd2BeforeInitializeError0004); +int APS5_VABI sceHmd2GazeStartUserCalibration() { + return BeforeInitialize(Hmd2::Error0004, __func__); +} +int APS5_VABI sceHmd2GazeStopUserCalibration() { + return BeforeInitialize(Hmd2::Error0004, __func__); +} APS5_EXPORT("CKlkvNngofQ", Hmd2BeforeInitializeError0004); -APS5_EXPORT("yL1CZaciFWE", Hmd2BeforeInitializeError0004); +int APS5_VABI sceHmd2GazeSetUserCalibrationPoint(Hmd2::CalibrationPoint point) { + return BeforeInitialize(Hmd2::Error0004, __func__); +} APS5_EXPORT("qlzgyG3s0RY", Hmd2BeforeInitializeError0004); APS5_EXPORT("kTIDrDLzMv0", Hmd2BeforeInitializeError0004); APS5_EXPORT("9Gee0ef8a+U", Hmd2BeforeInitializeError0004); -APS5_EXPORT("ecZVjQdwmBI", Hmd2BeforeInitializeError0004); +int APS5_VABI sceHmd2GazeSetEnabledEye(std::int32_t eye) { + return BeforeInitialize(Hmd2::Error0004, __func__); +} std::int32_t APS5_VABI Hmd2BeforeInitializeParameterNull() { return BeforeInitialize(Hmd2::ParameterNull, __func__); @@ -367,14 +404,20 @@ APS5_EXPORT("J44idh9izZM", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("Ps0wAvYaa1E", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("kLnMR8oQ1MY", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("kMz4rbBxvjo", Hmd2BeforeInitializeParameterNull); -APS5_EXPORT("DIU1sFRRaiE", Hmd2BeforeInitializeParameterNull); -APS5_EXPORT("ZQvU4vDzZGQ", Hmd2BeforeInitializeParameterNull); +int APS5_VABI sceHmd2GazeGetCalibrationData(std::int32_t type, Hmd2::GazeCalibrationData* data) { + return BeforeInitialize(Hmd2::ParameterNull, __func__); +} +int APS5_VABI sceHmd2GazeSetUserCalibrationData(const Hmd2::GazeUserCalibrationData* data) { + return BeforeInitialize(Hmd2::ParameterNull, __func__); +} APS5_EXPORT("TJpyGGP9YEY", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("Dih+Odk+RC0", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("oaqdSJw+HVY", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("m35gtHuanXU", Hmd2BeforeInitializeParameterNull); APS5_EXPORT("CTM7QPLJF9Q", Hmd2BeforeInitializeParameterNull); -APS5_EXPORT("+rExHeC9lP0", Hmd2BeforeInitializeParameterNull); +int APS5_VABI sceHmd2GazeObjectSelectionInitialize(const Hmd2::GazeObjectSelectionInitializeParameter* parameter) { + return BeforeInitialize(Hmd2::ParameterNull, __func__); +} std::int32_t APS5_VABI Hmd2BeforeInitializeParameterInvalid() { return BeforeInitialize(Hmd2::ParameterInvalid, __func__); @@ -395,18 +438,30 @@ std::int32_t APS5_VABI Hmd2BeforeInitializeReprojectionNotInitialized() { APS5_EXPORT("d3SEIYTP6wc", Hmd2BeforeInitializeReprojectionNotInitialized); APS5_EXPORT("EVECbTvBezo", Hmd2BeforeInitializeReprojectionNotInitialized); APS5_EXPORT("ciusPdJKjSU", Hmd2BeforeInitializeReprojectionNotInitialized); -APS5_EXPORT("DIwqTkcZiJo", Hmd2BeforeInitializeReprojectionNotInitialized); -APS5_EXPORT("64ERZWVFBTE", Hmd2BeforeInitializeReprojectionNotInitialized); -APS5_EXPORT("z0+0gXUJWs8", Hmd2BeforeInitializeReprojectionNotInitialized); -APS5_EXPORT("rjAd4nCY4DM", Hmd2BeforeInitializeReprojectionNotInitialized); -APS5_EXPORT("Acyg4vTdGm0", Hmd2BeforeInitializeReprojectionNotInitialized); +int APS5_VABI sceHmd2ReprojectionSetUserEventStart() { + return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); +} +int APS5_VABI sceHmd2ReprojectionSetUserEventEnd() { + return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); +} +int APS5_VABI sceHmd2ReprojectionClearUserEventStart() { + return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); +} +int APS5_VABI sceHmd2ReprojectionClearUserEventEnd() { + return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); +} +int APS5_VABI sceHmd2ReprojectionGetCurrentOutput() { + return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); +} APS5_EXPORT("aGOWaEeg04I", Hmd2BeforeInitializeReprojectionNotInitialized); APS5_EXPORT("9up+EPhw1No", Hmd2BeforeInitializeReprojectionNotInitialized); std::int32_t APS5_VABI Hmd2BeforeInitializeError002C() { return BeforeInitialize(Hmd2::Error002C, __func__); } -APS5_EXPORT("V1kLQmP-17U", Hmd2BeforeInitializeError002C); +int APS5_VABI sceHmd2GazeObjectSelectionTerminate() { + return BeforeInitialize(Hmd2::Error002C, __func__); +} std::int32_t APS5_VABI Hmd2BeforeInitializeError1002() { return BeforeInitialize(Hmd2::Error1002, __func__); @@ -563,8 +618,14 @@ APS5_EXPORT("Tc9rasWfrm4", Hmd2CheckFirstThenError0004); APS5_EXPORT("ckTbKTX4KZg", Hmd2CheckFirstThenError0004); APS5_EXPORT("D7576PUbUjk", Hmd2CheckFirstThenError0004); APS5_EXPORT("tTJ04SrBT8U", Hmd2CheckFirstThenError0004); -APS5_EXPORT("sDP7hZ-P6OI", Hmd2CheckFirstThenError0004); -APS5_EXPORT("J+eVNbN9ndY", Hmd2CheckFirstThenError0004); +int APS5_VABI sceHmd2GazeGetDeviceStatus(std::int32_t* status) { + if (status == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(Hmd2::Error0004, __func__); +} +int APS5_VABI sceHmd2GazeGetUserCalibrationStatus(Hmd2::GazeUserCalibrationStatus* status) { + if (status == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(Hmd2::Error0004, __func__); +} APS5_EXPORT("kozvHBNyxf0", Hmd2CheckFirstThenError0004); APS5_EXPORT("638Tmg5y6U4", Hmd2CheckFirstThenError0004); @@ -576,7 +637,10 @@ APS5_EXPORT("8ETtFtp5zqY", Hmd2CheckFirstThenZero); APS5_EXPORT("CWPkBm3EXQA", Hmd2CheckFirstThenZero); APS5_EXPORT("R62liQQIAaA", Hmd2CheckFirstThenZero); APS5_EXPORT("-bcWLHsdzo0", Hmd2CheckFirstThenZero); -APS5_EXPORT("JngJzgNbu8U", Hmd2CheckFirstThenZero); +int APS5_VABI sceHmd2SetFieldOfView(const Hmd2::FieldOfView* fieldOfView) { + if (fieldOfView == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(0, __func__); +} APS5_EXPORT("xNBSz8xrGdk", Hmd2CheckFirstThenZero); APS5_EXPORT("yUVPfN-jRIU", Hmd2CheckFirstThenZero); APS5_EXPORT("BKPR3RZ5aO4", Hmd2CheckFirstThenZero); @@ -610,8 +674,16 @@ std::int32_t APS5_VABI Hmd2CheckFirstTwoThenParameterInvalid(const void* a0, con return BeforeInitialize(Hmd2::ParameterInvalid, __func__); } APS5_EXPORT("PPX68fNToFM", Hmd2CheckFirstTwoThenParameterInvalid); -APS5_EXPORT("LxPIfjHaY4c", Hmd2CheckFirstTwoThenParameterInvalid); -APS5_EXPORT("w8gBTTdg+po", Hmd2CheckFirstTwoThenParameterInvalid); +int APS5_VABI sceHmd2GazeObjectSelectionQueryMemory(const Hmd2::GazeObjectSelectionMemoryParameter* parameter, Hmd2::GazeObjectSelectionMemoryInfo* info) { + if (parameter == nullptr) return Hmd2::ParameterNull; + if (info == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(Hmd2::ParameterInvalid, __func__); +} +int APS5_VABI sceHmd2GazeObjectSelectionGetSelectedObject(const Hmd2::GazeObjectSelectionParameter* parameter, Hmd2::GazeObjectSelectionResult* result) { + if (parameter == nullptr) return Hmd2::ParameterNull; + if (result == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(Hmd2::ParameterInvalid, __func__); +} std::int32_t APS5_VABI Hmd2CheckSecondThenError002C(const void* a0, const void* a1) { (void)a0; @@ -647,7 +719,11 @@ std::int32_t APS5_VABI Hmd2CheckSecondThenParameterInvalid(const void* a0, const return BeforeInitialize(Hmd2::ParameterInvalid, __func__); } APS5_EXPORT("72U64E+0z6U", Hmd2CheckSecondThenParameterInvalid); -APS5_EXPORT("LCQzi5ILGrY", Hmd2CheckSecondThenParameterInvalid); +int APS5_VABI sceHmd2GazeGetCalibrationDataMaxSize(std::int32_t type, std::uint32_t* size) { + (void)type; + if (size == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(Hmd2::ParameterInvalid, __func__); +} std::int32_t APS5_VABI Hmd2CheckFirstThenError800d0203(const void* a0) { if (a0 == nullptr) return Hmd2::ParameterNull; @@ -702,7 +778,11 @@ std::int32_t APS5_VABI Hmd2CheckSecondThenInvalidHandle(const void* a0, const vo if (a1 == nullptr) return Hmd2::ParameterNull; return BeforeInitialize(Hmd2::InvalidHandle, __func__); } -APS5_EXPORT("wEHILc7TP04", Hmd2CheckSecondThenInvalidHandle); +int APS5_VABI sceHmd2GazeInsertRawDataWithHandle(std::int32_t handle, Hmd2::GazeRawData* data) { + (void)handle; + if (data == nullptr) return Hmd2::ParameterNull; + return BeforeInitialize(Hmd2::InvalidHandle, __func__); +} std::int32_t APS5_VABI Hmd2CheckSecondThenError1002(const void* a0, const void* a1) { (void)a0; diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index 1dacc21ad2..21b7585b25 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -83,3 +83,5 @@ - [Hmd2SlotUpTo2CheckSecondThenError002C](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console returns `0x8a72002c` for a first argument of 0 to 2 and `0x8a720009` for 3 to 5, 8, 16, 255 and -1; a null second argument returns `0x8a720008` even with an invalid first argument: `wQ44gBwBw44`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2IndexZeroCheckSecondThenZero](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console returns 0 for a first argument of 0 and `0x8a72100f` for 1 to 5, 8, 16, 255 and -1; a null second argument returns `0x8a720008` even with a nonzero first argument. Its third to sixth arguments answered `0x8a72100f` when null with a nonzero first argument, which does not separate them from the first-argument check, so they are not looked at: `wiv8+7ogrq0`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [FBUEhH9tQ1A](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - left unimplemented: before initialization it writes the 32-bit value 9 through its second argument and returns 0 when its first argument points at zeroed memory, but returns `0x8a720001` without writing when that memory holds 0xAA bytes, and its fourth to sixth arguments answer `0x8a720001` when null, so the first argument is an in-out structure whose fields are not known + +- [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte parameter and a 56-byte or 24-byte output. Object selection uses a 120-byte parameter and 56-byte result, a 24-byte initialization parameter, and 8-byte/16-byte memory-query structures. These payload fields remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. From 03e14bd74c8d5bcf9ea7d67a88581c113efcf3a7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:45:31 +0300 Subject: [PATCH 337/523] fix(hmd2): restore reprojection frame and event signatures --- core/libs/prx/libSceHmd2/Export.cpp | 45 ++++++++++++++++++++--------- docs/dev/debt/libSceHmd2.md | 5 ++-- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index e78ebbbe10..68fa34443f 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -12,6 +12,23 @@ namespace Hmd2 { +struct ReprojectionStatus; +struct ReprojectionOutput; +struct ReprojectionInitializeParameter; + +struct MirroringParameter { + void* workMemory; + void* displayMemory; +}; + +struct MirroringOption { + std::uint32_t mode; + std::uint32_t reserved[7]; +}; + +static_assert(sizeof(MirroringParameter) == 16); +static_assert(sizeof(MirroringOption) == 32); + struct GazeResultParameter; struct GazeResult; struct FoveatedRenderingResult; @@ -75,7 +92,7 @@ std::int32_t GetDeviceInformation(Hmd2::DeviceInformation* info) { extern "C" { -int APS5_VABI sceHmd2ReprojectionGetStatus() { +int APS5_VABI sceHmd2ReprojectionGetStatus(Hmd2::ReprojectionStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -155,7 +172,7 @@ std::int32_t APS5_VABI sceHmd2Open(std::int32_t userId, std::int32_t type, std:: return DeviceHandle; } -int APS5_VABI sceHmd2ReprojectionBeginFrame() { +int APS5_VABI sceHmd2ReprojectionBeginFrame(std::uint64_t frameId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -170,12 +187,12 @@ int APS5_VABI sceHmd2ReprojectionDisableVrMode() { return 0; } -int APS5_VABI sceHmd2ReprojectionEnableMirroring() { +int APS5_VABI sceHmd2ReprojectionEnableMirroring(const Hmd2::MirroringParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2ReprojectionEnableVrMode() { +int APS5_VABI sceHmd2ReprojectionEnableVrMode(std::uint64_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -188,12 +205,12 @@ Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionGetMirroringWorkMemorySizeAlign() { return {0x4000, 0x100}; } -int APS5_VABI sceHmd2ReprojectionGetPredictedDisplayTime() { +int APS5_VABI sceHmd2ReprojectionGetPredictedDisplayTime(std::uint64_t frameId, std::uint64_t* time) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2ReprojectionInitialize() { +int APS5_VABI sceHmd2ReprojectionInitialize(const Hmd2::ReprojectionInitializeParameter* parameter, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -207,12 +224,12 @@ Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionQueryDisplayBufferSizeAlign() { return {0x6000000, 0x10000}; } -int APS5_VABI sceHmd2ReprojectionSetAllowPositionalReprojection() { +int APS5_VABI sceHmd2ReprojectionSetAllowPositionalReprojection(std::uint8_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2ReprojectionSetMirroringOption() { +int APS5_VABI sceHmd2ReprojectionSetMirroringOption(const Hmd2::MirroringOption* option) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -232,7 +249,7 @@ int APS5_VABI sceHmd2ReprojectionSetRenderConfig() { return 0; } -int APS5_VABI sceHmd2ReprojectionSetTiming() { +int APS5_VABI sceHmd2ReprojectionSetTiming(std::int32_t timing) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -284,12 +301,12 @@ int APS5_VABI sceHmd2ReprojectionSet() { return 0; } -int APS5_VABI sceHmd2ReprojectionSetEventGpuEnd() { +int APS5_VABI sceHmd2ReprojectionSetEventGpuEnd(KernelEqueue queue, std::int32_t eventId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2ReprojectionSetEventGpuStart() { +int APS5_VABI sceHmd2ReprojectionSetEventGpuStart(KernelEqueue queue, std::int32_t eventId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -438,10 +455,10 @@ std::int32_t APS5_VABI Hmd2BeforeInitializeReprojectionNotInitialized() { APS5_EXPORT("d3SEIYTP6wc", Hmd2BeforeInitializeReprojectionNotInitialized); APS5_EXPORT("EVECbTvBezo", Hmd2BeforeInitializeReprojectionNotInitialized); APS5_EXPORT("ciusPdJKjSU", Hmd2BeforeInitializeReprojectionNotInitialized); -int APS5_VABI sceHmd2ReprojectionSetUserEventStart() { +int APS5_VABI sceHmd2ReprojectionSetUserEventStart(KernelEqueue queue, std::int32_t eventId) { return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); } -int APS5_VABI sceHmd2ReprojectionSetUserEventEnd() { +int APS5_VABI sceHmd2ReprojectionSetUserEventEnd(KernelEqueue queue, std::int32_t eventId) { return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); } int APS5_VABI sceHmd2ReprojectionClearUserEventStart() { @@ -450,7 +467,7 @@ int APS5_VABI sceHmd2ReprojectionClearUserEventStart() { int APS5_VABI sceHmd2ReprojectionClearUserEventEnd() { return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); } -int APS5_VABI sceHmd2ReprojectionGetCurrentOutput() { +int APS5_VABI sceHmd2ReprojectionGetCurrentOutput(Hmd2::ReprojectionOutput* output) { return BeforeInitialize(Hmd2::ReprojectionNotInitialized, __func__); } APS5_EXPORT("aGOWaEeg04I", Hmd2BeforeInitializeReprojectionNotInitialized); diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index 21b7585b25..47cbda8590 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -6,12 +6,11 @@ ### Unknown function info -- PPSA09030 imports from its `Media/Plugins/psvr2.prx` declared without parameters, signatures unknown, added without an implementation because the title fails to load the plugin without them: [sceHmd2ReprojectionGetStatus](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - [sceHmd2Open](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the handle is the value the console's driver returned to the first open; titles built with an SDK before 7.00 may open for any user, and the firmware's `sceMbusAddHandleByUserId` result for a user other than the initial one is unknown, so that case throws. 0x8a720006 for a second open is named `AlreadyOpened` from its use - [sceHmd2SetVibration](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the second parameter is checked against 25 only while a headset is connected; its name and unit are unknown. The field of view member names are assumed: of the firmware's default tangents the first is the widest and the last two are equal - PPSA01317 imports declared without parameters, signatures unknown: the [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) exports that throw and the remaining [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) exports. Names from the aerolib NID list - PPSA15878 imports declared without parameters, signatures unknown: the [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) reprojection and gaze exports, `sceVrTracker2Finalize`, `sceVrTracker2GetPlayAreaOrientedBoundingBox` and `sceVrTracker2LocateCoordinateSystem` in [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp), and the `scePadVrController` trigger effect exports in [libScePad](../../../core/libs/prx/libScePad/Export.cpp). Names from the aerolib NID list -- [libSceHmd2 gaze and reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - `sceHmd2ReprojectionSeeThroughDraw`, `sceHmd2ReprojectionSeeThroughIC`, `sceHmd2ReprojectionVideoOutEq`, `sceHmd2ReprojectionWaitShader`, `sceHmd2SeeThroughCallbackGetPoseDataFunc`, `sceHmd2ReprojectionClear`, `sceHmd2ReprojectionSet`, `sceHmd2ReprojectionSetEventGpuEnd`, `sceHmd2ReprojectionSetEventGpuStart`, `sceHmd2RpeorjectionSetParam` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws +- [libSceHmd2 gaze and reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - `sceHmd2ReprojectionSeeThroughDraw`, `sceHmd2ReprojectionSeeThroughIC`, `sceHmd2ReprojectionVideoOutEq`, `sceHmd2ReprojectionWaitShader`, `sceHmd2SeeThroughCallbackGetPoseDataFunc`, `sceHmd2ReprojectionClear`, `sceHmd2ReprojectionSet`, `sceHmd2RpeorjectionSetParam` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws - [sceHmd2InternalSetPortStatus](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - unknown signature - [sceHmd2InternalLedDetectorStartBySlot](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - unknown signature - [sceHmd2InternalImageStartBySlot](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - unknown signature @@ -85,3 +84,5 @@ - [FBUEhH9tQ1A](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - left unimplemented: before initialization it writes the 32-bit value 9 through its second argument and returns 0 when its first argument points at zeroed memory, but returns `0x8a720001` without writing when that memory holds 0xAA bytes, and its fourth to sixth arguments answer `0x8a720001` when null, so the first argument is an in-out structure whose fields are not known - [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte parameter and a 56-byte or 24-byte output. Object selection uses a 120-byte parameter and 56-byte result, a 24-byte initialization parameter, and 8-byte/16-byte memory-query structures. These payload fields remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. + +- [libSceHmd2 reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - frame identifiers and predicted display times are 64-bit. Event setters accept a kernel event queue and 32-bit event ID, consumed by sceKernelTriggerUserEvent. Mirroring uses two memory pointers and a separate 32-byte mode/reserved option. Status and current-output queries write 104 and 32 bytes; those field layouts and the reprojection initialization fields remain incomplete. Positional-reprojection mode is a byte with values 0 through 5, not a boolean. From 8d202ee697a6927f8a7cd80dbb145dfb8f247813 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:46:48 +0300 Subject: [PATCH 338/523] fix(hmd): restore device screen and capture signatures --- core/libs/prx/libSceHmd/Export.cpp | 54 +++++++++++++++++++----------- docs/dev/debt/libSceHmd.md | 22 ++---------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index fbdfeacca6..693276b1a2 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -3,6 +3,20 @@ #include +struct HmdVr2dData; +struct HmdPuPositionParameter; +struct HmdOpticalParameter; +struct HmdCrashDumpInfo; +struct HmdScreenOutputParameter; +struct HmdScreenFadeParameter; +struct HmdScreenStatus; +struct HmdCaptureInitializeParameter; +struct HmdCaptureInfo; +struct HmdLiveCaptureParameter; +struct HmdReprojectionDebugInfo; +struct HmdReprojectionMultilayerDebugInfo; +struct HmdReprojectionCallbackParameter; + namespace { std::atomic initialized{false}; @@ -127,7 +141,7 @@ int APS5_VABI sceHmdInternalSetVirtualDisplaySize(std::uint8_t size) { return 0; } -int APS5_VABI sceHmdInternalSocialScreenSetOutput(void) { +int APS5_VABI sceHmdInternalSocialScreenSetOutput(std::uint32_t output, const HmdScreenOutputParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -220,7 +234,7 @@ int APS5_VABI sceHmdInternalGetRequiredPUPVersion(std::uint32_t* firstVersion, s return 0; } -int APS5_VABI sceHmdReprojectionSetCallback(void) { +int APS5_VABI sceHmdReprojectionSetCallback(const HmdReprojectionCallbackParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -259,7 +273,7 @@ int APS5_VABI sceHmdReprojectionStartMultilayer(void) { return 0; } -int APS5_VABI sceHmdInternalPushVr2dData(void) { +int APS5_VABI sceHmdInternalPushVr2dData(const HmdVr2dData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -284,7 +298,7 @@ int APS5_VABI sceHmdInternalCrashReportReadDataSize(std::uint32_t* dataSize) { return 0; } -int APS5_VABI sceHmdInternalGetVr2dData(void) { +int APS5_VABI sceHmdInternalGetVr2dData(HmdVr2dData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -294,12 +308,12 @@ int APS5_VABI sceHmdInternalAnotherScreenSendAudio(const HmdScreenData* data) { return 0; } -int APS5_VABI sceHmdInternalGetPuPositionParam(void) { +int APS5_VABI sceHmdInternalGetPuPositionParam(HmdPuPositionParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdReprojectionInitializeCapture(void) { +int APS5_VABI sceHmdReprojectionInitializeCapture(const HmdCaptureInitializeParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -330,7 +344,7 @@ int APS5_VABI sceHmdReprojectionSetDisplayBuffers(void) { return 0; } -int APS5_VABI sceHmdInternalAnotherScreenSetFadeAndSwitch(void) { +int APS5_VABI sceHmdInternalAnotherScreenSetFadeAndSwitch(const HmdScreenFadeParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -359,7 +373,7 @@ int APS5_VABI sceHmdInternalSetS3dPassMode(std::uint8_t mode) { return 0; } -int APS5_VABI sceHmdReprojectionStopCapture(void) { +int APS5_VABI sceHmdReprojectionStopCapture(HmdCaptureInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -408,7 +422,7 @@ int APS5_VABI sceHmdInternalSetPortConnection(std::uint64_t connection) { return 0; } -int APS5_VABI sceHmdReprojectionDebugGetLastInfo(void) { +int APS5_VABI sceHmdReprojectionDebugGetLastInfo(HmdReprojectionDebugInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -498,12 +512,12 @@ int APS5_VABI sceHmdInternalSetHmuPowerControl(std::uint8_t control) { return 0; } -int APS5_VABI sceHmdInternalBindDeviceWithUserId(void) { +int APS5_VABI sceHmdInternalBindDeviceWithUserId(std::int32_t handle, std::int32_t userId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalGetHmuSerialNumber(void) { +int APS5_VABI sceHmdInternalGetHmuSerialNumber(std::uint8_t* serialNumber) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -518,17 +532,17 @@ int APS5_VABI sceHmdInternalCheckS3dPassModeAvailable(bool* available) { return 0; } -int APS5_VABI sceHmdInternalAnotherScreenGetAudioStatus(void) { +int APS5_VABI sceHmdInternalAnotherScreenGetAudioStatus(HmdScreenStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalSeparateModeGetAudioStatus(void) { +int APS5_VABI sceHmdInternalSeparateModeGetAudioStatus(HmdScreenStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdReprojectionStartLiveCapture(void) { +int APS5_VABI sceHmdReprojectionStartLiveCapture(const HmdLiveCaptureParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -548,7 +562,7 @@ int APS5_VABI sceHmdReprojectionFinalize(void) { return 0; } -int APS5_VABI sceHmdInternalSocialScreenSetFadeAndSwitch(void) { +int APS5_VABI sceHmdInternalSocialScreenSetFadeAndSwitch(const HmdScreenFadeParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -633,7 +647,7 @@ int APS5_VABI sceHmdInternalCrashReportCancel(void) { return 0; } -int APS5_VABI sceHmdInternalSeparateModeGetVideoStatus(void) { +int APS5_VABI sceHmdInternalSeparateModeGetVideoStatus(HmdScreenStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -703,7 +717,7 @@ int APS5_VABI sceHmdReprojectionUnsetDisplayBuffers(void) { return 0; } -int APS5_VABI sceHmdInternalGetHmuOpticalParam(void) { +int APS5_VABI sceHmdInternalGetHmuOpticalParam(HmdOpticalParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -777,7 +791,7 @@ int APS5_VABI sceHmdInternalMapSharedMemory(void) { return 0; } -int APS5_VABI sceHmdInternalGetCrashDumpInfo(void) { +int APS5_VABI sceHmdInternalGetCrashDumpInfo(HmdCrashDumpInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -859,7 +873,7 @@ int APS5_VABI sceHmdReprojectionStop(void) { return 0; } -int APS5_VABI sceHmdInternalAnotherScreenGetVideoStatus(void) { +int APS5_VABI sceHmdInternalAnotherScreenGetVideoStatus(HmdScreenStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -884,7 +898,7 @@ int APS5_VABI sceHmdInternalGetIpdSettingEnableForSystemService(std::uint8_t* en return 0; } -int APS5_VABI sceHmdReprojectionDebugGetLastInfoMultilayer(void) { +int APS5_VABI sceHmdReprojectionDebugGetLastInfoMultilayer(HmdReprojectionMultilayerDebugInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 73cc19fd6c..2c4ba6528c 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -6,55 +6,37 @@ ### Unknown function info +- [libSceHmd internal and capture exports](../../../core/libs/prx/libSceHmd/Export.cpp) - input/output directions and argument registers are recovered for shared VR2D data, device binding, optical and serial queries, screen status and capture operations. VR2D records are 128 bytes, optical output 16, serial output 16, PU position output 20, crash-dump output 68, audio/video status 32, and reprojection debug outputs 216 or 1136. Payload field meanings and capture callback contracts remain incomplete; these structures use forward declarations. + - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count -- [sceHmdInternalSocialScreenSetOutput](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalDfuGetStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionSetCallback](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalPushVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetVr2dData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetPuPositionParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionInitializeCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionSetDisplayBuffers](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalMirroringModeSetAspectDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStopCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionDebugGetLastInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalBindDeviceWithUserId](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetHmuSerialNumber](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetWideNearCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSeparateModeGetAudioStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStartLiveCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSocialScreenSetFadeAndSwitch](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalCrashReportCancel](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSeparateModeGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSeparateModeOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetHmuOpticalParam](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetCrashDumpInfo](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalAnotherScreenGetVideoStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionDebugGetLastInfoMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature From 86b8fe3a253267f07f307648a833276027d5e376 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:48:37 +0300 Subject: [PATCH 339/523] fix(pad): restore report tracker and vr trigger signatures --- core/libs/prx/libScePad/Export.cpp | 49 +++++++++++++++++++----------- docs/dev/debt/libScePad.md | 12 ++------ 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index ab3e74552d..52d915d3ca 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -11,6 +11,21 @@ #include "prx/libScePad/include/Pad.hpp" #include "prx/libScePad/include/PadState.hpp" +struct PadDataExtended; +struct PadVrControllerData; +struct PadVrControllerDeviceInformation; +struct PadVrTriggerEffect; +struct PadVrTriggerEffects; +struct PadTriggerEffectExtended; +struct PadExtensionReport; +struct PadExtensionUnitInfo; +struct PadTriggerInternalState; +struct PadTrackerData; +struct PadBlasterTrackerData; +struct PadAstonTrackerData; +struct PadUiData; +struct PadUiTuningData; + namespace { constexpr int PAD_ERROR_DEVICE_NOT_CONNECTED = static_cast(0x80920007); @@ -183,11 +198,11 @@ int APS5_VABI scePadOpenExt(int userId, int type, int index, const void* param) return PAD_ERROR_DEVICE_NOT_CONNECTED; } -int APS5_VABI scePadReadExt() { NotImplemented_nid_no_patch(__func__); return 0; } +int APS5_VABI scePadReadExt(std::int32_t handle, PadDataExtended* data, std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetFeatureReport() { NotImplemented_nid_no_patch(__func__); return 0; } +int APS5_VABI scePadGetFeatureReport(std::int32_t handle, std::uint8_t reportId, void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetFeatureReport() { NotImplemented_nid_no_patch(__func__); return 0; } +int APS5_VABI scePadSetFeatureReport(std::int32_t handle, std::uint8_t reportId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } int APS5_VABI scePadOutputReport() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -283,12 +298,12 @@ int APS5_VABI scePadSetVibrationTriggerEffectWeakWhileEmbeddedMicInUse(bool enab return PAD_OK; } -int APS5_VABI scePadVrControllerGetDeviceInformation() { +int APS5_VABI scePadVrControllerGetDeviceInformation(std::int32_t handle, PadVrControllerDeviceInformation* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVrControllerRead() { +int APS5_VABI scePadVrControllerRead(std::int32_t handle, PadVrControllerData* data, std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -305,12 +320,12 @@ int APS5_VABI scePadVrControllerReadState(int handle, void* state) { return 0; } -int APS5_VABI scePadVrControllerSetTriggerEffect() { +int APS5_VABI scePadVrControllerSetTriggerEffect(std::int32_t handle, const PadVrTriggerEffect* effect) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVrControllerSetTriggerEffects() { +int APS5_VABI scePadVrControllerSetTriggerEffects(std::int32_t leftHandle, std::int32_t rightHandle, const PadVrTriggerEffects* effects) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -347,7 +362,7 @@ int APS5_VABI scePadGetAccessibilityControllerState(int handle, std::uint32_t* s return PAD_OK; } -int APS5_VABI scePadSetTriggerEffectExt(void) { +int APS5_VABI scePadSetTriggerEffectExt(std::int32_t handle, const PadTriggerEffectExtended* effect) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -357,17 +372,17 @@ int APS5_VABI scePadLastLotusButtonData(void) { return 0; } -int APS5_VABI scePadReadStateExt(void) { +int APS5_VABI scePadReadStateExt(std::int32_t handle, PadDataExtended* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetExtensionReport(void) { +int APS5_VABI scePadSetExtensionReport(std::int32_t handle, const PadExtensionReport* report) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetExtensionUnitInfo(void) { +int APS5_VABI scePadGetExtensionUnitInfo(std::int32_t handle, PadExtensionUnitInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -536,7 +551,7 @@ int APS5_VABI scePadSetTriggerEffectStrength(std::int32_t handle, std::uint32_t return 0; } -int APS5_VABI scePadGetTriggerEffectInternalState(void) { +int APS5_VABI scePadGetTriggerEffectInternalState(std::int32_t handle, PadTriggerInternalState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -586,12 +601,12 @@ int APS5_VABI scePadGetUsbDetectionState(void) { return 0; } -int APS5_VABI scePadUiReadState(void) { +int APS5_VABI scePadUiReadState(std::int32_t handle, PadUiData* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadUiReadStateForTuning(void) { +int APS5_VABI scePadUiReadStateForTuning(std::int32_t handle, PadUiTuningData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -791,12 +806,12 @@ int APS5_VABI scePadSetLightBarForTracker(std::int32_t handle, const PadLightBar return 0; } -int APS5_VABI scePadReadForTracker(void) { +int APS5_VABI scePadReadForTracker(std::int32_t handle, PadTrackerData* data, std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadReadBlasterForTracker(void) { +int APS5_VABI scePadReadBlasterForTracker(std::int32_t handle, PadBlasterTrackerData* data, std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -856,7 +871,7 @@ int APS5_VABI scePadIsMoveReproductionModel(std::int32_t handle) { return 0; } -int APS5_VABI scePadReadAstonForTracker(void) { +int APS5_VABI scePadReadAstonForTracker(std::int32_t handle, PadAstonTrackerData* data, std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 738e21a092..fdd00cf961 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -2,6 +2,8 @@ ### Unknown function info +- [libScePad report and tracking exports](../../../core/libs/prx/libScePad/Export.cpp) - read, extended-read, tracker, UI, VR-controller and report argument registers are recovered. Feature reports use a byte report ID and 64-bit byte count, capped at 64. VR trigger effects consume a 56-byte record; the dual-controller call uses two handles and a mask with records at offsets 8 and 64. Remaining payload field layouts use incomplete declarations, including extended samples and kernel-owned extension reports. + - [scePadUnknown_n3kSX62fgNo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `n3kSX62fgNo`) - the name is unknown, and the 0 returned after `scePadInit` is inferred, not measured. In the OS 10.01 system module the function takes no arguments, returns `0x80920005` before `scePadInit`, and otherwise sends a per-process value of 1 to ShellCore over Mbus, returning `0x809200ff` if that call fails and 0 if it succeeds, with or without an open handle. The #3177 probe measured `0x809200ff` in a process that is not a game; a game process is assumed to get 0. The ShellCore setting it changes is unknown and not modelled - [scePadSetAngularVelocityDeadbandState](../../../core/libs/prx/libScePad/src/PadState.cpp) (libScePad) - with the deadband on, each angular velocity axis whose magnitude is below 0.017453292 rad/s (1 degree per second) reads as 0, before the orientation is integrated; it starts off. Threshold, per-axis test and default follow [duaLib](https://github.com/WujekFoliarz/duaLib/blob/03bad1bea2a36561b520846776f2a07ced6773e0/src/source/duaLib.cpp#L44), which reimplements the PC `libScePad.dll`; the console's threshold is not measured, and the deadband is kept per library rather than per handle since one pad handle exists - [scePadOpen and scePadOpenExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - special ports (type 2) return `DEVICE_NOT_CONNECTED` (0x80920007) because the host has no special controller, and `scePadGetHandle` has no handle for them; whether the console opens a handle and reports the device as disconnected later is unknown. scePadOpenExt handles only special ports. PPSA01317 passes a 16-byte param that names the device by vendor and product id (`b7 0e 04 0e ...` for 0x0eb7:0x0e04) and skips the user on any negative result. Other port types throw @@ -11,11 +13,7 @@ - [scePadIsRemoteController](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false - [scePadSetAngularVelocityBiasCorrectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled - [scePadGetAccessibilityControllerState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the value a console reports for a DualSense is not measured; the emulated pad, which is not an Access controller, reports 0. The name hashes to NID `zBsgF0q8DIM`. In the OS 10.01 system module the `(int handle, uint32_t* state)` function returns `0x80920005` before `scePadInit`, then `INVALID_ARG` for a NULL `state` and `INVALID_HANDLE` for a bad handle, and otherwise asks the pad driver for a per-device value of the device behind the handle: 0 when no device is behind it, 1 when the driver has no answer, else the driver's value. What the values mean is unknown -- [scePadSetTriggerEffectExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadLastLotusButtonData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadReadStateExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetExtensionReport](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetExtensionUnitInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadOpenExt2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadDeviceOpen](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDataInternal](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature @@ -24,10 +22,7 @@ - [scePadShareOutputDataWithFunctionType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetVersionInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetButtonRemappingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetTriggerEffectInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetUsbDetectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadUiReadState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadUiReadStateForTuning](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetStickTuningParam](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetTriggerTuningParam](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetProfileHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature @@ -44,9 +39,6 @@ - [scePadResetLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadReadForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadReadBlasterForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadReadAstonForTracker](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDeviceInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProcessFocus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVertualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From 2f8bf3c16ecfb0f1ec2b68bc9dad8470987f329b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:49:04 +0300 Subject: [PATCH 340/523] fix(pad): restore profile and device setting signatures --- core/libs/prx/libScePad/Export.cpp | 52 +++++++++++++++++++----------- docs/dev/debt/libScePad.md | 21 ++---------- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 52d915d3ca..4eab1575c4 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -11,6 +11,20 @@ #include "prx/libScePad/include/Pad.hpp" #include "prx/libScePad/include/PadState.hpp" +struct PadStickTuningParameter; +struct PadTriggerTuningParameter; +struct PadHuntProfile; +struct PadLeonardoProfile; +struct PadProfileInfo; +struct PadActiveProfileInfo; +struct PadSavedSettingInfo; +struct PadUsbDetectionState; +struct PadVrUsbConnectionStatus; +struct PadDeviceInfo; +struct PadLicenseControllerInformation; +struct PadRemoteSetting; +struct PadVirtualTriggerSetting; +struct PadProcessFocusParameter; struct PadDataExtended; struct PadVrControllerData; struct PadVrControllerDeviceInformation; @@ -486,12 +500,12 @@ int APS5_VABI scePadGetCapability(std::int32_t deviceId, std::uint32_t* capabili return 0; } -int APS5_VABI scePadShareOutputData(void) { +int APS5_VABI scePadShareOutputData(std::int32_t source, std::int32_t destination) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadShareOutputDataWithFunctionType(void) { +int APS5_VABI scePadShareOutputDataWithFunctionType(std::int32_t source, std::int32_t destination, std::uint32_t functionType) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -596,7 +610,7 @@ int APS5_VABI scePadSetLotusDisableIrKey(std::int32_t handle) { return 0; } -int APS5_VABI scePadGetUsbDetectionState(void) { +int APS5_VABI scePadGetUsbDetectionState(std::int32_t deviceId, PadUsbDetectionState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -616,17 +630,17 @@ int APS5_VABI scePadSetStickTriggerTuningMode(std::int32_t handle, std::uint32_t return 0; } -int APS5_VABI scePadSetStickTuningParam(void) { +int APS5_VABI scePadSetStickTuningParam(std::int32_t handle, std::uint32_t stick, const PadStickTuningParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetTriggerTuningParam(void) { +int APS5_VABI scePadSetTriggerTuningParam(std::int32_t handle, const PadTriggerTuningParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetProfileHunt(void) { +int APS5_VABI scePadGetProfileHunt(std::int32_t handle, std::int32_t profileId, PadHuntProfile* profile) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -636,17 +650,17 @@ int APS5_VABI scePadSetProfileHunt(void) { return 0; } -int APS5_VABI scePadResetHunt(void) { +int APS5_VABI scePadResetHunt(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetProfileInfo(void) { +int APS5_VABI scePadGetProfileInfo(std::int32_t deviceId, std::int32_t profileId, PadProfileInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetActiveProfileInfo(void) { +int APS5_VABI scePadGetActiveProfileInfo(std::int32_t handle, PadActiveProfileInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -666,17 +680,17 @@ int APS5_VABI scePadSetActiveProfileSlot(std::int32_t deviceId, std::uint32_t sl return 0; } -int APS5_VABI scePadGetSavedSettingInfo(void) { +int APS5_VABI scePadGetSavedSettingInfo(std::int32_t handle, PadSavedSettingInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetSavedSettingInfo(void) { +int APS5_VABI scePadSetSavedSettingInfo(std::int32_t handle, const PadSavedSettingInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVrControllerGetPhysicalUsbConnectionStatus(void) { +int APS5_VABI scePadVrControllerGetPhysicalUsbConnectionStatus(PadVrUsbConnectionStatus* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -696,7 +710,7 @@ int APS5_VABI scePadSetControllerTypeLeonardo(std::int32_t deviceId, std::int32_ return 0; } -int APS5_VABI scePadGetProfileLeonardo(void) { +int APS5_VABI scePadGetProfileLeonardo(std::int32_t handle, std::int32_t profileId, PadLeonardoProfile* profile) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -706,7 +720,7 @@ int APS5_VABI scePadSetProfileLeonardo(void) { return 0; } -int APS5_VABI scePadResetLeonardo(void) { +int APS5_VABI scePadResetLeonardo(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -906,12 +920,12 @@ int APS5_VABI scePadDisconnectDevice(std::int32_t deviceId) { return 0; } -int APS5_VABI scePadGetDeviceInfo(void) { +int APS5_VABI scePadGetDeviceInfo(std::int32_t deviceId, PadDeviceInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadSetProcessFocus(void) { +int APS5_VABI scePadSetProcessFocus(const PadProcessFocusParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -941,12 +955,12 @@ int APS5_VABI scePadVirtualDeviceInsertTriggerInternalState(void) { return 0; } -int APS5_VABI scePadVirtualDeviceGetRemoteSetting(void) { +int APS5_VABI scePadVirtualDeviceGetRemoteSetting(std::int32_t deviceId, PadRemoteSetting* setting) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVirtualDeviceSetTriggerEffectSetting(void) { +int APS5_VABI scePadVirtualDeviceSetTriggerEffectSetting(std::int32_t handle, const PadVirtualTriggerSetting* setting) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -976,7 +990,7 @@ int APS5_VABI scePadEnablePs4CompatibleMode(std::int32_t handle, bool enabled) { return 0; } -int APS5_VABI scePadGetLicenseControllerInformation(void) { +int APS5_VABI scePadGetLicenseControllerInformation(std::int32_t handle, PadLicenseControllerInformation* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index fdd00cf961..81f69445a0 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -2,6 +2,8 @@ ### Unknown function info +- [libScePad profile and device-setting exports](../../../core/libs/prx/libScePad/Export.cpp) - profile getters, tuning, saved settings, USB state, shared output and virtual-device setting arguments are recovered. Hunt and Leonardo reset each consume only a handle; physical VR USB status consumes only an output pointer. Remote settings output is 64 bytes and license-controller information is 1224 bytes. Kernel ioctl payload fields remain incomplete. + - [libScePad report and tracking exports](../../../core/libs/prx/libScePad/Export.cpp) - read, extended-read, tracker, UI, VR-controller and report argument registers are recovered. Feature reports use a byte report ID and 64-bit byte count, capped at 64. VR trigger effects consume a 56-byte record; the dual-controller call uses two handles and a mask with records at offsets 8 and 64. Remaining payload field layouts use incomplete declarations, including extended samples and kernel-owned extension reports. - [scePadUnknown_n3kSX62fgNo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `n3kSX62fgNo`) - the name is unknown, and the 0 returned after `scePadInit` is inferred, not measured. In the OS 10.01 system module the function takes no arguments, returns `0x80920005` before `scePadInit`, and otherwise sends a per-process value of 1 to ShellCore over Mbus, returning `0x809200ff` if that call fails and 0 if it succeeds, with or without an open handle. The #3177 probe measured `0x809200ff` in a process that is not a game; a game process is assumed to get 0. The ShellCore setting it changes is unknown and not modelled @@ -18,37 +20,18 @@ - [scePadDeviceOpen](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetDataInternal](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetLightBarBlinking](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadShareOutputData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadShareOutputDataWithFunctionType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetVersionInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetButtonRemappingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetUsbDetectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetStickTuningParam](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetTriggerTuningParam](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetProfileHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProfileHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadResetHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetProfileInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetActiveProfileInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetSavedSettingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetSavedSettingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVrControllerGetPhysicalUsbConnectionStatus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadParseUniqueDataLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadResetLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetDeviceInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetProcessFocus](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVertualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceInsertTriggerInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceGetRemoteSetting](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceSetTriggerEffectSetting](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetVirtualDeviceComponentInformation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetLicenseControllerInformation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadReadHistory](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadRemoteControlInsertDataForAston](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature From f0e10376bb39331d63cd832bdd19c465f563adc2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:50:58 +0300 Subject: [PATCH 341/523] fix(http): restore epoll and diagnostic query signatures --- core/libs/prx/libSceHttp/Export.cpp | 13 ++++++++----- docs/dev/debt/libSceHttp.md | 15 ++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 092e6d3fb5..e6e88e47a5 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -24,6 +24,9 @@ // No network is emulated: contexts, templates and requests can be created, but any request // that would touch the network fails with the library's network error. static std::atomic g_nextHandle{1}; +struct HttpConnectionStat; +struct HttpRequestStat; + using HttpRequestStatusCallback = void (APS5_VABI *)(int, int, void*); struct RequestStatusRegistration { HttpRequestStatusCallback callback; @@ -706,12 +709,12 @@ int APS5_VABI sceHttpDbgShowMemoryPoolStat(void) { return 0; } -int APS5_VABI sceHttpDbgGetConnectionStat(void) { +int APS5_VABI sceHttpDbgGetConnectionStat(std::int32_t connectionId, HttpConnectionStat* stat) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpDbgGetRequestStat(void) { +int APS5_VABI sceHttpDbgGetRequestStat(std::int32_t requestId, HttpRequestStat* stat) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -731,12 +734,12 @@ int APS5_VABI sceHttpGetAuthEnabled(int id, int* enabled) { return 0; } -int APS5_VABI sceHttpSetEpollId(void) { +int APS5_VABI sceHttpSetEpollId(std::int32_t id, std::int32_t epollId, void* userArg) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttpGetEpollId(void) { +int APS5_VABI sceHttpGetEpollId(std::int32_t id, std::int32_t* epollId, void** userArg) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -837,7 +840,7 @@ int APS5_VABI sceHttpGetRegisteredCtxIds(int contextId, int* netPoolId, int* ssl return 0; } -int APS5_VABI sceHttpGetConnectionStat(void) { +int APS5_VABI sceHttpGetConnectionStat(std::int32_t connectionId, HttpConnectionStat* stat) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 8163e0c50f..59b4ddb5b2 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -8,6 +8,8 @@ ### Unknown function info - [HTTP object options](../../../core/libs/prx/libSceHttp/src/Options.cpp) (libSceHttp) - a template starts with redirects followed, blocking calls and no `Accept-Encoding: gzip`, and a connection or request copies its parent's options when it is created. The signatures of the option calls, these defaults, the copying and the order of the checks (`BEFORE_INIT` first; the last `sceHttpTerm` ends every object's options) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/network/http.cpp) (PS4) and are not measured on a PS5. `sceHttpSetDefaultAcceptEncodingGZIPEnabled` sets the gzip value for templates created later in that context, where shadPS4 keeps one value for the library, and `sceHttpUnsetEpoll` takes a template or connection id, where shadPS4 looks only at requests. The options change no request, as none reaches the network +- [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - connection and request diagnostic signatures are recovered; their outputs contain four and three 32-bit words, respectively. Field semantics remain incomplete. Epoll setters/getters distinguish a 32-bit epoll ID from a pointer-sized handle, and use a pointer-sized user argument. The supplied firmware getter dispatch does not handle the pointer-handle selector used by GetEpoll, so its intended successful behavior is not recoverable from this implementation. + - [sceHttpUriBuild](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) retains `//` for a non-opaque URI independently of the selected fields, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a8a70da595da2a96e0410309472ba4a7eb558144/src/core/libraries/network/http.cpp#L3551-L3558). Black Myth: Wukong builds its `Scheme` header with option 1 and expects `http://` or `https://`. Other partial field combinations follow the PS4 reference and remain unverified on PS5 hardware. - [sceHttpSetCookieEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) - the two-argument signature and 0/1 enable values follow [VitaSDK](https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h) and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp); PPSA01521 passes 0 to disable cookies. Disabling succeeds because the offline HTTP backend has no cookie engine; enabling throws. Invalid enable values use the same error as `sceHttpSetInflateGZIPEnabled`. Like the existing offline HTTP setters, the handle is not validated; requests still fail with the network error. Console behavior is unverified - [sceHttpParseResponseHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); the result, value bounds and error codes follow a payload process on a PS5 (firmware 13.60). Unmeasured there: a bare carriage return, and vertical tab or form feed around the value, which are skipped as blanks before it as before. A value end above `INT_MAX` throws instead of wrapping @@ -23,17 +25,12 @@ - [sceHttpUriCopy](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - read from the disassembly of libSceHttp.sprx on firmware 13.60, not measured on a console and not compared with other emulators. The arguments are (out, src, pool, prepare, require), `prepare` before `require`, the reverse of `sceHttpUriParse`. A null source returns `ERROR_INVALID_URL`; without an output or a pool, a null `require` returns `ERROR_INVALID_VALUE`, otherwise the size is only reported. Only the scheme, hostname, path, username and password are copied, in that order, each with its terminator (a missing one becomes an empty string and takes one byte), and the port; the query, the fragment and the opaque flag stay 0. A pool that is too small clears the output and the pool and returns `ERROR_OUT_OF_MEMORY` (0x80431022, the console's out-of-size error) - [sceHttpDbgEnableProfile](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpDbgSetPrintf](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgShowStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgShowConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgShowRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgShowMemoryPoolStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgGetRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetEpollId](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature +- [sceHttpDbgShowStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. +- [sceHttpDbgShowConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. +- [sceHttpDbgShowRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. +- [sceHttpDbgShowMemoryPoolStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. - [sceHttpSetCookieSendCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature - [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature ### Functional From 62e4e204f1b35fdc4687be73210f30b6131385ee Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:52:04 +0300 Subject: [PATCH 342/523] fix(audioout): restore device id open argument slots --- core/libs/prx/libSceAudioOut/src/AudioOut.cpp | 2 +- docs/dev/debt/libSceAudioOut.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp index c6a9930bb6..41c2ad30a8 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp @@ -599,7 +599,7 @@ int APS5_VABI sceAudioOutGetPortState(int handle, AudioOutPortState* state) { return 0; } -int APS5_VABI sceAudioOutDeviceIdOpen() { +int APS5_VABI sceAudioOutDeviceIdOpen(std::uint64_t deviceId, std::int32_t type, std::int32_t index, std::uint32_t length, std::int32_t frequency, std::uint32_t parameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAudioOut.md b/docs/dev/debt/libSceAudioOut.md index 80f284769c..dd5aa7cfb3 100644 --- a/docs/dev/debt/libSceAudioOut.md +++ b/docs/dev/debt/libSceAudioOut.md @@ -21,7 +21,7 @@ - [sceAudioOut2PortSetAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Port.cpp) (libSceAudioOut) - each PCM buffer set (attribute 0) is taken as one grain: buffers set between two pushes are queued and played one per grain, at most 16 queued. A grain with no new buffer repeats the last one only when the port has always been given that same buffer (rewritten in place); a port whose buffers move on plays silence instead. Inferred from PPSA12544, whose object port (type 6) is given one grain of a 64-grain ring per set, often two or three between pushes and then none for a few; the console behaviour for several sets per push and for a push without a new buffer is not documented - [Legacy vibration ports](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp): PPSA99902's [public input wrapper](https://github.com/Nazky/UnleashedRecomp-Prospero/blob/2ce067a1d51bb58670c7ee4a3aed893c504da805/UnleashedRecomp/hid/driver/sdl_hid.cpp#L316) opens type 10 with 256 stereo S16 or F32 frames at 48000 Hz. Mono duplicates its channel as AudioOut2 does; wider vibration layouts throw. Native controller output uses AudioOut2's four-channel layout, or its six-channel PulseAudio mapping. Without a controller sound card the existing RMS motor transform is applied to segments of at most 256 frames, scheduled by the legacy queue's frame count and rate; drain and close remove only that port's segments. Concurrent sources combine by the maximum per motor, and the pad consumer uses this approximation only for controllers other than DualSense. This host timing and motor model is not a console measurement or waveform reproduction; legacy native output is unmeasured on hardware, and user routing retains the single-controller limitation. AudioOut2 retains its separate 80 ms peak hold. -- [sceAudioOutDeviceIdOpen](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - imported by PPSA01317, which fails to load without it; declared without parameters, signature unknown. It opens a port on a given audio device; which device ids exist and how they map to host outputs is unknown, so it throws +- [sceAudioOutDeviceIdOpen](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - the native wrapper consumes a 64-bit device ID, port type, sample count, frequency and format from argument slots 1, 2, 4, 5 and 6. Slot 3 is discarded, with the common open API index position preserved. Device enumeration and mapping to host outputs remain unimplemented; the throwing body is unchanged. - [sceAudioOut2PortGetState](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Port.cpp) and [sceAudioOut2GetSpeakerInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut2SpeakerArray.cpp) (libSceAudioOut) - the port state reports the channel count of the port's data format, output main, volume 127 and a zero reroute counter and flags. Taking the channel count from the data format follows [SharpEmu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/Audio/AudioOut2Exports.cs), another emulator. A payload process on a PS5 (firmware 13.60), which is not the foreground application, read output 0, volume -1 and reroute counter 5 for its main ports, and 6 channels for an 8-channel port on a 5.1 system, so a title's values are unverified, including the output of a pad port, the volume after `sceAudioOut2PortSetAttributes` and the reroute counter on a device change. The speaker info reports type 0 with available bits `0x3`, the front left speaker at +30 degrees and the front right one at -30, and -360 for the azimuth and elevation of every other slot. On that console the type followed the HDMI device type and the available bits its channels: type 0 with the device set to TV, 1 to AV amplifier and 2 to sound bar, which [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs) names television, AV receiver and sound bar (3 is headphones there), and available bits `0x3` for 2 channels and `0x3F` for 5.1 (centre at 0, surround pair at +110 and -110, -360 for the LFE). The front pair and the empty slots read the same in every setting; 7.1 on the AV amplifier reported 2 channels on that console's HDMI chain. The host output is always reported as a stereo television here. The console also returns `0x80268006` before `sceAudioOut2Initialize`, `0x80268001` for flags above 1 and `0x8026800C` for a null pointer, which are not modelled. ASTRO BOT and ASTRO's PLAYROOM reportedly stop on an assertion that depends on these values (#4317), but which field matters has not been isolated - [sceAudioOut2UserCreateEx](../../../core/libs/prx/libSceAudioOut/src/Unimplemented.cpp) (libSceAudioOut) - unknown signature - [sceAudioOutOpenEx](../../../core/libs/prx/libSceAudioOut/src/Unimplemented.cpp) (libSceAudioOut) - unknown signature From 08904c9c47eef1a4a4fae7befe139a11ff1556bd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:53:54 +0300 Subject: [PATCH 343/523] fix(net): restore address route and internal statistics signatures --- core/libs/prx/libSceNet/Export.cpp | 10 ++++++---- docs/dev/debt/libSceNet.md | 10 ++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index bf95cdd988..85465086ae 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -96,6 +96,8 @@ struct NetEmulationParameter; struct NetResolverConnection; struct NetThreadParameter; +struct NetIpv4InterfaceAddress; +struct NetStatisticsInfoInternal; struct NetInitializeParameter; struct NetInterfaceInfo; struct NetInterfaceStatistics; @@ -2314,7 +2316,7 @@ int APS5_VABI sceNetGetRandom(std::uint32_t* output) { return 0; } -int APS5_VABI sceNetGetRouteInfo(void) { +int APS5_VABI sceNetGetRouteInfo(std::int32_t family, NetRouteInfo* routes, std::int32_t capacity, std::uint8_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2569,12 +2571,12 @@ int APS5_VABI sceNetConfigFlushRoute6(std::uint32_t interfaceId) { return 0; } -int APS5_VABI sceNetConfigAddIfaddr(void) { +int APS5_VABI sceNetConfigAddIfaddr(int interfaceId, const std::uint32_t* address, const std::uint32_t* broadcastAddress, const std::uint32_t* netmask) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceNetConfigGetIfaddr(void) { +int APS5_VABI sceNetConfigGetIfaddr(std::int32_t interfaceId, NetIpv4InterfaceAddress* addresses, std::int32_t capacity) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2954,7 +2956,7 @@ int APS5_VABI sceNetConfigRoutingShowtCtlVar(const char* name) { return 0; } -int APS5_VABI sceNetGetStatisticsInfoInternal(void) { +int APS5_VABI sceNetGetStatisticsInfoInternal(NetStatisticsInfoInternal* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index eb17a8b8d3..def2d8deb6 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -7,19 +7,18 @@ ### Unknown function info +- [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - disassembly confirms the same interface/address/broadcast/netmask arguments as SetIfaddr, entering its shared handler with add mode selected. GetIfaddr copies up to two 16-byte interface-address records. GetRouteInfo uses a family, record buffer, signed capacity and byte flags; internal statistics output is 16 bytes. The address-record and internal-statistics fields remain incomplete. + - [Resolver-connect, thread creation, interface and diagnostic queries, WLAN configuration and DHCP queries](../../../core/libs/prx/libSceNet/Export.cpp) retain unsupported implementations. Their restored argument lists still reference incomplete connection, thread, interface, route, WLAN and statistics types. Kernel ioctl payloads need reconstruction from their handlers or consumers; buffer lengths alone do not establish field types. - [sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats, sceNetGetMemoryPoolStats](../../../core/libs/prx/libSceNet/Export.cpp) (libSceHttp2, libSceSsl, libSceNet) - the signatures `(int id, stats*)`, the layouts (pool size, peak and current use, and for Http2 and Ssl a 32-bit reserved field) and the error codes were measured on a PS5 (firmware 13.60). The pool size is the one given at init or pool creation. Http2 and Net allocate nothing from these pools. Ssl counts its certificate backend allocations and returned data against its pool; C++ bookkeeping is outside that budget. All three initially report zero use, where the console already reports 0x15C0 bytes for Ssl, 0x1640 for Http2 and 4 for a Net pool. Ssl's allocation sizes and peak usage do not reproduce the console allocator - [sceNetClearDnsCache](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the library has no DNS cache to clear because lookups use the host resolver. The host resolver cache is left unchanged. - [sceNetEtherStrton](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - its signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs#L737-L913) and its [Net bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs#L19), not measured on a console; the throwing declaration only resolves the import. - [sceNetGetDnsInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(info, int flags)` signature, the two IPv4 server slots in network byte order and the server count as the return value follow the PS4 [OpenOrbis musl resolver](https://github.com/OpenOrbis/musl/blob/2b0bb8ce2fa64ac255cb656785729d159a5f074c/src/network/resolvconf.c) and [OpenOrbis Net.h](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/Net.h); unverified on PS5 hardware. It reports the host DNS servers of the interface `sceNetCtlGetInfo` reports, never servers set through `sceNetSetDnsInfo`. Zeroing the unused slots and returning 0 without a host IPv4 interface are unverified. Nonzero flags and a null info throw because their console results are unknown - [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - generates values with the host random device; the system library maintains its own seeded PRNG state. -- [sceNetGetRouteInfo](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. - [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced. Allocation flag 4 bypasses the system pool reserve threshold; it throws here because the pool reserve is not implemented. Flag 2 zero-fills and other bits are ignored, matching the system allocator. - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigAddIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigGetIfaddr](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature +- [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. - [sceNetDhcpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature @@ -27,8 +26,7 @@ - [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetGetStatisticsInfoInternal](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature +- [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. ### Functional From 46912523df088dd64c63f570c04611595e94c79e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 22:58:34 +0300 Subject: [PATCH 344/523] fix(hmd2): define calibration and object selection memory layouts --- core/libs/prx/libSceHmd2/Export.cpp | 48 +++++++++++++++++++++++++---- docs/dev/debt/libSceHmd2.md | 2 +- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index 68fa34443f..15afad777e 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -6,6 +6,7 @@ #include "prx/libSceUserService/UserService.hpp" #include "SceTypes.hpp" +#include #include #include #include @@ -32,15 +33,38 @@ static_assert(sizeof(MirroringOption) == 32); struct GazeResultParameter; struct GazeResult; struct FoveatedRenderingResult; -struct GazeCalibrationData; -struct GazeUserCalibrationData; -struct GazeUserCalibrationStatus; +struct GazeCalibrationData { + void* data; + std::int32_t size; +}; +struct GazeUserCalibrationData { + const void* data; + std::int32_t size; +}; +struct GazeUserCalibrationStatus { + std::int32_t status; + std::int32_t eye; + float point[3]; +}; struct GazeRawData; struct GazeObjectSelectionParameter; struct GazeObjectSelectionResult; -struct GazeObjectSelectionInitializeParameter; -struct GazeObjectSelectionMemoryParameter; -struct GazeObjectSelectionMemoryInfo; +struct GazeObjectSelectionInitializeParameter { + std::uint32_t size; + std::uint32_t maxObjects; + void* memory; + std::uint32_t memorySize; + std::uint32_t reserved; +}; +struct GazeObjectSelectionMemoryParameter { + std::uint32_t size; + std::uint32_t maxObjects; +}; +struct GazeObjectSelectionMemoryInfo { + std::uint32_t size; + std::uint32_t memorySize; + std::uint64_t alignment; +}; struct CalibrationPoint { float x; @@ -49,6 +73,18 @@ struct CalibrationPoint { }; static_assert(sizeof(CalibrationPoint) == 12); +static_assert(sizeof(GazeCalibrationData) == 16); +static_assert(offsetof(GazeCalibrationData, size) == 8); +static_assert(sizeof(GazeUserCalibrationData) == 16); +static_assert(offsetof(GazeUserCalibrationData, size) == 8); +static_assert(sizeof(GazeUserCalibrationStatus) == 20); +static_assert(offsetof(GazeUserCalibrationStatus, point) == 8); +static_assert(sizeof(GazeObjectSelectionInitializeParameter) == 24); +static_assert(offsetof(GazeObjectSelectionInitializeParameter, memory) == 8); +static_assert(offsetof(GazeObjectSelectionInitializeParameter, memorySize) == 16); +static_assert(sizeof(GazeObjectSelectionMemoryParameter) == 8); +static_assert(sizeof(GazeObjectSelectionMemoryInfo) == 16); +static_assert(offsetof(GazeObjectSelectionMemoryInfo, alignment) == 8); } diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index 47cbda8590..41deb3e6e3 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -83,6 +83,6 @@ - [Hmd2IndexZeroCheckSecondThenZero](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console returns 0 for a first argument of 0 and `0x8a72100f` for 1 to 5, 8, 16, 255 and -1; a null second argument returns `0x8a720008` even with a nonzero first argument. Its third to sixth arguments answered `0x8a72100f` when null with a nonzero first argument, which does not separate them from the first-argument check, so they are not looked at: `wiv8+7ogrq0`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [FBUEhH9tQ1A](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - left unimplemented: before initialization it writes the 32-bit value 9 through its second argument and returns 0 when its first argument points at zeroed memory, but returns `0x8a720001` without writing when that memory holds 0xAA bytes, and its fourth to sixth arguments answer `0x8a720001` when null, so the first argument is an in-out structure whose fields are not known -- [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte parameter and a 56-byte or 24-byte output. Object selection uses a 120-byte parameter and 56-byte result, a 24-byte initialization parameter, and 8-byte/16-byte memory-query structures. These payload fields remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. +- [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte parameter and a 56-byte or 24-byte output. Object selection uses a 120-byte parameter and 56-byte result whose fields remain incomplete. Its initialization and memory-query structures are recovered, including the 32-bit object count and memory size, memory pointer and 64-bit alignment. Calibration data descriptors contain a pointer and signed 32-bit size; calibration status contains two 32-bit selectors and three floats. The underlying calibration blob formats remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. - [libSceHmd2 reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - frame identifiers and predicted display times are 64-bit. Event setters accept a kernel event queue and 32-bit event ID, consumed by sceKernelTriggerUserEvent. Mirroring uses two memory pointers and a separate 32-byte mode/reserved option. Status and current-output queries write 104 and 32 bytes; those field layouts and the reprojection initialization fields remain incomplete. Positional-reprojection mode is a byte with values 0 through 5, not a boolean. From e7a9514f8e6a31064187207d78bbe07508d6d440 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:00:23 +0300 Subject: [PATCH 345/523] fix(net): restore address callbacks and diagnostic start parameters --- core/libs/prx/libSceNet/Export.cpp | 22 ++++++++++++++++++---- docs/dev/debt/libSceNet.md | 6 ++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 85465086ae..f69fa0f01d 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -99,6 +99,20 @@ struct NetThreadParameter; struct NetIpv4InterfaceAddress; struct NetStatisticsInfoInternal; struct NetInitializeParameter; +struct NetDhcpStartParameter; +struct NetAddress6StartParameter; + +using NetAddressCallback = void (APS5_VABI *)(void* userArg, std::int32_t event, std::int32_t error); + +struct NetInfoDumpParameter { + std::uint32_t flags; + std::uint32_t intervalSeconds; + char path[64]; +}; + +static_assert(sizeof(NetInfoDumpParameter) == 72); +static_assert(offsetof(NetInfoDumpParameter, path) == 8); + struct NetInterfaceInfo; struct NetInterfaceStatistics; @@ -2661,7 +2675,7 @@ int APS5_VABI sceNetDhcpGetInfoEx(std::int32_t interfaceId, std::int32_t type, N return 0; } -int APS5_VABI sceNetDhcpStart(void) { +int APS5_VABI sceNetDhcpStart(std::uint32_t interfaceId, NetAddressCallback callback, void* userArg, std::int32_t threadParameter1, std::int32_t threadParameter2, NetDhcpStartParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2721,7 +2735,7 @@ int APS5_VABI sceNetConfigAddRoute6(const NetIpv6Address* destination, const Net return 0; } -int APS5_VABI sceNetAddrConfig6Start(void) { +int APS5_VABI sceNetAddrConfig6Start(std::uint32_t interfaceId, NetAddressCallback callback, void* userArg, std::int32_t threadParameter1, std::int32_t threadParameter2, const NetAddress6StartParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2736,7 +2750,7 @@ int APS5_VABI sceNetAddrConfig6GetInfo(std::int32_t interfaceId, NetAddress6Info return 0; } -int APS5_VABI sceNetInfoDumpStart(void) { +int APS5_VABI sceNetInfoDumpStart(std::int32_t threadParameter1, std::int32_t threadParameter2, const NetInfoDumpParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2756,7 +2770,7 @@ int APS5_VABI sceNetDhcpdStop(int interfaceId) { return 0; } -int APS5_VABI sceNetDuplicateIpStart(void) { +int APS5_VABI sceNetDuplicateIpStart(std::int32_t interfaceId, NetAddressCallback callback, void* userArg, std::int32_t threadParameter1, std::int32_t threadParameter2) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index def2d8deb6..3abcb2a50e 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -19,11 +19,7 @@ - [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced. Allocation flag 4 bypasses the system pool reserve threshold; it throws here because the pool reserve is not implemented. Flag 2 zero-fills and other bits are ignored, matching the system allocator. - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. -- [sceNetDhcpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetAddrConfig6Start](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetDuplicateIpStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. @@ -46,3 +42,5 @@ - [sceNetResolverAbort and sceNetResolverStartNtoaMultipleRecords](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) follow shadPS4 ([net.cpp](https://github.com/shadps4-emu/shadPS4/blob/811bfcd7d17ed8ae975c477d9ff28abc744e1ca8/src/core/libraries/net/net.cpp), [net_resolver.cpp](https://github.com/shadps4-emu/shadPS4/blob/811bfcd7d17ed8ae975c477d9ff28abc744e1ca8/src/core/libraries/net/net_resolver.cpp)); the `(int rid, int flags)` signature of the abort also matches [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Socket.cs). Unverified on hardware. Lookups run synchronously over the host resolver, which cannot be interrupted: an abort while a lookup runs on another thread returns at once, and that lookup fails with `EINTR` without writing its output only when the host call returns; the abort flags are then ignored, as shadPS4 does for a running lookup. With nothing running, abort flag 1 (NTOA preservation) or 2 (ATON preservation) makes the next `sceNetResolverStartNtoa*` or `sceNetResolverStartAton` call on that resolver fail with `EINTR`, reported by `sceNetResolverGetError`, after its arguments are checked. Unlike shadPS4, which ignores them, other abort flag bits throw, and `sceNetResolverStartNtoaMultipleRecords` throws for any flag, including async (1) and 0x10000 (IP address literals disabled), as `sceNetResolverStartNtoaMultipleRecordsEx` does - [sceNetResolverStartNtoa6](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) resolves a host name to its first IPv6 address through the host resolver (`getaddrinfo` with `AF_INET6`) and writes the 16-byte address; the `(rid, hostname, addr, timeout, retry, flags)` signature follows `sceNetResolverStartNtoa`. A name with no IPv6 address fails with `RESOLVER_ENODNS` (`0x804101E1`) like a failed lookup; whether the console returns IPv4-mapped addresses instead is not verified. Timeout and retry are ignored as in `sceNetResolverStartNtoa`, and any nonzero flags throw because their meaning for this call is not known. Unverified on hardware. - [sceNetInetPton](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) ends an IPv4 address at the first space, tab, LF, VT, FF or CR and ignores the rest (`"1.2.3.4 x"` converts), leaves the destination and `sceNetErrnoLoc` unchanged when a conversion fails, as PS4 firmware 10.71 does; not yet confirmed on PS5. Leading whitespace, any other trailing byte (`0x01`, `0xa0`) and IPv6 text with whitespace fail. + +- [sceNetDhcpStart, sceNetAddrConfig6Start, sceNetDuplicateIpStart and sceNetInfoDumpStart](../../../core/libs/prx/libSceNet/Export.cpp) - argument widths and callback ABI are recovered. Address callbacks receive user data, a 32-bit event and a 32-bit error. DHCP configuration is mutable; the 512-byte IPv6 configuration is read-only. Their complete field layouts remain unknown. The two 32-bit thread parameters are stored but not consumed by this firmware thread wrapper, so their original names and units are unconfirmed. Information-dump options contain flags, an interval in seconds and a 64-byte path. All four exports remain unimplemented. From ee807399c3ffc329ce903cdabbe3fd62107a4d85 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:02:49 +0300 Subject: [PATCH 346/523] fix(mbus): restore by-value device service condition abi --- core/libs/prx/libSceMbus/PendingExports.cpp | 11 +++++++++-- docs/dev/debt/libSceMbus.md | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index d0596729f0..593546e9b6 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -1,5 +1,7 @@ #include "prx/libc/include/General.hpp" +#include + struct MbusDeviceDescription; struct MbusDeviceInfo; struct MbusSimulatedHandleStatus; @@ -7,7 +9,12 @@ struct MbusSparkState; struct MbusDriverCallbacks; struct MbusUsbPowerEntry; struct MbusEvent; -struct MbusDeviceCondition; +struct alignas(8) MbusDeviceCondition { + std::byte data[256]; +}; + +static_assert(sizeof(MbusDeviceCondition) == 256); +struct MbusDeviceServiceInfo; struct MbusEventCallbacks { void (APS5_VABI *onOverflow)(std::int32_t eventHandle, void* userData); void (APS5_VABI *onEvent)(std::int32_t eventHandle, void* userData, const MbusEvent* event); @@ -373,7 +380,7 @@ int APS5_VABI sceMbusGetDeviceInfoByBusId_(std::int32_t busId, MbusDeviceInfo* i return 0; } -int APS5_VABI sceMbusGetDeviceInfoByConditionForDeviceService() { +int APS5_VABI sceMbusGetDeviceInfoByConditionForDeviceService(std::int32_t flags, MbusDeviceServiceInfo* infos, std::int32_t capacity, std::int32_t* first, std::int32_t* second, MbusDeviceCondition condition) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index a892a6c785..b1bb624f14 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,7 +8,7 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): GetDeviceInfoByConditionForDeviceService and SetDeviceFunctionState still require by-value aggregate layouts. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. +- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): SetDeviceFunctionState still requires its by-value aggregate layout. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. @@ -23,3 +23,5 @@ - [sceMbus settings](../../../core/libs/prx/libSceMbus/PendingExports.cpp): USB power-list entries occupy 12 bytes, with an 8-bit count limited to 100; their fields remain unresolved. Several routing, scratch-data and proxy scalar meanings still require the service handler. Start/StopAudioOutAuxBroadcast and StopAudioOutSharePlay have confirmed zero-argument ABIs. - [sceMbusGetDeviceInfoByCondition_](../../../core/libs/prx/libSceMbus/PendingExports.cpp): its first parameter points to a 256-byte query condition; the second is a 32-bit scalar. Two optional 32-bit outputs and the sized 256-byte record array are confirmed, while condition fields and output meanings remain unresolved. + +- [sceMbusGetDeviceInfoByConditionForDeviceService](../../../core/libs/prx/libSceMbus/PendingExports.cpp): assembly and its DeviceService caller confirm five register arguments followed by a 256-byte condition passed by value on the stack. The condition keeps opaque storage because its service-side fields remain unresolved; byte 228 contains the 16-bit device class. Output records occupy 112 bytes. The two optional outputs are 32-bit values whose meanings remain unresolved. From 7e6ae8f1c9cba4283954f6d8f3f799d9081df1e0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:09:16 +0300 Subject: [PATCH 347/523] fix(ime): restore current text notification argument slots --- core/libs/prx/libSceIme/Export.cpp | 4 ++-- docs/dev/debt/libSceIme.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceIme/Export.cpp b/core/libs/prx/libSceIme/Export.cpp index 29d014c627..a67cd80b39 100644 --- a/core/libs/prx/libSceIme/Export.cpp +++ b/core/libs/prx/libSceIme/Export.cpp @@ -744,12 +744,12 @@ int APS5_VABI sceImeVshInformConfirmdString(ImeVshContext* context, const char16 return 0; } -int APS5_VABI sceImeVshNotifyCurrentTextInfo(void) { +int APS5_VABI sceImeVshNotifyCurrentTextInfo(ImeVshContext* context, const char16_t* text, std::int32_t caretIndex, std::int32_t preeditFirst, std::int32_t preeditSecond, std::int32_t selectionFirst, std::int32_t selectionSecond) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeVshNotifyCurrentTextInfoForReconvertText(void) { +int APS5_VABI sceImeVshNotifyCurrentTextInfoForReconvertText(ImeVshContext* context, const char16_t* text, std::int32_t caretIndex, std::int32_t preeditFirst, std::int32_t preeditSecond, std::int32_t selectionFirst, std::int32_t selectionSecond, std::int32_t option) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceIme.md b/docs/dev/debt/libSceIme.md index f4689b4ab8..d7134b33e2 100644 --- a/docs/dev/debt/libSceIme.md +++ b/docs/dev/debt/libSceIme.md @@ -12,8 +12,6 @@ - [sceImeFilterText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the behaviour follows the disassembly of `libSceIme.sprx` from firmware 13.60 and is differential-tested against that function in an emulator, not measured on a console. [ImeFilterCodepages.hpp](../../../core/libs/prx/libSceIme/include/ImeFilterCodepages.hpp) holds the two codepage checks (helpers at 0x4850 and 0x48f0), rebuilt from Unicode's public [CP932](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT) and [CP936](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT) tables plus the Latin-1, CJK Extension A and a few other units the firmware also accepts (listed in the PR). The parameter layout beyond `type` is inferred: `mask` is the allowed-class bitmask, and `version` and `variant` derive it when it is 0. [sceImeCheckFilterText](../../../core/libs/prx/libSceIme/Export.cpp) throws, as its contract is unknown - [sceImeOpen, sceImeUpdate, sceImeSetText, sceImeSetCaret, sceImeClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the event ids (Open 0, UpdateText 1, UpdateCaret 2, PressClose 4, PressEnter 5), the caret directions (left 1, right 2), the error codes and the text event payload follow the PS4 reimplementation in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/b72489d8c24e63fc56a29fe2c90120ab768a210d/src/core/libraries/ime/ime.cpp); none is measured on a PS5. Events are queued and delivered by `sceImeUpdate` only when it is passed the handler given to `sceImeOpen`, with that call's `arg`. A text event points `str` at a copy of the text kept in `work`, and `inputTextBuffer` holds the same text; both are terminated, `caret_index` is the caret after the edit and the one edit area has the edit position and the change in UTF-16 units (0 for Enter and Close). An edit sends one UpdateText; a caret move sends one UpdateCaret per UTF-16 unit, as shadPS4 does. Enter inserts a line break with the multiline option and sends PressEnter otherwise; Escape sends PressClose. Typed text is cut to `maxTextLength` without splitting a surrogate pair, line breaks are dropped without the multiline option, and the BasicLatin, URL, mail and number types do not filter characters. `sceImeOpen` checks, in shadPS4's order, the user, type, enter label, input method, option bits (the `sceImeGetPanelSize` mask), `maxTextLength` (1 to 2048), the text buffer, the position (below 1920x1080, or 3840x2160 with the over-2K option), the alignments, the extended priority and reserved bytes, `work` (non-null, 4-byte aligned), the handler, the reserved bytes and the initial text (no unpaired surrogate, no line break without the multiline option); shadPS4's SDK-dependent option, language and extended-option masks and its work-overlap check are not applied, and only the text copy is written to `work`. `sceImeSetText` keeps the caret unless the text became shorter, `sceImeSetCaret` rejects an index past the text with `0x80bc0030`, and neither sends an event. `sceImeSetTextGeometry` accepts any geometry while a panel is open, since no panel is drawn - [sceImeForTestFunction](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshNotifyCurrentTextInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeVshNotifyCurrentTextInfoForReconvertText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigSet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature - [sceImeConfigGet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature @@ -25,3 +23,5 @@ - [sceImeKeyboardGetInfo](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) returns `0x80bc0023` (no resource id) for every id while a keyboard is open, since the IME keyboard never reports a connected device. [sceImeKeyboardSetMode](../../../core/libs/prx/libSceIme/Export.cpp) validates the mode bits but the mode has no effect, as no IME keyboard events are delivered. - [sceImeKeyboardGetResourceId](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) reports that no USB keyboard is connected (`0x80bc0004`, as [shadPS4](https://github.com/shadps4-emu/shadPS4) does) because IME keyboard events are not delivered to the handler, although [libSceKeyboard](../../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) exposes the host keyboard - [sceImeOpen text filter](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) runs on the text edits of host input - typing, backspace, forward delete and a multiline Enter - following [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/1211958b4eb38256cc0c5c7ba92d668820c05389/src/libs/ime.cpp), not confirmed on a PS5. The host input thread only queues the edit under the IME mutex; `sceImeUpdate` applies it on the title's thread, where the filter also runs with the mutex released - like the event handler, so a filter that calls back into `sceIme*` cannot deadlock - and the structural edit and the filtered commit are serialized under the mutex, with a commit after the session closed or reopened dropped by a session id. The filter is guest code and `ImeTextFilter` and `ImeExtKeyboardFilter` are declared `APS5_VABI` like `EventHandler`. The filter is called with the edited text, a buffer of `maxTextLength` UTF-16 units and its length; a nonzero return, an output longer than the capacity, an unpaired surrogate or, without the multiline option, a line break keeps the edit unfiltered. An accepted output replaces the text, the caret stays where the edit put it and is clamped to the text and normalized past a surrogate pair, and one UpdateText is sent with the minimal edit delta of the whole old and new text, as KytyPS5 computes it; an output equal to the previous text sends no event. Caret moves, PressEnter without multiline, PressClose, `sceImeSetText` and `sceImeSetCaret` do not run the filter, as in KytyPS5; a `sceImeSetText` or `sceImeSetCaret` that lands while the filter runs bumps a revision beside the session id, and the filtered commit is dropped when either changed, as KytyPS5 drops the commit when its revision changed, so the title's own text or caret setting wins, the edit is discarded and sends no event. An external keyboard filter is accepted and stored on the session: it is called only for keyboard events that carry key data, which the host input path does not produce - it delivers text and actions, as KytyPS5's host API queues external inputs with a zeroed key and skips the filter for them - so it is never called + +- [sceImeVshNotifyCurrentTextInfo and sceImeVshNotifyCurrentTextInfoForReconvertText](../../../core/libs/prx/libSceIme/Export.cpp): assembly confirms seven and eight integer/pointer arguments, including the stack arguments, with no incoming SIMD parameter. Both take a context, UTF-16 string, signed 32-bit caret index and two pairs of 32-bit preedit/selection values. Pair endpoint-versus-length semantics and the reconversion option remain unresolved. Protected bodies are unchanged. From 241fa440942fcd872c24865d6d3dae3e4e6b4001 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:12:45 +0300 Subject: [PATCH 348/523] fix(voice): restore resource layout and port validation --- core/libs/SceTypes.hpp | 11 +-- core/libs/prx/libSceVoice/Export.cpp | 109 +++++++++++++++++-------- core/libs/tests/GuestVoice.cpp | 84 ++++++++++++------- core/libs/tests/GuestVoicePortAttr.cpp | 54 ++++++------ docs/dev/debt/libSceVoice.md | 10 +-- 5 files changed, 172 insertions(+), 96 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 978123ba23..390c010495 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -963,13 +963,14 @@ struct VoicePortInfo { }; struct VoiceResourceInfo { - std::uint16_t max_voice_input_ports; - std::uint16_t max_voice_output_ports; - std::uint16_t max_device_input_ports; - std::uint16_t max_device_output_ports; - std::uint16_t max_ports; + std::uint16_t maxVoiceInputPorts; + std::uint16_t maxVoiceOutputPorts; + std::uint16_t maxDeviceInputPorts; + std::uint16_t maxDeviceOutputPorts; }; +static_assert(sizeof(VoiceResourceInfo) == 8); + struct VoiceStartParam { void* container; std::uint32_t mem_size; diff --git a/core/libs/prx/libSceVoice/Export.cpp b/core/libs/prx/libSceVoice/Export.cpp index b2ae0e88ac..b89fea488a 100644 --- a/core/libs/prx/libSceVoice/Export.cpp +++ b/core/libs/prx/libSceVoice/Export.cpp @@ -23,19 +23,24 @@ constexpr std::uint32_t VoiceFrameMs = 20; constexpr std::int32_t PortAttrVoiceActivity = 1001; constexpr int VoiceErrorNotInitialized = static_cast(0x804E0801u); constexpr int VoiceErrorAlreadyInitialized = static_cast(0x804E0802u); +constexpr auto NotInitialized = static_cast(0x804e0801u); +constexpr auto InvalidPort = static_cast(0x804e0804u); +constexpr auto InvalidArgument = static_cast(0x804e0805u); +constexpr auto PortNotFound = static_cast(0x804e0807u); struct Port { std::int32_t type; float volume; std::uint32_t bitrate; bool muted = false; + bool paused = false; }; struct Voice { std::mutex mutex; bool initialized = false; bool started = false; - bool chatEnabled = false; + std::int32_t appType = 0; std::uint32_t nextId = 0; std::map ports; std::set> connections; @@ -69,6 +74,7 @@ int APS5_VABI sceVoiceInit(VoiceInitParam* param, int32_t version) { auto& voice = State(); std::lock_guard lock(voice.mutex); if (voice.initialized) return VoiceErrorAlreadyInitialized; + voice.appType = param->app_type; voice.initialized = true; return 0; } @@ -102,7 +108,7 @@ int APS5_VABI sceVoiceEnd_nid_postfix(void) { voice.ports.clear(); voice.connections.clear(); voice.started = false; - voice.chatEnabled = false; + voice.appType = 0; voice.initialized = false; return 0; } @@ -185,16 +191,36 @@ int APS5_VABI sceVoiceGetPortInfo(uint32_t port_id, VoicePortInfo* info) { return 0; } -int APS5_VABI sceVoiceGetPortAttr(uint32_t port_id, int32_t attr, void* value, int32_t size) { +int APS5_VABI sceVoiceGetPortAttr(std::uint32_t portId, std::int32_t attr, void* value, std::int32_t size) { auto& voice = State(); std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__); - if (attr == PortAttrVoiceActivity) { - if (value == nullptr || size != 1) APS5_INVALID_ARG_EX; - std::memset(value, 0, 1); - return 0; + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + if (value == nullptr) return InvalidArgument; + std::int32_t expectedSize; + switch (attr) { + case 1000: + case 1001: + case 2000: + case 2002: + expectedSize = 1; + break; + case 1002: + case 2001: + expectedSize = 4; + break; + case 1003: + expectedSize = 2; + break; + default: + return InvalidArgument; } - Fail(__func__, "attribute " + std::to_string(attr) + " not implemented"); + if (size != expectedSize) return InvalidArgument; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return InvalidPort; + const auto requiredType = attr == 1003 ? PortOutDevice : PortInDevice; + if (found->second.type != requiredType) return InvalidArgument; + Fail(__func__, "device attribute backend is not implemented"); } int APS5_VABI sceVoiceReadFromOPort(uint32_t output_port_id, void* data, uint32_t* size) { @@ -231,53 +257,72 @@ int APS5_VABI sceVoiceGetMuteFlag(uint32_t port_id, bool* muted) { } int APS5_VABI sceVoiceGetResourceInfo(VoiceResourceInfo* info) { - if (info == nullptr) APS5_INVALID_ARG_EX; auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) Fail(__func__, "library not initialized"); - info->max_voice_input_ports = 7; - info->max_voice_output_ports = 7; - info->max_device_input_ports = 1; - info->max_device_output_ports = 1; - info->max_ports = 64; + if (!voice.initialized) return NotInitialized; + if (info == nullptr) return InvalidArgument; + switch (voice.appType) { + case 4: + *info = {0, 0, 4, 5}; + break; + case 0x08000000: + case 0x10000000: + *info = {15, 4, 4, 4}; + break; + case 0x20000000: + *info = {7, 4, 4, 4}; + break; + default: + Fail(__func__, "unsupported initialization mode"); + } return 0; } int APS5_VABI sceVoiceEnableChat(void) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) Fail(__func__, "library not initialized"); - voice.chatEnabled = true; + if (!voice.initialized) return NotInitialized; + Fail(__func__, "audio chat backend is not implemented"); +} + +int APS5_VABI sceVoiceResetPort(std::uint32_t portId) { + auto& voice = State(); + std::lock_guard lock(voice.mutex); + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + if (voice.started && (found->second.type == PortInDevice || found->second.type == PortOutDevice)) Fail(__func__, "device reset backend is not implemented"); return 0; } -int APS5_VABI sceVoiceResetPort(uint32_t port_id) { +int APS5_VABI sceVoicePausePort(std::uint32_t portId) { auto& voice = State(); std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__); + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + found->second.paused = true; return 0; } -int APS5_VABI sceVoicePausePort(uint32_t port_id) { +int APS5_VABI sceVoiceResumePort(std::uint32_t portId) { auto& voice = State(); std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__); - return 0; -} - -int APS5_VABI sceVoiceResumePort(uint32_t port_id) { - auto& voice = State(); - std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__); + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + found->second.paused = false; return 0; } int APS5_VABI sceVoiceDisableChat(void) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) Fail(__func__, "library not initialized"); - voice.chatEnabled = false; - return 0; + if (!voice.initialized) return NotInitialized; + Fail(__func__, "audio chat backend is not implemented"); } int APS5_VABI sceVoiceInitHQ(void) { diff --git a/core/libs/tests/GuestVoice.cpp b/core/libs/tests/GuestVoice.cpp index a8671553de..ac56c3d949 100644 --- a/core/libs/tests/GuestVoice.cpp +++ b/core/libs/tests/GuestVoice.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include extern "C" { @@ -78,27 +79,35 @@ std::uint32_t CreatePort(std::int32_t type, float volume) { int main() { TestNotInitialized(); - RequireThrows([] { sceVoiceResetPort(0); }); - RequireThrows([] { sceVoicePausePort(0); }); - RequireThrows([] { sceVoiceResumePort(0); }); - RequireThrows([] { VoiceResourceInfo info{}; sceVoiceGetResourceInfo(&info); }); + constexpr auto notInitialized = static_cast(0x804e0801u); + constexpr auto invalidPort = static_cast(0x804e0804u); + constexpr auto invalidArgument = static_cast(0x804e0805u); + constexpr auto portNotFound = static_cast(0x804e0807u); + Require(sceVoiceResetPort(0) == notInitialized); + Require(sceVoicePausePort(0) == notInitialized); + Require(sceVoiceResumePort(0) == notInitialized); + Require(sceVoiceGetResourceInfo(nullptr) == notInitialized); + Require(sceVoiceEnableChat() == notInitialized); + Require(sceVoiceDisableChat() == notInitialized); + VoiceInitParam init{}; + init.app_type = 0x10000000; + VoiceStartParam start{}; + Require(sceVoiceInit(&init, 100) == 0); + Require(sceVoiceInit(&init, 100) == AlreadyInitialized); + Require(sceVoiceStart(&start) == 0); + Require(sceVoiceGetResourceInfo(nullptr) == invalidArgument); + struct ResourceOutput { + VoiceResourceInfo info; + std::uint32_t guard; + } resourceOutput{{}, 0xa5a5a5a5u}; + Require(sceVoiceGetResourceInfo(&resourceOutput.info) == 0); + Require(resourceOutput.info.maxVoiceInputPorts == 15); + Require(resourceOutput.info.maxVoiceOutputPorts == 4); + Require(resourceOutput.info.maxDeviceInputPorts == 4); + Require(resourceOutput.info.maxDeviceOutputPorts == 4); + Require(resourceOutput.guard == 0xa5a5a5a5u); RequireThrows([] { sceVoiceEnableChat(); }); RequireThrows([] { sceVoiceDisableChat(); }); - VoiceInitParam init{}; - VoiceStartParam start{}; - Require(sceVoiceInit(&init, 0) == 0); - Require(sceVoiceInit(&init, 0) == AlreadyInitialized); - Require(sceVoiceStart(&start) == 0); - RequireThrows([] { sceVoiceGetResourceInfo(nullptr); }); - VoiceResourceInfo resourceInfo{}; - Require(sceVoiceGetResourceInfo(&resourceInfo) == 0); - Require(resourceInfo.max_voice_input_ports == 7); - Require(resourceInfo.max_voice_output_ports == 7); - Require(resourceInfo.max_device_input_ports == 1); - Require(resourceInfo.max_device_output_ports == 1); - Require(resourceInfo.max_ports == 64); - Require(sceVoiceEnableChat() == 0); - Require(sceVoiceDisableChat() == 0); const auto input = CreatePort(2, 0.5f); const auto output = CreatePort(3, 0.25f); Require(sceVoiceConnectIPortToOPort(input, output) == 0); @@ -108,14 +117,17 @@ int main() { Require(sceVoiceResetPort(input) == 0); Require(sceVoiceResetPort(output) == 0); - RequireThrows([&] { sceVoiceResetPort(output + 1); }); + Require(sceVoiceResetPort(output + 1) == portNotFound); + Require(sceVoiceResetPort(128) == invalidPort); + Require(sceVoicePausePort(input) == 0); Require(sceVoicePausePort(input) == 0); Require(sceVoicePausePort(output) == 0); VoicePortInfo paused{}; Require(sceVoiceGetPortInfo(input, &paused) == 0); Require(paused.port_type == before.port_type && paused.state == before.state && paused.frame_size == before.frame_size); Require(sceVoiceResumePort(input) == 0); + Require(sceVoiceResumePort(input) == 0); Require(sceVoiceResumePort(output) == 0); Require(sceVoicePausePortAll() == 0); Require(sceVoicePausePortAll() == 0); @@ -123,8 +135,10 @@ int main() { Require(paused.port_type == before.port_type && paused.state == before.state && paused.frame_size == before.frame_size); Require(sceVoiceResumePortAll() == 0); Require(sceVoiceResumePortAll() == 0); - RequireThrows([&] { sceVoicePausePort(output + 1); }); - RequireThrows([&] { sceVoiceResumePort(output + 1); }); + Require(sceVoicePausePort(output + 1) == portNotFound); + Require(sceVoicePausePort(128) == invalidPort); + Require(sceVoiceResumePort(output + 1) == portNotFound); + Require(sceVoiceResumePort(128) == invalidPort); VoicePortInfo after{}; Require(sceVoiceGetPortInfo(input, &after) == 0); @@ -139,14 +153,26 @@ int main() { Require(sceVoiceDisconnectIPortFromOPort(input, output) == 0); Require(sceVoiceDeletePort(input) == 0); - RequireThrows([&] { sceVoiceResetPort(input); }); - RequireThrows([&] { sceVoicePausePort(input); }); + Require(sceVoiceResetPort(input) == portNotFound); + Require(sceVoicePausePort(input) == portNotFound); Require(sceVoiceResetPort(output) == 0); Require(sceVoiceEnd_nid_postfix() == 0); TestNotInitialized(); - RequireThrows([&] { sceVoiceResetPort(output); }); - RequireThrows([&] { sceVoiceResumePort(output); }); - RequireThrows([] { VoiceResourceInfo info{}; sceVoiceGetResourceInfo(&info); }); - RequireThrows([] { sceVoiceEnableChat(); }); - RequireThrows([] { sceVoiceDisableChat(); }); + Require(sceVoiceResetPort(output) == notInitialized); + Require(sceVoiceResumePort(output) == notInitialized); + Require(sceVoiceGetResourceInfo(nullptr) == notInitialized); + Require(sceVoiceEnableChat() == notInitialized); + Require(sceVoiceDisableChat() == notInitialized); + for (const auto mode : {4, 0x08000000, 0x10000000, 0x20000000}) { + init.app_type = mode; + Require(sceVoiceInit(&init, 100) == 0); + Require(sceVoiceGetResourceInfo(&resourceOutput.info) == 0); + Require(resourceOutput.info.maxVoiceInputPorts == (mode == 4 ? 0 : mode == 0x20000000 ? 7 : 15)); + Require(resourceOutput.info.maxVoiceOutputPorts == (mode == 4 ? 0 : 4)); + Require(resourceOutput.info.maxDeviceInputPorts == 4); + Require(resourceOutput.info.maxDeviceOutputPorts == (mode == 4 ? 5 : 4)); + Require(resourceOutput.guard == 0xa5a5a5a5u); + Require(sceVoiceEnd_nid_postfix() == 0); + } + } diff --git a/core/libs/tests/GuestVoicePortAttr.cpp b/core/libs/tests/GuestVoicePortAttr.cpp index 3264ec11c4..082c828ec5 100644 --- a/core/libs/tests/GuestVoicePortAttr.cpp +++ b/core/libs/tests/GuestVoicePortAttr.cpp @@ -18,8 +18,8 @@ int APS5_VABI sceVoiceGetPortAttr(uint32_t, int32_t, void*, int32_t); static void Require(bool value) { if (!value) std::abort(); } -template -static bool Throws(Call call, std::string_view message) { +template +static bool Throws(TCall call, std::string_view message) { try { call(); } catch (const std::logic_error& error) { @@ -29,31 +29,37 @@ static bool Throws(Call call, std::string_view message) { } int main() { - constexpr std::int32_t voiceActivity = 1001; - - Require(Throws([] { sceVoiceGetPortAttr(0, voiceActivity, nullptr, 1); }, "library not initialized")); - + constexpr auto notInitialized = static_cast(0x804e0801u); + constexpr auto invalidPort = static_cast(0x804e0804u); + constexpr auto invalidArgument = static_cast(0x804e0805u); + Require(sceVoiceGetPortAttr(128, 1001, nullptr, 1) == notInitialized); VoiceInitParam init{}; - Require(sceVoiceInit(&init, 0) == 0); - + init.app_type = 0x10000000; + Require(sceVoiceInit(&init, 100) == 0); VoicePortParam param{}; - param.port_type = 2; param.volume = 1.0f; - param.voice.bitrate = 16000; - std::uint32_t port = 0; - Require(sceVoiceCreatePort(&port, ¶m) == 0); - - std::array buffer{0xa5, 0xa5, 0xa5}; - Require(sceVoiceGetPortAttr(port, voiceActivity, &buffer[1], 1) == 0); - Require(buffer[0] == 0xa5 && buffer[1] == 0 && buffer[2] == 0xa5); - - Require(Throws([port, &buffer] { sceVoiceGetPortAttr(port, 1, &buffer[1], 1); }, "attribute 1 not implemented")); - - Require(Throws([&buffer] { sceVoiceGetPortAttr(0x1234, voiceActivity, &buffer[1], 1); }, "unknown port")); - - Require(sceVoiceDeletePort(port) == 0); - Require(Throws([port, &buffer] { sceVoiceGetPortAttr(port, voiceActivity, &buffer[1], 1); }, "unknown port")); - + std::array buffer{0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5}; + const auto original = buffer; + Require(sceVoiceGetPortAttr(128, 1001, nullptr, 1) == invalidPort); + Require(sceVoiceGetPortAttr(0, 1001, nullptr, 1) == invalidArgument); + for (std::int32_t type = 0; type <= 5; ++type) { + param.port_type = type; + std::uint32_t port = 0; + Require(sceVoiceCreatePort(&port, ¶m) == 0); + for (const auto attr : {1000, 1001, 1002, 1003, 2000, 2001, 2002}) { + const auto size = attr == 1003 ? 2 : attr == 1002 || attr == 2001 ? 4 : 1; + Require(sceVoiceGetPortAttr(port, attr, buffer.data() + 1, size + 1) == invalidArgument); + if (type == (attr == 1003 ? 5 : 0)) { + Require(Throws([&] { sceVoiceGetPortAttr(port, attr, buffer.data() + 1, size); }, "device attribute backend")); + } else { + Require(sceVoiceGetPortAttr(port, attr, buffer.data() + 1, size) == invalidArgument); + } + Require(buffer == original); + } + Require(sceVoiceGetPortAttr(port, 1, buffer.data() + 1, 1) == invalidArgument); + Require(sceVoiceDeletePort(port) == 0); + Require(sceVoiceGetPortAttr(port, 1001, buffer.data() + 1, 1) == invalidPort); + } Require(sceVoiceEnd_nid_postfix() == 0); return 0; } diff --git a/docs/dev/debt/libSceVoice.md b/docs/dev/debt/libSceVoice.md index c6fee962c1..df5a8e4c45 100644 --- a/docs/dev/debt/libSceVoice.md +++ b/docs/dev/debt/libSceVoice.md @@ -1,16 +1,13 @@ # libSceVoice technical debt -### Silent stubs +### Functional -- [sceVoiceGetPortAttr](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - attribute 1001 writes one zero byte and returns 0 for an existing port; the backend processes no voice audio. PPSA34501 requests size 1 and reads one byte on success, and this fallback avoids its startup exception. The name follows attribute 1001 (`CELLVOICE_ATTR_VAD`) (Voice Activity Detection) in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellVoice.h), not confirmed for the PS5. The correct result, eligible port types and behaviour are unknown. +- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization still lacks native mode/version validation, allocator callbacks and backend creation; port creation does not enforce native resource limits. +- [sceVoiceEnableChat and sceVoiceDisableChat](../../../core/libs/prx/libSceVoice/Export.cpp): zero-argument signatures and the not-initialized error are confirmed. Initialized calls throw because the required audio chat backend is not implemented; they no longer report success for a local flag change. ### Unknown function info -- [sceVoiceResetPort](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - the `(uint32_t portId)` signature is the PS Vita one in [VitaSDK](https://github.com/vitasdk/vita-headers/blob/ae5808f3105c42fa3373ba4ab64ab54e2e3ca848/include/psp2/voice.h#L503-L514) and the PS3 `cellVoiceResetPort` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/be69c054eeac57e2e671697a463473d37fe5a7b2/rpcs3/Emu/Cell/Modules/cellVoice.cpp#L493-L510); that the PS5 library takes the same argument is assumed. A reset clears a port's buffered data, and ports here buffer nothing (`sceVoiceReadFromOPort` returns 0 bytes and `sceVoiceWriteToIPort` throws), so it only checks that the library is initialized and the port exists, as RPCS3 does, and keeps the port's connections, volume and mute flag. An uninitialized library or an unknown port throws like the other port calls; the console's error codes are not measured. PPSA23566 imports it -- [sceVoiceGetResourceInfo](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - the `(VoiceResourceInfo* info)` signature and port limits (7 voice inputs, 7 voice outputs, 1 device input, 1 device output, 64 total ports) are the PS Vita ones in [VitaSDK](https://github.com/vitasdk/vita-headers/blob/ae5808f3105c42fa3373ba4ab64ab54e2e3ca848/include/psp2/voice.h#L150-L157); that the PS5 library uses the same layout and limits is assumed. An uninitialized library or a null output pointer throws like the other Voice calls; console limits and error codes are not measured. PPSA23566 imports it -- [sceVoiceEnableChat, sceVoiceDisableChat](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) take no arguments and return 0, following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs#L460-L464) and the `sceAudioOut2EnableChat` model. The host mixes no chat audio, so the calls only record whether chat audio is enabled and fail when the library is not initialized. Console error codes and chat session requirements are unverified. PPSA23566 imports them - [sceVoiceInit, sceVoiceStart, sceVoiceCreatePort, sceVoiceGetPortInfo](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - a second `sceVoiceInit` returns 0x804E0802, and Start, CreatePort and GetPortInfo return 0x804E0801 before checking any argument while the library is not initialized, as [prosper](https://github.com/mattias800/prosper/blob/ed42e499888d5a86792c3e1b2df3aebd8ca1a681/prosper/src/hle/audio/hle_audio.cpp#L4697-L4713) reads them from the shipped module. The other port and lifecycle calls still throw when the library is not initialized, since prosper gives no module address for them. Whether `sceVoiceInit` checks a null param before its state is unknown, so a null param still throws first, and that `sceVoiceEnd` returns the library to the not-initialized state is assumed. Closes when: the other entry points are read from the module or measured on a PS5 -- [sceVoicePausePort, sceVoiceResumePort](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - not measured on a PS5: the `(uint32_t portId)` signature follows [VitaSDK](https://github.com/vitasdk/vita-headers/blob/ae5808f3105c42fa3373ba4ab64ab54e2e3ca848/include/psp2/voice.h#L516-L538) and the PS3 calls in [RPCS3](https://github.com/RPCS3/rpcs3/blob/be69c054eeac57e2e671697a463473d37fe5a7b2/rpcs3/Emu/Cell/Modules/cellVoice.cpp#L443-L529), and the console's error codes and its result for pausing a paused port or resuming a running one are unknown (0 here, as in RPCS3). Both only check that the library is initialized and the port exists: VitaSDK documents that a paused port keeps its data and reported state, and nothing here processes port data. PPSA24191 imports them - [sceVoicePausePortAll, sceVoiceResumePortAll](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - not measured on a PS5: both return 0x804E0801 while the library is not initialized and 0 otherwise, as in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/c1b26558352b863f80142256e4df38585aa7d727/src/core/libraries/voice/voice.cpp#L356-L437); prosper reads the same code for `sceVoiceStart`, and the PS3 calls in [RPCS3](https://github.com/RPCS3/rpcs3/blob/be69c054eeac57e2e671697a463473d37fe5a7b2/rpcs3/Emu/Cell/Modules/cellVoice.cpp#L462-L543) make the same check. Like `sceVoicePausePort` they change nothing that is reported: [VitaSDK](https://github.com/vitasdk/vita-headers/blob/ae5808f3105c42fa3373ba4ab64ab54e2e3ca848/include/psp2/voice.h#L540-L557) documents that pausing every port keeps the buffered data and the reported state, and nothing here processes port data - [sceVoiceInitHQ](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature - [sceVoiceGetMemorySize](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature @@ -22,3 +19,4 @@ - [sceVoiceSetMuteFlagAll](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature - [sceVoiceSetBitRate](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature - [sceVoiceUpdatePort](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature +- [sceVoiceGetPortAttr](../../../core/libs/prx/libSceVoice/Export.cpp): native initialization, port-range, pointer, attribute-size and port-type validation is implemented. Supported device attributes throw because the host has no device state or voice-activity processing. The former constant-zero voice-activity fallback is removed; failed calls leave output untouched. From 56a1c1beda4749d064a1f66e1e3d2f80c17ead60 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:12:46 +0300 Subject: [PATCH 349/523] fix(http2): correct cookie pool ownership and websocket ping abi --- core/libs/prx/libSceHttp2/Export.cpp | 16 +++++++++++----- core/libs/tests/GuestHttp2.cpp | 22 ++++++++++++++-------- docs/dev/debt/libSceHttp2.md | 8 ++++---- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceHttp2/Export.cpp b/core/libs/prx/libSceHttp2/Export.cpp index bc1682e4fe..d97f8d6c6d 100644 --- a/core/libs/prx/libSceHttp2/Export.cpp +++ b/core/libs/prx/libSceHttp2/Export.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -23,6 +25,7 @@ struct Http2Handle { Http2Kind kind; int owner; std::size_t poolSize; + std::shared_ptr cookieMemory; }; static std::atomic g_nextHandle{1}; @@ -353,11 +356,14 @@ int APS5_VABI sceHttp2CookieFlush(int id) { return 0; } -int APS5_VABI sceHttp2CreateCookieBox(int lib_http2_ctx_id) { +int APS5_VABI sceHttp2CreateCookieBox(std::size_t poolSize) { + if (poolSize < 0x10000) return static_cast(0x817b121du); + if (poolSize > std::numeric_limits::max() - 0x3fff) throw std::overflow_error("sceHttp2CreateCookieBox: pool size overflow"); + const auto allocationSize = (poolSize + 0x3fff) & ~std::size_t{0x3fff}; + auto memory = std::make_shared(allocationSize); std::lock_guard lock(g_handlesMutex); - if (FindHandle(lib_http2_ctx_id, Http2Kind::Context) == nullptr) return ERROR_INVALID_ID; const int id = g_nextHandle.fetch_add(1, std::memory_order_relaxed); - g_handles[id] = {Http2Kind::CookieBox, lib_http2_ctx_id, 0}; + g_handles[id] = {Http2Kind::CookieBox, 0, poolSize, std::move(memory)}; return id; } @@ -406,12 +412,12 @@ int APS5_VABI sceHttp2WebSocketSendDataMessageAsync(std::int32_t requestId, cons return 0; } -int APS5_VABI sceHttp2WebSocketSetPingInterval() { +int APS5_VABI sceHttp2WebSocketSetPingInterval(std::int32_t requestId, std::uint32_t intervalMicroseconds) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHttp2WebSocketSetPingTimeout() { +int APS5_VABI sceHttp2WebSocketSetPingTimeout(std::int32_t requestId, std::uint32_t timeoutMicroseconds) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestHttp2.cpp b/core/libs/tests/GuestHttp2.cpp index d29c0021e4..102369b0c4 100644 --- a/core/libs/tests/GuestHttp2.cpp +++ b/core/libs/tests/GuestHttp2.cpp @@ -7,7 +7,7 @@ extern "C" { int APS5_VABI sceHttp2Init(int, int, std::size_t, int); int APS5_VABI sceHttp2CreateTemplate(int, const char*, int, int); int APS5_VABI sceHttp2CreateRequestWithURL(int, const char*, const char*, std::uint64_t); -int APS5_VABI sceHttp2CreateCookieBox(int); +int APS5_VABI sceHttp2CreateCookieBox(std::size_t); int APS5_VABI sceHttp2DeleteCookieBox(int); int APS5_VABI sceHttp2SetCookieBox(int, int); int APS5_VABI sceHttp2CookieFlush(int); @@ -40,7 +40,7 @@ struct Http2MemoryPoolStats { extern "C" { int APS5_VABI sceHttp2GetMemoryPoolStats(int, Http2MemoryPoolStats*); int APS5_VABI sceHttp2SetResolveRetry(int, std::int32_t); -int APS5_VABI sceHttp2WebSocketSetPingInterval(); +int APS5_VABI sceHttp2WebSocketSetPingInterval(std::int32_t, std::uint32_t); } static void Require(bool value) { if (!value) std::abort(); } @@ -64,15 +64,20 @@ static bool Throws(TCall call) { } int main() { + Require(sceHttp2CreateCookieBox(0) == static_cast(0x817b121du)); + Require(sceHttp2CreateCookieBox(0xffff) == static_cast(0x817b121du)); + const int independentBox = sceHttp2CreateCookieBox(0x10000); + Require(independentBox > 0); + Require(sceHttp2DeleteCookieBox(independentBox) == 0); const int context = sceHttp2Init(1, 1, 0x10000, 4); Require(context > 0); Http2MemoryPoolStats stats{1, 1, 1, 1}; Require(sceHttp2GetMemoryPoolStats(context, &stats) == 0); Require(stats.pool_size == 0x10000 && stats.max_inuse_size == 0 && stats.current_inuse_size == 0 && stats.reserved == 0); - const int box = sceHttp2CreateCookieBox(context); - const int other = sceHttp2CreateCookieBox(context); + const int box = sceHttp2CreateCookieBox(0x10000); + const int other = sceHttp2CreateCookieBox(0x10000); Require(box > 0 && other > 0 && box != other && box != context); - const int discarded = sceHttp2CreateCookieBox(context); + const int discarded = sceHttp2CreateCookieBox(0x10000); Require(discarded > 0 && discarded != box && discarded != other); Require(sceHttp2DeleteCookieBox(discarded) == 0); Require(sceHttp2DeleteCookieBox(discarded) == InvalidId); @@ -86,7 +91,7 @@ int main() { Require(request > 0); Require(sceHttp2SetCookieBox(request, other) == 0); Require(sceHttp2SetRequestNoContentLength(request) == 0); - Require(Throws([] { sceHttp2WebSocketSetPingInterval(); })); + Require(Throws([] { sceHttp2WebSocketSetPingInterval(0, 1000000); })); Require(sceHttp2CookieFlush(context) == 0); Require(sceHttp2SendRequest(request, nullptr, 0) == static_cast(0x80436063)); @@ -116,7 +121,7 @@ int main() { const int doomedContext = sceHttp2Init(1, 1, 0x2000, 4); Require(doomedContext > 0); - const int doomedContextBox = sceHttp2CreateCookieBox(doomedContext); + const int doomedContextBox = sceHttp2CreateCookieBox(0x10001); const int doomedContextTemplate = sceHttp2CreateTemplate(doomedContext, "agent", 2, 0); Require(doomedContextBox > 0 && doomedContextTemplate > 0); const int doomedContextRequest = sceHttp2CreateRequestWithURL(doomedContextTemplate, "GET", "https://example.com/descendant", 0); @@ -124,7 +129,8 @@ int main() { RequireSslSetters(doomedContext, 0); Require(sceHttp2Term(doomedContext) == 0); RequireSslSetters(doomedContext, InvalidId); - RequireSslSetters(doomedContextBox, InvalidId); + RequireSslSetters(doomedContextBox, 0); + Require(sceHttp2DeleteCookieBox(doomedContextBox) == 0); Require(sceHttp2DeleteCookieBox(doomedContextBox) == InvalidId); RequireSslSetters(doomedContextTemplate, InvalidId); RequireSslSetters(doomedContextRequest, InvalidId); diff --git a/docs/dev/debt/libSceHttp2.md b/docs/dev/debt/libSceHttp2.md index f3db1ce9df..74ba9af4d0 100644 --- a/docs/dev/debt/libSceHttp2.md +++ b/docs/dev/debt/libSceHttp2.md @@ -2,18 +2,18 @@ ### Silent stubs -- [libSceHttp2](../../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and its value is ignored, as for `sceHttp2SslEnableOption` and `sceHttp2SslDisableOption`. The three setters validate `id` against the live handle registry and return `SCE_HTTP2_ERROR_INVALID_ID` (`0x817B1100`) for a handle that was never allocated, was deleted, or belonged to a terminated context, following [prosper](https://github.com/mattias800/prosper). Whether the console restricts the object kind these setters accept, and what it does with an id from a previous init/term cycle, is unverified, so any live handle kind is accepted and ids are never reused. `sceHttp2Term`, `sceHttp2DeleteTemplate`, `sceHttp2DeleteRequest`, `sceHttp2CreateTemplate`, `sceHttp2CreateRequestWithURL`, `sceHttp2CreateCookieBox` and `sceHttp2SendRequestAsync` also return the invalid-id error for a handle of the wrong kind or no longer live, following prosper (not measured on a console) +- [libSceHttp2](../../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and its value is ignored, as for `sceHttp2SslEnableOption` and `sceHttp2SslDisableOption`. The three setters validate `id` against the live handle registry and return `SCE_HTTP2_ERROR_INVALID_ID` (`0x817B1100`) for a handle that was never allocated, was deleted, or belonged to a terminated context, following [prosper](https://github.com/mattias800/prosper). Whether the console restricts the object kind these setters accept, and what it does with an id from a previous init/term cycle, is unverified, so any live handle kind is accepted and ids are never reused. `sceHttp2Term`, `sceHttp2DeleteTemplate`, `sceHttp2DeleteRequest`, `sceHttp2CreateTemplate`, `sceHttp2CreateRequestWithURL` and `sceHttp2SendRequestAsync` also return the invalid-id error for a handle of the wrong kind or no longer live, following prosper (not measured on a console) ### Unknown function info -- [sceHttp2WebSocketSetPingInterval](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2, NID `YfaXPnc8PJE`) - imported by `libdiscord_partner_sdk.prx` of PPSA23126 (issue 2648). The parameters, the unit of the interval and the console errors are unknown. Every call throws. -- [sceHttp2WebSocketSetPingTimeout](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2, NID `-MU9L6S3Fj4`) - imported by PPSA27836 01.000.001, whose loader stopped on the missing export; not called during its start-up. The parameters and the console errors are unknown. Every call throws. - [sceHttp2DeleteCookieBox](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA35943's Unity player. The `(int cookieBoxId)` signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http2.cs#L215-L217). A box `sceHttp2CreateCookieBox` returned is discarded; an unknown or already deleted id returns the library's invalid-id error (0x817B1100), as [prosper](https://github.com/mattias800/prosper/blob/b1bec57186a0/prosper/src/hle/net/hle_http2.cpp#L512-L523) does; not measured on a console - [sceHttp2SetResolveRetry](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signature `(int id, int32_t retry)` from libSceHttp's `sceHttpSetResolveRetry`; the value is accepted and ignored like the other Http2 settings, since no request reaches the network -- [libSceHttp2 cookie boxes](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - `sceHttp2CreateCookieBox` is taken to take the library context id first, as in the [prosper](https://github.com/mattias800/prosper) reimplementation; further arguments are unknown and ignored. `sceHttp2SetRequestNoContentLength` is assumed to take only the request id. Cookies are never stored, so `sceHttp2CookieFlush` has nothing to discard +- [libSceHttp2 cookie boxes](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - `sceHttp2CreateCookieBox` takes a machine-sized pool size, requires at least 64 KiB and allocates independently of HTTP contexts. The host reserves the pool with 16 KiB rounding but does not reproduce native cookie-heap bookkeeping, identifier ranges or retained request references. Its delete signature and invalid-ID error are confirmed. `sceHttp2SetRequestNoContentLength` takes only the request id. Cookies are never stored, so `sceHttp2CookieFlush` has nothing to discard - [libSceHttp2 pending exports](../../../core/libs/prx/libSceHttp2/Export.cpp) - 18 named callable exports were identified in the PS5 3.20 export table and cross-checked against firmware 13.60. Signatures and behavior remain unverified; declarations use the unknown-signature convention and throw through `NotImplemented_nid_no_patch`. Evidence: [3.20 export list](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceHttp2.c). ### Functional - [libSceHttp2 asynchronous requests](../../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) never reach the network: `sceHttp2SendRequestAsync` accepts the request, queues a completion whose result is the library's network error and triggers the user event named by the kqueue option (equeue at offset 0, event id at 8, user data at 16, layout inferred from PPSA17221); `sceHttp2WaitAsync` returns that completion. The completion's `event_type` value is unknown and left 0. PPSA17221 completes its request object only after `sceHttp2SendRequestAsync` returns, and its handler for a synchronous failure calls `shared_from_this` before that, so a send must not fail synchronously. PPSA15508 passes null kqueue and extra options at original VA 0xb63931, then calls `sceHttp2WaitAsync` with a 24-byte result and a 2,000,000 timeout, and checks the result at offset 8. A null kqueue option therefore retains the same offline completion for direct waiting without an event notification. The timeout is not used because the offline result is already complete. This path follows the original title call and caller control flow, not a console measurement. A supplied kqueue with a missing user event, an extra option, and a wait with no completed operation still throw + +- [WebSocket ping settings](../../../core/libs/prx/libSceHttp2/Export.cpp): both setters take a 32-bit request ID and a 32-bit interval/timeout in microseconds, confirmed by the timer consumer and its clock conversion. The protected implementations remain unchanged. From 582362546c315b03654e63e50aa2a61c2802dd39 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:13:50 +0300 Subject: [PATCH 350/523] fix(npauth): validate and retain request timeout settings --- core/libs/prx/libSceNpAuth/Export.cpp | 22 +++++++++++++++------- core/libs/tests/GuestNpAuth.cpp | 12 +++++++++++- docs/dev/debt/libSceNpAuth.md | 5 ++++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceNpAuth/Export.cpp b/core/libs/prx/libSceNpAuth/Export.cpp index 7ce7edb5ee..414e8de20b 100644 --- a/core/libs/prx/libSceNpAuth/Export.cpp +++ b/core/libs/prx/libSceNpAuth/Export.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -22,6 +23,8 @@ struct RequestInfo { bool aborted = false; bool polled = false; int result = SCE_NP_ERROR_SIGNED_OUT; + std::int32_t resolveRetry = 0; + std::array timeouts{}; }; std::mutex g_requestMutex; @@ -124,14 +127,19 @@ int APS5_VABI sceNpAuthPollAsync(int req_id, int* result) { return NP_POLL_ASYNC_FINISHED; } -int APS5_VABI sceNpAuthSetTimeout(int req_id, int resolve_retry, uint32_t resolve_timeout, uint32_t connect_timeout, uint32_t send_timeout, uint32_t receive_timeout) { - (void)resolve_retry; - (void)resolve_timeout; - (void)connect_timeout; - (void)send_timeout; - (void)receive_timeout; +int APS5_VABI sceNpAuthSetTimeout(std::int32_t requestId, std::int32_t resolveRetry, std::uint32_t resolveTimeout, std::uint32_t connectTimeout, std::uint32_t sendTimeout, std::uint32_t receiveTimeout) { + if (resolveRetry < 0) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + if ((static_cast(resolveRetry) | resolveTimeout | connectTimeout | sendTimeout | receiveTimeout) == 0) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + if (resolveTimeout != 0 && resolveTimeout < 1000000) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + if (connectTimeout != 0 && connectTimeout < 10000000) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + if (sendTimeout != 0 && sendTimeout < 10000000) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + if (receiveTimeout != 0 && receiveTimeout < 10000000) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; std::lock_guard lock(g_requestMutex); - return g_requests.contains(req_id) ? 0 : ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND; + const auto found = g_requests.find(requestId); + if (found == g_requests.end()) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND; + found->second.resolveRetry = resolveRetry; + found->second.timeouts = {resolveTimeout, connectTimeout, sendTimeout, receiveTimeout}; + return 0; } int APS5_VABI sceNpAuthWaitAsync(int req_id, int* result) { diff --git a/core/libs/tests/GuestNpAuth.cpp b/core/libs/tests/GuestNpAuth.cpp index 2ab80ba71c..7cabfdb6f0 100644 --- a/core/libs/tests/GuestNpAuth.cpp +++ b/core/libs/tests/GuestNpAuth.cpp @@ -50,7 +50,17 @@ int main() { int issuerId = 0; Require(sceNpAuthSetTimeout(syncReq, 2, 5000000, 10000000, 10000000, 10000000) == 0); - Require(sceNpAuthSetTimeout(asyncReq, 0, 0, 0, 0, 0) == 0); + Require(sceNpAuthSetTimeout(asyncReq, 0, 0, 0, 0, 0) == invalidArgument); + + Require(sceNpAuthSetTimeout(syncReq, -1, 1000000, 10000000, 10000000, 10000000) == invalidArgument); + Require(sceNpAuthSetTimeout(syncReq, 1, 999999, 10000000, 10000000, 10000000) == invalidArgument); + Require(sceNpAuthSetTimeout(syncReq, 1, 1000000, 9999999, 10000000, 10000000) == invalidArgument); + Require(sceNpAuthSetTimeout(syncReq, 1, 1000000, 10000000, 9999999, 10000000) == invalidArgument); + Require(sceNpAuthSetTimeout(syncReq, 1, 1000000, 10000000, 10000000, 9999999) == invalidArgument); + Require(sceNpAuthSetTimeout(syncReq, 1, 1000000, 10000000, 10000000, 10000000) == 0); + Require(sceNpAuthSetTimeout(syncReq, 1, 0, 0, 0, 0) == 0); + Require(sceNpAuthSetTimeout(syncReq, 0, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu) == 0); + Require(sceNpAuthSetTimeout(-1, 0, 0, 0, 0, 0) == invalidArgument); int pollUnprocessed = 0; Require(sceNpAuthPollAsync(asyncReq, &pollUnprocessed) == invalidId); diff --git a/docs/dev/debt/libSceNpAuth.md b/docs/dev/debt/libSceNpAuth.md index 2ad98e9bdd..9303b08f53 100644 --- a/docs/dev/debt/libSceNpAuth.md +++ b/docs/dev/debt/libSceNpAuth.md @@ -4,4 +4,7 @@ - [sceNpAuthGetAuthorizedAppCode](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - declared without parameters and returns `SCE_NP_ERROR_SIGNED_OUT` without reading them, as there is no PSN sign-in; the request parameter and code output are not known. Minecraft (PPSA17221) imports it, fails to load without it, and after its sign-in dialog treats the signed-out code as offline play. - [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCode, sceNpAuthGetAuthorizationCodeA, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdToken, sceNpAuthGetIdTokenA, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp) (PS4); the A calls forward to their V3 counterparts, which in shadPS4 ([AuthorizationCode A](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L189-L193), [V3](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L195-L199); [IdToken A](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L375-L378), [V3](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L380-L383)) call the same helper and differ only in a version flag; async requests defer `SCE_NP_ERROR_SIGNED_OUT` to Poll/Wait while sync requests fail immediately; PSN is not contacted. Parameter size validation and PS5 behavior are unverified -- [sceNpAuthSetTimeout](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - imported by PPSA01317, which fails to load without it. The signature `(reqId, resolveRetry, resolveTimeout, connTimeout, sendTimeout, recvTimeout)` follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_auth.cpp) (PS4), which only stubs it. An unknown request ID returns `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` like the other request calls; otherwise it returns OK and keeps nothing, as no request reaches the network the timeouts would bound. Argument checks of the console are unverified + +### Functional + +- [sceNpAuthSetTimeout](../../../core/libs/prx/libSceNpAuth/Export.cpp): native validation and storage are implemented: retry must be nonnegative, the all-zero tuple is invalid, resolve timeout is zero or at least 1000000, and other timeouts are zero or at least 10000000. Invalid arguments precede request lookup. Stored settings currently have no transport to configure because authentication remains offline. From 17d93d1ff2961caf86c8edf1e7f7344120ea38c1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:20:05 +0300 Subject: [PATCH 351/523] fix(npauth): restore authorized app code parameters and validation --- core/libs/SceTypes.hpp | 15 ++++++ core/libs/prx/libSceNpAuth/CMakeLists.txt | 3 +- core/libs/prx/libSceNpAuth/Export.cpp | 11 ++++- core/libs/tests/GuestNpAuth.cpp | 4 +- .../tests/GuestNpAuthAuthorizedAppCode.cpp | 48 ++++++++++++++++--- docs/dev/debt/libSceNpAuth.md | 3 +- 6 files changed, 72 insertions(+), 12 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 390c010495..688c87737a 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -962,6 +962,21 @@ struct VoicePortInfo { std::uint16_t reserved; }; +struct NpAuthClientId; + +struct NpAuthAuthorizedAppCodeParameter { + std::uint64_t size; + std::int32_t userId; + const NpAuthClientId* clientId; + const char* scope; + std::uint32_t option; +}; + +static_assert(sizeof(NpAuthAuthorizedAppCodeParameter) == 40); +static_assert(offsetof(NpAuthAuthorizedAppCodeParameter, clientId) == 16); +static_assert(offsetof(NpAuthAuthorizedAppCodeParameter, scope) == 24); +static_assert(offsetof(NpAuthAuthorizedAppCodeParameter, option) == 32); + struct VoiceResourceInfo { std::uint16_t maxVoiceInputPorts; std::uint16_t maxVoiceOutputPorts; diff --git a/core/libs/prx/libSceNpAuth/CMakeLists.txt b/core/libs/prx/libSceNpAuth/CMakeLists.txt index ce6ce6a2f8..67acfa7500 100644 --- a/core/libs/prx/libSceNpAuth/CMakeLists.txt +++ b/core/libs/prx/libSceNpAuth/CMakeLists.txt @@ -5,7 +5,8 @@ configure_windows_unwind(libSceNpAuth) if(BUILD_TESTING) add_test_executable(guest_np_auth_authorized_app_code_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpAuthAuthorizedAppCode.cpp) target_include_directories(guest_np_auth_authorized_app_code_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_auth_authorized_app_code_tests PRIVATE libSceNpAuth) + target_link_libraries(guest_np_auth_authorized_app_code_tests PRIVATE libSceNpAuth libc) + configure_windows_unwind(guest_np_auth_authorized_app_code_tests) add_test(NAME guest_np_auth_authorized_app_code COMMAND guest_np_auth_authorized_app_code_tests) if(WIN32) set_tests_properties(guest_np_auth_authorized_app_code PROPERTIES ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceNpAuth/Export.cpp b/core/libs/prx/libSceNpAuth/Export.cpp index 414e8de20b..265c3fd4df 100644 --- a/core/libs/prx/libSceNpAuth/Export.cpp +++ b/core/libs/prx/libSceNpAuth/Export.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" @@ -65,8 +66,14 @@ int APS5_VABI sceNpAuthDeleteRequest(int req_id) { return 0; } -int APS5_VABI sceNpAuthGetAuthorizedAppCode() { - return SCE_NP_ERROR_SIGNED_OUT; +int APS5_VABI sceNpAuthGetAuthorizedAppCode(std::int32_t requestId, const NpAuthAuthorizedAppCodeParameter* parameter, char* code, std::int32_t* issuerId, bool* authorizationRequired) { + (void)issuerId; + if (parameter == nullptr || code == nullptr || authorizationRequired == nullptr) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + if (parameter->size != sizeof(NpAuthAuthorizedAppCodeParameter)) return static_cast(0x80550302u); + if (parameter->userId == -1 || parameter->clientId == nullptr || parameter->scope == nullptr || parameter->option > 1) return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT; + std::lock_guard lock(g_requestMutex); + if (!g_requests.contains(requestId)) return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND; + throw std::logic_error("sceNpAuthGetAuthorizedAppCode: authorization backend is not implemented"); } int APS5_VABI sceNpAuthGetAuthorizationCodeV3(int req_id, const void* param, void* auth_code, int* issuer_id) { diff --git a/core/libs/tests/GuestNpAuth.cpp b/core/libs/tests/GuestNpAuth.cpp index 7cabfdb6f0..95b11eddc5 100644 --- a/core/libs/tests/GuestNpAuth.cpp +++ b/core/libs/tests/GuestNpAuth.cpp @@ -11,7 +11,7 @@ int APS5_VABI sceNpAuthDeleteRequest(int req_id); int APS5_VABI sceNpAuthGetAuthorizationCode(int req_id, const void* param, void* auth_code); int APS5_VABI sceNpAuthGetAuthorizationCodeA(int req_id, const void* param, void* auth_code, int* issuer_id); int APS5_VABI sceNpAuthGetAuthorizationCodeV3(int req_id, const void* param, void* auth_code, int* issuer_id); -int APS5_VABI sceNpAuthGetAuthorizedAppCode(); +int APS5_VABI sceNpAuthGetAuthorizedAppCode(std::int32_t, const NpAuthAuthorizedAppCodeParameter*, char*, std::int32_t*, bool*); int APS5_VABI sceNpAuthGetIdToken(int req_id, const void* param, void* id_token); int APS5_VABI sceNpAuthGetIdTokenA(int req_id, const void* param, void* id_token); int APS5_VABI sceNpAuthGetIdTokenV3(int req_id, const void* param, void* id_token); @@ -67,7 +67,7 @@ int main() { int waitUnprocessed = 0; Require(sceNpAuthWaitAsync(asyncReq, &waitUnprocessed) == invalidId); - Require(sceNpAuthGetAuthorizedAppCode() == signedOut); + Require(sceNpAuthGetAuthorizedAppCode(syncReq, nullptr, nullptr, nullptr, nullptr) == invalidArgument); Require(sceNpAuthGetIdToken(syncReq, nullptr, token) == invalidArgument); Require(sceNpAuthGetIdToken(syncReq, param, nullptr) == invalidArgument); diff --git a/core/libs/tests/GuestNpAuthAuthorizedAppCode.cpp b/core/libs/tests/GuestNpAuthAuthorizedAppCode.cpp index f6dfce0de8..a5d7f6fe17 100644 --- a/core/libs/tests/GuestNpAuthAuthorizedAppCode.cpp +++ b/core/libs/tests/GuestNpAuthAuthorizedAppCode.cpp @@ -1,13 +1,49 @@ -#include "prx/libc/include/general/VabiMacros.hpp" +#include "SceTypes.hpp" +#include #include +#include extern "C" { -int APS5_VABI sceNpAuthGetAuthorizedAppCode(); +int APS5_VABI sceNpAuthCreateRequest(); +int APS5_VABI sceNpAuthDeleteRequest(int); +int APS5_VABI sceNpAuthGetAuthorizedAppCode(std::int32_t, const NpAuthAuthorizedAppCodeParameter*, char*, std::int32_t*, bool*); +} + +static void Require(bool condition) { + if (!condition) std::abort(); } int main() { - constexpr int kSignedOut = static_cast(0x80550006); - if (sceNpAuthGetAuthorizedAppCode() != kSignedOut) std::abort(); - if (sceNpAuthGetAuthorizedAppCode() != kSignedOut) std::abort(); - return 0; + constexpr auto invalidArgument = static_cast(0x80550301u); + constexpr auto invalidSize = static_cast(0x80550302u); + constexpr auto requestNotFound = static_cast(0x80550306u); + std::array client{}; + std::array code{}; + code.fill('x'); + const auto original = code; + bool authorizationRequired = true; + NpAuthAuthorizedAppCodeParameter parameter{40, 1, reinterpret_cast(client.data()), "", 0}; + Require(sceNpAuthGetAuthorizedAppCode(0, nullptr, code.data(), nullptr, &authorizationRequired) == invalidArgument); + parameter.size = 39; + Require(sceNpAuthGetAuthorizedAppCode(0, ¶meter, code.data(), nullptr, &authorizationRequired) == invalidSize); + parameter.size = 40; + Require(sceNpAuthGetAuthorizedAppCode(0, ¶meter, nullptr, nullptr, &authorizationRequired) == invalidArgument); + Require(sceNpAuthGetAuthorizedAppCode(0, ¶meter, code.data(), nullptr, nullptr) == invalidArgument); + parameter.option = 2; + Require(sceNpAuthGetAuthorizedAppCode(0, ¶meter, code.data(), nullptr, &authorizationRequired) == invalidArgument); + parameter.option = 0; + parameter.userId = -1; + Require(sceNpAuthGetAuthorizedAppCode(0, ¶meter, code.data(), nullptr, &authorizationRequired) == invalidArgument); + parameter.userId = 1; + Require(sceNpAuthGetAuthorizedAppCode(0, ¶meter, code.data(), nullptr, &authorizationRequired) == requestNotFound); + const auto requestId = sceNpAuthCreateRequest(); + Require(requestId > 0); + bool threw = false; + try { + sceNpAuthGetAuthorizedAppCode(requestId, ¶meter, code.data(), nullptr, &authorizationRequired); + } catch (const std::logic_error&) { + threw = true; + } + Require(threw && code == original && authorizationRequired); + Require(sceNpAuthDeleteRequest(requestId) == 0); } diff --git a/docs/dev/debt/libSceNpAuth.md b/docs/dev/debt/libSceNpAuth.md index 9303b08f53..9f41ec750a 100644 --- a/docs/dev/debt/libSceNpAuth.md +++ b/docs/dev/debt/libSceNpAuth.md @@ -2,9 +2,10 @@ ### Unknown function info -- [sceNpAuthGetAuthorizedAppCode](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - declared without parameters and returns `SCE_NP_ERROR_SIGNED_OUT` without reading them, as there is no PSN sign-in; the request parameter and code output are not known. Minecraft (PPSA17221) imports it, fails to load without it, and after its sign-in dialog treats the signed-out code as offline play. - [sceNpAuthAbortRequest, sceNpAuthGetAuthorizationCode, sceNpAuthGetAuthorizationCodeA, sceNpAuthGetAuthorizationCodeV3, sceNpAuthGetIdToken, sceNpAuthGetIdTokenA, sceNpAuthGetIdTokenV3, sceNpAuthPollAsync, sceNpAuthWaitAsync](../../../core/libs/prx/libSceNpAuth/Export.cpp) (libSceNpAuth) - request tracking, `ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND` (0x80550306) on unknown IDs, `ORBIS_NP_AUTH_ERROR_INVALID_ID` (0x80550307) on non-async or unprocessed requests in Poll/Wait, `ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT` (0x80550301) on null arguments, and `ORBIS_NP_AUTH_ERROR_ABORTED` (0x80550304) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp) (PS4); the A calls forward to their V3 counterparts, which in shadPS4 ([AuthorizationCode A](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L189-L193), [V3](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L195-L199); [IdToken A](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L375-L378), [V3](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_auth/np_auth.cpp#L380-L383)) call the same helper and differ only in a version flag; async requests defer `SCE_NP_ERROR_SIGNED_OUT` to Poll/Wait while sync requests fail immediately; PSN is not contacted. Parameter size validation and PS5 behavior are unverified ### Functional - [sceNpAuthSetTimeout](../../../core/libs/prx/libSceNpAuth/Export.cpp): native validation and storage are implemented: retry must be nonnegative, the all-zero tuple is invalid, resolve timeout is zero or at least 1000000, and other timeouts are zero or at least 10000000. Invalid arguments precede request lookup. Stored settings currently have no transport to configure because authentication remains offline. + +- [sceNpAuthGetAuthorizedAppCode](../../../core/libs/prx/libSceNpAuth/Export.cpp): the five-argument ABI and 40-byte request parameter are recovered. Pointer, size, user, option and request-ID validation replaces the unconditional signed-out result. Valid calls throw because authorization and asynchronous result delivery are not implemented. Client-ID storage is copied as 136 bytes by the firmware; its internal fields and the complete output-code format remain unresolved. From 10829db69fb0d334f8f12b597b6a8b5593c6984e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:20:06 +0300 Subject: [PATCH 352/523] fix(font): validate script and language selection --- core/libs/prx/libSceFont/src/Style.cpp | 38 ++++++++++++++++++++++++-- core/libs/tests/GuestFont.cpp | 27 ++++++++++++++---- docs/dev/debt/libSceFont.md | 3 +- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Style.cpp b/core/libs/prx/libSceFont/src/Style.cpp index e4adb89793..22291654ee 100644 --- a/core/libs/prx/libSceFont/src/Style.cpp +++ b/core/libs/prx/libSceFont/src/Style.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "prx/libSceFont/include/FontInternal.hpp" @@ -75,6 +76,29 @@ int GetFontSetting(FontHandle fontHandle, std::map FontState::*table, }); } +bool IsScriptLanguageValid(int script, int language) { + if (script == 0x100) return language >= 0x100 && language <= 0x105; + if (script == 0x600) return language >= 0x600 && language <= 0x602; + if (script != 0x3000) return false; + switch (language) { + case 0x3000: + case 0x3011: + case 0x3012: + case 0x3013: + case 0x3014: + case 0x3016: + case 0x3017: + case 0x3018: + case 0x3021: + case 0x3022: + case 0x3023: + case 0x3041: + return true; + default: + return false; + } +} + void ResetScaleOutputs(float* w, float* h) { if (w) *w = 0.0f; if (h) *h = 0.0f; @@ -119,11 +143,21 @@ int APS5_VABI sceFontGetAttribute(FontHandle fontHandle, int attribute, int* now } int APS5_VABI sceFontSetScriptLanguage(FontHandle fontHandle, int fontScript, int fontLanguage) { - return SetFontSetting(fontHandle, &FontState::scriptLanguages, fontScript, fontLanguage); + const int result = AccessFontSettings(fontHandle, [&](FontState&) { + return IsScriptLanguageValid(fontScript, fontLanguage) ? SCE_FONT_OK : SCE_FONT_ERROR_INVALID_PARAMETER; + }); + if (result != SCE_FONT_OK) return result; + throw std::logic_error("sceFontSetScriptLanguage: driver language selection is not implemented"); } int APS5_VABI sceFontGetScriptLanguage(FontHandle fontHandle, int fontScript, int* fontLanguage) { - return GetFontSetting(fontHandle, &FontState::scriptLanguages, fontScript, fontLanguage); + if (fontLanguage) *fontLanguage = 0; + const int result = AccessFontSettings(fontHandle, [&](FontState&) { + if (!fontLanguage) return SCE_FONT_ERROR_INVALID_PARAMETER; + return fontScript == 0x100 || fontScript == 0x600 || fontScript == 0x3000 ? SCE_FONT_OK : SCE_FONT_ERROR_INVALID_PARAMETER; + }); + if (result != SCE_FONT_OK) return result; + throw std::logic_error("sceFontGetScriptLanguage: driver language query is not implemented"); } int APS5_VABI sceFontSetTypographicDesign(FontHandle fontHandle, int typographic, int feature) { diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index ac87c2de70..afa5e93d2c 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -302,12 +302,29 @@ int main() { const std::vector fontData = EmptyGlyphFont(); Require(sceFontOpenFontMemory(library, fontData.data(), static_cast(fontData.size()), nullptr, &font) == SCE_FONT_OK && font != nullptr); int setting = -1; - Require(sceFontGetScriptLanguage(font, 3, &setting) == SCE_FONT_OK && setting == 0); - Require(sceFontSetScriptLanguage(font, 3, 7) == SCE_FONT_OK && sceFontSetScriptLanguage(font, 4, 9) == SCE_FONT_OK); - Require(sceFontGetScriptLanguage(font, 3, &setting) == SCE_FONT_OK && setting == 7); - Require(sceFontGetScriptLanguage(font, 4, &setting) == SCE_FONT_OK && setting == 9); - Require(sceFontGetScriptLanguage(font, 3, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontGetScriptLanguage(font, 3, &setting) == SCE_FONT_ERROR_INVALID_PARAMETER && setting == 0); + Require(sceFontSetScriptLanguage(font, 3, 7) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontSetScriptLanguage(font, 0x100, 0x106) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontSetScriptLanguage(font, 0x600, 0x603) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontSetScriptLanguage(font, 0x3000, 0x3015) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontGetScriptLanguage(font, 0x100, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); Require(sceFontSetScriptLanguage(nullptr, 3, 7) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + Require(sceFontGetScriptLanguage(nullptr, 3, nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + for (const auto script : {0x100, 0x600, 0x3000}) { + bool setThrew = false; + bool getThrew = false; + try { + sceFontSetScriptLanguage(font, script, script); + } catch (const std::logic_error&) { + setThrew = true; + } + try { + sceFontGetScriptLanguage(font, script, &setting); + } catch (const std::logic_error&) { + getThrew = true; + } + Require(setThrew && getThrew && setting == 0); + } Require(sceFontSetTypographicDesign(font, 1, 2) == SCE_FONT_OK); Require(sceFontGetTypographicDesign(font, 1, &setting) == SCE_FONT_OK && setting == 2); Require(sceFontGetTypographicDesign(font, 5, &setting) == SCE_FONT_OK && setting == 0); diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 919ecce40a..a8c0fe64db 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -2,7 +2,8 @@ ### Unknown function info -- [sceFontSetScriptLanguage, sceFontGetScriptLanguage, sceFontSetTypographicDesign, sceFontGetTypographicDesign](../../../core/libs/prx/libSceFont/src/Style.cpp) (libSceFont) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.cs), not confirmed for the PS5. The handle keeps the language set for each script and the feature set for each typographic design and returns them, with 0 for one never set; the console default and its valid ranges are unknown, so any value is accepted. Neither setting changes shaping or rendering yet +- [sceFontSetTypographicDesign and sceFontGetTypographicDesign](../../../core/libs/prx/libSceFont/src/Style.cpp): signatures are confirmed by the supplied PS5 library. Native feature ranges, defaults and driver-dependent availability are not implemented; the host still accepts arbitrary values and stores them without affecting shaping or rendering. +- [sceFontSetScriptLanguage and sceFontGetScriptLanguage](../../../core/libs/prx/libSceFont/src/Style.cpp): native script/language ranges, handle-error precedence and clearing of failed getter outputs are implemented. Valid driver queries and updates throw; the previous arbitrary-value map and fabricated zero default are no longer used. Driver selection and actual language-dependent shaping remain unimplemented. - [sceFontGetRenderScaledKerning](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale - [sceFontDettachDeviceCacheBuffer](../../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. It detaches the cache buffer attached by `sceFontAttachDeviceCacheBuffer`, returns the buffer and its size, and reports `SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER` when no cache buffer is attached - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` From f60226dcf5155777335b37f103bcfa2263f4d32a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:21:07 +0300 Subject: [PATCH 353/523] fix(font): restore writing order and metrics validation --- core/libs/prx/libSceFont/src/WritingLine.cpp | 27 ++++++++++++------ core/libs/tests/GuestFontWritingLine.cpp | 29 ++++++++++++-------- docs/dev/debt/libSceFont.md | 4 +-- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceFont/src/WritingLine.cpp b/core/libs/prx/libSceFont/src/WritingLine.cpp index 35e014c3c8..c0643b51d0 100644 --- a/core/libs/prx/libSceFont/src/WritingLine.cpp +++ b/core/libs/prx/libSceFont/src/WritingLine.cpp @@ -246,19 +246,24 @@ int APS5_VABI sceFontDestroyWritingLine(void** pWritingLine) { } int APS5_VABI sceFontWritingLineClear(void* writingLine) { - auto* line = GetLine(__func__, writingLine); + if (!writingLine) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* line = static_cast(writingLine); + { + std::lock_guard lock(linesMutex); + if (!lines.contains(line)) return SCE_FONT_ERROR_INVALID_WRITING; + } ResetOrders(*line); return SCE_FONT_OK; } int APS5_VABI sceFontWritingLineWritesOrder(void* writingLine, std::uint64_t writingAttribute, const FontWritingMetrics* writingMetrics, void* writingOrderer) { - auto* line = GetLine(__func__, writingLine); - if (writingAttribute == 0) writingAttribute = WRITING_ATTRIBUTE_ID; - if ((writingAttribute & WRITING_ATTRIBUTE_ID_MASK) != WRITING_ATTRIBUTE_ID) - Unsupported(__func__, "writing attribute " + std::to_string(writingAttribute) + " is not modelled"); const std::size_t level = (writingAttribute >> 8) & 0xFF; - if (level >= WRITING_LEVEL_COUNT) Unsupported(__func__, "embedding level " + std::to_string(level) + " (error code not verified)"); - if (!writingOrderer) Unsupported(__func__, "null writing orderer (error code not verified)"); + if (!writingLine || !writingOrderer || (writingAttribute & WRITING_ATTRIBUTE_ID_MASK) != WRITING_ATTRIBUTE_ID || level >= WRITING_LEVEL_COUNT) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* line = static_cast(writingLine); + { + std::lock_guard lock(linesMutex); + if (!lines.contains(line)) return SCE_FONT_ERROR_INVALID_WRITING; + } if (line->cursor) ResetOrders(*line); WritingOrder* order = &line->orders.emplace_back(); order->orderer = writingOrderer; @@ -319,8 +324,12 @@ const FontWritingLineStep* APS5_VABI sceFontWritingLineRefersRenderStep(void* wr } int APS5_VABI sceFontWritingLineGetRenderMetrics(void* writingLine, FontWritingMetrics* writingMetrics) { - auto* line = GetLine(__func__, writingLine); - if (!writingMetrics) Unsupported(__func__, "null metrics pointer (error code not verified)"); + if (!writingMetrics) return SCE_FONT_ERROR_INVALID_PARAMETER; + *writingMetrics = {}; + if (!writingLine) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* line = static_cast(writingLine); + std::lock_guard lock(linesMutex); + if (!lines.contains(line)) return SCE_FONT_ERROR_INVALID_WRITING; *writingMetrics = line->renderMetrics; return SCE_FONT_OK; } diff --git a/core/libs/tests/GuestFontWritingLine.cpp b/core/libs/tests/GuestFontWritingLine.cpp index 7b40b889ca..019d9aaffe 100644 --- a/core/libs/tests/GuestFontWritingLine.cpp +++ b/core/libs/tests/GuestFontWritingLine.cpp @@ -108,14 +108,15 @@ int main() { const FontWritingMetrics runC{8.0f, 0.0f, {-4.0f, 1.0f, 2.0f, 5.0f}}; const FontWritingMetrics runD{4.0f, 0.0f, {-4.0f, 1.0f, 0.0f, 20.0f}}; - Require(Throws([&] { sceFontWritingLineWritesOrder(nullptr, Attribute(0), &runA, &a); })); - Require(Throws([&] { sceFontWritingLineWritesOrder(line, 1, &runA, &a); })); - Require(Throws([&] { sceFontWritingLineWritesOrder(line, 0x1FB, &runA, &a); })); - Require(Throws([&] { sceFontWritingLineWritesOrder(line, Attribute(0), &runA, nullptr); })); - Require(Throws([&] { sceFontWritingLineWritesOrder(line, Attribute(0x7F), &runA, &a); })); + Require(sceFontWritingLineWritesOrder(nullptr, Attribute(0), &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineWritesOrder(line, 1, &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineWritesOrder(line, 0x1FB, &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runA, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineWritesOrder(line, Attribute(0x7F), &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); Require(sceFontWritingLineRefersRenderStep(line) == nullptr); - Require(sceFontWritingLineWritesOrder(line, 0, &runA, &a) == SCE_FONT_OK); + Require(sceFontWritingLineWritesOrder(line, 0, &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runA, &a) == SCE_FONT_OK); Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runB, &b) == SCE_FONT_OK); Require(SpaceIs(line, 16.0f, 1.0f, 0.0f, 1.0f)); Require(sceFontWritingLineGetRenderMetrics(line, &metrics) == SCE_FONT_OK && Same(metrics, {})); @@ -222,15 +223,19 @@ int main() { Require(sceFontWritingLineGetOrderingSpace(rtl, nullptr, nullptr, &space, nullptr) == SCE_FONT_OK && space == 15.0f); Require(Throws([&] { sceFontWritingLineGetOrderingSpace(rtl, &space, &space, &space, nullptr); })); Require(Throws([&] { sceFontWritingLineGetOrderingSpace(nullptr, &space, nullptr, nullptr, nullptr); })); - Require(Throws([&] { sceFontWritingLineGetRenderMetrics(nullptr, &metrics); })); - Require(Throws([&] { sceFontWritingLineGetRenderMetrics(rtl, nullptr); })); - Require(Throws([&] { sceFontWritingLineClear(nullptr); })); + Require(sceFontWritingLineGetRenderMetrics(nullptr, &metrics) == SCE_FONT_ERROR_INVALID_PARAMETER && Same(metrics, {})); + Require(sceFontWritingLineGetRenderMetrics(rtl, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineClear(nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); int notALine = 0; Require(sceFontWritingLineRefersRenderStep(nullptr) == nullptr); - Require(Throws([&] { sceFontWritingLineClear(¬ALine); })); + Require(sceFontWritingLineClear(¬ALine) == SCE_FONT_ERROR_INVALID_WRITING); Require(Throws([&] { sceFontWritingLineRefersRenderStep(¬ALine); })); Require(Throws([&] { sceFontWritingLineGetOrderingSpace(¬ALine, &space, &space, &space, &space); })); + metrics = runA; + Require(sceFontWritingLineGetRenderMetrics(¬ALine, &metrics) == SCE_FONT_ERROR_INVALID_WRITING && Same(metrics, {})); + Require(sceFontWritingLineWritesOrder(¬ALine, 0, nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontWritingLineWritesOrder(¬ALine, Attribute(0), nullptr, &a) == SCE_FONT_ERROR_INVALID_WRITING); void* bogus = ¬ALine; Require(Throws([&] { sceFontDestroyWritingLine(&bogus); }) && bogus == ¬ALine); @@ -238,8 +243,8 @@ int main() { Require(sceFontDestroyWritingLine(&line) == SCE_FONT_OK && line == nullptr); Require(sceFontDestroyWritingLine(&line) == SCE_FONT_OK && line == nullptr); Require(sceFontDestroyWritingLine(nullptr) == SCE_FONT_OK); - Require(Throws([&] { sceFontWritingLineClear(destroyed); })); - Require(Throws([&] { sceFontWritingLineWritesOrder(destroyed, Attribute(0), &runA, &a); })); + Require(sceFontWritingLineClear(destroyed) == SCE_FONT_ERROR_INVALID_WRITING); + Require(sceFontWritingLineWritesOrder(destroyed, Attribute(0), &runA, &a) == SCE_FONT_ERROR_INVALID_WRITING); Require(sceFontDestroyWritingLine(<r) == SCE_FONT_OK && ltr == nullptr); Require(sceFontDestroyWritingLine(&rtl) == SCE_FONT_OK && rtl == nullptr); std::puts("font writing line: ok"); diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index a8c0fe64db..f26cf6d061 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -8,7 +8,7 @@ - [sceFontDettachDeviceCacheBuffer](../../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. It detaches the cache buffer attached by `sceFontAttachDeviceCacheBuffer`, returns the buffer and its size, and reports `SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER` when no cache buffer is attached - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` - [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title; the outputs are inferred from the names. The resolution is the face's units per EM and the scale pixel the driver's face scale (units per EM / 64 for FreeType), the pixel scale a font has when opened. Either output may be null, as in `sceFontGetResolutionDpi` -- [sceFontCreateWritingLine, sceFontWritingLineWritesOrder](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - signatures and the `SceFontWritingLineStep`/`SceFontCreateWritingLineDetail` layouts (detail id `0x0FD5`) taken from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFontStructs.cs), not confirmed on a title. The 64-bit writing attribute is read as the PS4 11.00 `libSceFont.sprx` reads it: low byte `0xFA`, embedding level in bits 8-15 (0 to 126), bit 30 and bit 31 marking space before and after the run; the PS5 library was not measured, and the names of the two bits are ours. PPSA01325 builds its attribute the same way (`0xFA | level << 8`, bits 30 and 31) and passes a null metrics pointer when its run has no metrics object. Attribute 0 is accepted and treated as `0xFA` with level 0 and no spacing bits, as before this reading; the PS4 library rejects it and what the PS5 library does with it is unknown. Any other attribute, a level above 126 and a null orderer throw: the PS4 library returns `0x80460002` for them, not confirmed for the PS5 +- [sceFontCreateWritingLine, sceFontWritingLineWritesOrder](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): supplied PS5 code confirms the four-argument signatures, low attribute byte `0xFA`, embedding levels 0 through 126 and spacing bits 30/31. Invalid attributes, including zero, null orderers and null lines return `0x80460002`; invalid non-null line handles return `0x8046000A`. Metrics are optional. Native creation accepts only forms `0x11`/`0x12` and a null detail, validates font memory and allocates 2208 bytes through its callback; those creation and allocation rules remain unimplemented. - [sceFontWritingRefersRenderStepCharacter](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the second argument is an out pointer to the writing's letter step, as in [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1882-L1896) and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings. Measured on PS4 firmware 11.00 `libSceFont.sprx` (not on a PS5): exactly 8 bytes are written, the address of a step stored inside the writing, and a null writing writes null; shadPS4 and prosper treat the argument as a struct, which the measurement contradicts. The letter step lives in the writing, so the pointer stays valid as long as the writing does, and its contents are rebuilt by each call of this function. On the PS4 library repeated calls on one step walk the characters it covers and then return null; here every call returns the step's one character, so the second call differs. Assumed: each step covers one character and one glyph. PPSA06699 (Hot Shots Golf 2) dereferences the returned pointer for its character counts. - [sceFontWritingLineGetOrderingSpace](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - the four outputs are written in the order the PS4 11.00 `libSceFont.sprx` writes them (line advance, leading space, space between spaced runs, trailing space); the parameter names in the [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) IDA type database and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings order them differently (head, inline, tail, advance), and the PS5 library was not measured. PPSA01325's only call uses them in the PS4 order: it computes first - second - fourth - 0.25 * third and compares that, divided by a scale, with its width limit. The PS4 library writes the trailing space through its pointer without a null check whenever the leading-space pointer is set; that call throws here - [sceFontCharacterGetSyllableStringState](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the signature `(textCharacter, int* syllableStringState)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs). Measured on PS4 firmware 11.00 `libSceFont.sprx` (not on a PS5): the state is 0 for Latin, CJK, Hangul, Arabic, Hebrew, Devanagari and the other scripts tried, and the library sets 1, 3 or 4 (never 2 on real text) for Thai while it creates the string; a null character or a null result returns `SCE_FONT_ERROR_INVALID_PARAMETER`, and a null character writes 0 to a non-null result. This returns 0 for every character outside the Thai block (U+0E00 to U+0E7F), as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1805-L1815) does, and throws for Thai characters, whose cluster states are not modelled. SharpProspero's `SceFontSyllableString` names 0 NonDetection, 1 Starting, 2 Following, 3 Depending and 4 Connecting. PPSA06699 (Hot Shots Golf 2) calls it. @@ -21,7 +21,7 @@ - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the other script files of a set are chosen from its type, not measured on a console: Vietnamese for low-byte bit 0x10, Thai for script bit 0x10, Japanese for 0x04, simplified Chinese for 0x80 (before Japanese with 0x08), Hangul for 0x20 and Arabic for low-byte bit 0x80, as the SDK set names (`..._JP_AR`, `..._GBCJK_TH`) listed by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/font/font_internal.cpp) suggest; the file order and the Hangul file name (`SCEPS4Yoongd-Medium.otf`, `SCEPS4Yoongd-Bold.otf`) also come from shadPS4. A character the primary file lacks is taken from the first of them that has it; kerning between two files is 0, and `sceFontGetCharGlyphCode` and `sceFontGetFontGlyphsCount` answer for the primary file only - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans Arabic, Noto Sans CJK with its Korean face for Hangul) is loaded instead; its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [sceFontGetCharGlyphMetrics, sceFontGetRenderCharGlyphMetrics](../../../core/libs/prx/libSceFont/src/Layout.cpp) (libSceFont) report the vertical metrics the way the [FreeType driver](../../../core/libs/prx/libSceFontFt/src/Driver.cpp) reports them for a rendered glyph, not confirmed on a title: FreeType's vertical advance, and for a face without vertical metrics a bearing of half the horizontal advance left of the horizontal bearing and of half the advance left over by the glyph height. -- [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics`, `sceFontWritingLineGetOrderingSpace` and `sceFontWritingLineClear` follow the PS4 11.00 `libSceFont.sprx` (firmware module, read from its disassembly and compared call for call on random sequences): runs are linked in visual order by embedding level, the space at each spaced join is summed for the line, steps walk the visual order from the left (form `0x12`) or from the right (form `0x11`) and the render metrics grow as steps are consumed. The PS5 library was not measured. Not from that module: form `0x10` is accepted and treated as left to right and a detail with id `0x0FD5` is accepted (the PS4 library rejects both); the memory argument of `sceFontCreateWritingLine` and the rest of the detail are not read (the PS4 library validates its library object there); an unknown writing form, another detail id, a null line pointer on creation, unknown and null handles (except in `sceFontWritingLineRefersRenderStep`, which returns null for a null line) and a null output of `sceFontWritingLineGetRenderMetrics` throw instead of returning the PS4 error codes, which are not confirmed for the PS5; `sceFontDestroyWritingLine` was not compared. Lines are allocated on the host, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. +- [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics`, `sceFontWritingLineGetOrderingSpace` and `sceFontWritingLineClear` follow the PS4 11.00 `libSceFont.sprx` (firmware module, read from its disassembly and compared call for call on random sequences): runs are linked in visual order by embedding level, the space at each spaced join is summed for the line, steps walk the visual order from the left (form `0x12`) or from the right (form `0x11`) and the render metrics grow as steps are consumed. The PS5 library was not measured. Not from that module: form `0x10` is accepted and treated as left to right and a detail with id `0x0FD5` is accepted (the PS4 library rejects both); the memory argument of `sceFontCreateWritingLine` and the rest of the detail are not read (the PS4 library validates its library object there); an unknown writing form, another detail id, a null line pointer on creation, unknown and null handles (except in `sceFontWritingLineRefersRenderStep`, which returns null for a null line) and other unchecked outputs throw. PS5 validation for writes, clear and render metrics is confirmed and implemented, including zeroing failed metrics outputs; `sceFontDestroyWritingLine` was not compared. Lines are allocated on the host, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [Font graphics and metric signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Metric, outline-profile, style-information and expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged. From 979aa24f40f1cbe0ff2a49666268f26917a7df2e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:23:31 +0300 Subject: [PATCH 354/523] fix(net): restore server startup and routing parameter layouts --- core/libs/prx/libSceNet/Export.cpp | 63 ++++++++++++++++++++++++++++-- docs/dev/debt/libSceNet.md | 6 +-- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index f69fa0f01d..808cdc7364 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -104,6 +104,63 @@ struct NetAddress6StartParameter; using NetAddressCallback = void (APS5_VABI *)(void* userArg, std::int32_t event, std::int32_t error); +struct NetDhcpdStartParameter { + std::uint32_t addresses[5]; + std::uint32_t maxClients; +}; + +struct NetPppoeStartParameter { + std::uint32_t dnsAddresses[2]; + char userName[128]; + char password[128]; + std::int32_t mtu; + std::uint32_t flags; +}; + +struct NetRoutingAddressParameter { + std::uint32_t address; + std::uint32_t mask; +}; + +struct NetRoutingServerParameter { + std::int32_t count; + std::uint32_t localAddresses[2]; + std::uint32_t remoteAddresses[2]; +}; + +struct NetRoutingRule { + std::int32_t protocol; + std::uint32_t firstAddress; + std::uint16_t firstPort; + std::uint16_t reserved; + std::uint32_t secondAddress; + std::uint16_t secondPort; + std::uint16_t flags; +}; + +struct NetRoutingRuleParameter { + std::int32_t count; + const NetRoutingRule* rules; +}; + +static_assert(sizeof(NetRoutingAddressParameter) == 8); +static_assert(sizeof(NetRoutingServerParameter) == 20); +static_assert(offsetof(NetRoutingServerParameter, remoteAddresses) == 12); +static_assert(sizeof(NetRoutingRule) == 20); +static_assert(offsetof(NetRoutingRule, firstPort) == 8); +static_assert(offsetof(NetRoutingRule, secondAddress) == 12); +static_assert(offsetof(NetRoutingRule, flags) == 18); +static_assert(sizeof(NetRoutingRuleParameter) == 16); +static_assert(offsetof(NetRoutingRuleParameter, rules) == 8); + +static_assert(sizeof(NetDhcpdStartParameter) == 24); +static_assert(offsetof(NetDhcpdStartParameter, maxClients) == 20); +static_assert(sizeof(NetPppoeStartParameter) == 272); +static_assert(offsetof(NetPppoeStartParameter, userName) == 8); +static_assert(offsetof(NetPppoeStartParameter, password) == 136); +static_assert(offsetof(NetPppoeStartParameter, mtu) == 264); +static_assert(offsetof(NetPppoeStartParameter, flags) == 268); + struct NetInfoDumpParameter { std::uint32_t flags; std::uint32_t intervalSeconds; @@ -2760,7 +2817,7 @@ int APS5_VABI sceNetInfoDumpStop(void) { return 0; } -int APS5_VABI sceNetDhcpdStart(void) { +int APS5_VABI sceNetDhcpdStart(std::uint32_t interfaceId, NetAddressCallback callback, void* userArg, std::int32_t threadParameter1, std::int32_t threadParameter2, const NetDhcpdStartParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2920,7 +2977,7 @@ int APS5_VABI sceNetConfigEtherSetLinkMode(std::uint32_t interfaceId, int mode) return 0; } -int APS5_VABI sceNetPppoeStart(void) { +int APS5_VABI sceNetPppoeStart(std::uint32_t interfaceId, std::int32_t pppoeInterfaceId, NetAddressCallback callback, void* userArg, std::int32_t threadParameter1, std::int32_t threadParameter2, const NetPppoeStartParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2950,7 +3007,7 @@ int APS5_VABI sceNetConfigSetDefaultScope(std::uint32_t interfaceId, const char* return 0; } -int APS5_VABI sceNetConfigRoutingStart(void) { +int APS5_VABI sceNetConfigRoutingStart(std::uint32_t firstInterfaceId, std::uint32_t secondInterfaceId, const NetRoutingAddressParameter* addressParameter, NetRoutingServerParameter* serverParameter, const NetRoutingRuleParameter* ruleParameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 3abcb2a50e..8c3e26db4d 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -19,9 +19,9 @@ - [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced. Allocation flag 4 bypasses the system pool reserve threshold; it throws here because the pool reserve is not implemented. Flag 2 zero-fills and other bits are ignored, matching the system allocator. - [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. -- [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature -- [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature +- [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp): six arguments and the three-argument address callback are confirmed. The 24-byte parameter contains five network-order addresses and a client limit. Individual address roles and the two unused thread-option meanings remain unresolved. +- [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp): seven arguments are confirmed, including the parameter pointer on the stack. The 272-byte parameter contains two DNS addresses, two 128-byte credential arrays, MTU and flags. Callback arguments are user data, event and error; the two unused thread-option meanings remain unresolved. +- [sceNetConfigRoutingStart](../../../core/libs/prx/libSceNet/Export.cpp): five pointer/scalar arguments are confirmed. Input address data is eight bytes; the mutable server parameter is 20 bytes and duplicates its first address when its count is one. Optional rules contain a count and pointer to 20-byte records with protocol, two address/port pairs and flags. Layouts are defined; rule flag meanings and protocol-specific port interpretation remain unresolved. - [sceNetConfigRoutingShowRoutingConfig](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. ### Functional From 4fa7e142c3b6b9e1ddc6f52778f5d4be9e0a1a26 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:26:38 +0300 Subject: [PATCH 355/523] fix(npentitlementaccess): restore request and result argument signatures --- core/libs/SceTypes.hpp | 18 +++++++++++ .../prx/libSceNpEntitlementAccess/Export.cpp | 30 ++++++++--------- core/libs/tests/GuestOfflineImports.cpp | 32 +++++++++---------- docs/dev/debt/libSceNpEntitlementAccess.md | 2 +- 4 files changed, 50 insertions(+), 32 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 688c87737a..9c81747059 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1411,6 +1411,24 @@ struct NpUnifiedEntitlementLabel { char padding[3]; }; +struct NpServiceEntitlementLabel { + char data[7]; + std::uint8_t reserved[13]; +}; + +struct NpEntitlementAccessTransactionId { + char data[65]; + std::uint8_t reserved[7]; +}; + +struct NpEntitlementAccessListParameter; +struct NpEntitlementAccessUnifiedInfo; +struct NpEntitlementAccessServiceInfo; + +static_assert(sizeof(NpUnifiedEntitlementLabel) == 20); +static_assert(sizeof(NpServiceEntitlementLabel) == 20); +static_assert(sizeof(NpEntitlementAccessTransactionId) == 72); + struct NpEntitlementAccessInitParam { char reserved[32]; }; struct NpEntitlementAccessBootParam { char reserved[32]; }; diff --git a/core/libs/prx/libSceNpEntitlementAccess/Export.cpp b/core/libs/prx/libSceNpEntitlementAccess/Export.cpp index ba654ff02f..ecec40a1a2 100644 --- a/core/libs/prx/libSceNpEntitlementAccess/Export.cpp +++ b/core/libs/prx/libSceNpEntitlementAccess/Export.cpp @@ -118,46 +118,46 @@ int APS5_VABI sceNpEntitlementAccessInitialize(const NpEntitlementAccessInitPara return 0; } -int APS5_VABI sceNpEntitlementAccessAbortRequest(void) { +int APS5_VABI sceNpEntitlementAccessAbortRequest(std::int64_t requestId) { return 0; } -int APS5_VABI sceNpEntitlementAccessDeleteRequest(void) { +int APS5_VABI sceNpEntitlementAccessDeleteRequest(std::int64_t requestId) { return 0; } -int APS5_VABI sceNpEntitlementAccessGenerateTransactionId(void* transaction_id) { - if (!transaction_id) return SCE_NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; +int APS5_VABI sceNpEntitlementAccessGenerateTransactionId(NpEntitlementAccessTransactionId* transactionId) { + if (!transactionId) return SCE_NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessPollConsumeEntitlement(void) { +int APS5_VABI sceNpEntitlementAccessPollConsumeEntitlement(std::int64_t requestId, std::int32_t* result, std::int32_t* remainingCount) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessRequestConsumeUnifiedEntitlement(void) { +int APS5_VABI sceNpEntitlementAccessRequestConsumeUnifiedEntitlement(std::int32_t userId, std::uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, const NpEntitlementAccessTransactionId* transactionId, std::int32_t count, std::int64_t* requestId) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessRequestConsumeServiceEntitlement(void) { +int APS5_VABI sceNpEntitlementAccessRequestConsumeServiceEntitlement(std::int32_t userId, std::uint32_t serviceLabel, const NpServiceEntitlementLabel* entitlementLabel, const NpEntitlementAccessTransactionId* transactionId, std::int32_t count, std::int64_t* requestId) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo(void) { +int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessServiceInfo* info) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfo(void) { +int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfo(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessUnifiedInfo* info) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfo(void) { +int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfo(std::int32_t userId, std::uint32_t serviceLabel, const NpServiceEntitlementLabel* entitlementLabel, std::int64_t* requestId) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfo(void) { +int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfo(std::int32_t userId, std::uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, std::int64_t* requestId) { return SCE_NP_ERROR_SIGNED_OUT; } @@ -175,19 +175,19 @@ int APS5_VABI sceNpEntitlementAccessGetGameTrialsFlag(uint32_t* flag) { return 0; } -int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfoList() { +int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfoList(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessServiceInfo* infos, std::uint32_t capacity, std::int32_t* firstResult, std::int32_t* secondResult, std::int32_t* thirdResult) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfoList() { +int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfoList(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessUnifiedInfo* infos, std::uint32_t capacity, std::int32_t* firstResult, std::int32_t* secondResult, std::int32_t* thirdResult) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfoList() { +int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfoList(std::int32_t userId, std::uint32_t serviceLabel, const NpServiceEntitlementLabel* entitlementLabels, std::uint32_t labelCount, const NpEntitlementAccessListParameter* parameter, std::int64_t* requestId) { return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfoList() { +int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfoList(std::int32_t userId, std::uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabels, std::uint32_t labelCount, const NpEntitlementAccessListParameter* parameter, std::int64_t* requestId) { return SCE_NP_ERROR_SIGNED_OUT; } diff --git a/core/libs/tests/GuestOfflineImports.cpp b/core/libs/tests/GuestOfflineImports.cpp index fc8be8f7f4..56dadb62ba 100644 --- a/core/libs/tests/GuestOfflineImports.cpp +++ b/core/libs/tests/GuestOfflineImports.cpp @@ -15,14 +15,14 @@ int APS5_VABI sceNpEntitlementAccessGetEntitlementKey( std::uint32_t, const NpUnifiedEntitlementLabel*, NpEntitlementAccessEntitlementKey*); int APS5_VABI sceNpEntitlementAccessGetGameTrialsFlag(std::uint32_t*); int APS5_VABI sceNpEntitlementAccessGetPftFlag(std::uint32_t*); -int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfoList(); -int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfoList(); -int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfoList(); -int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfoList(); -int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfo(); -int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfo(); -int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfo(); -int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo(); +int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfoList(std::int32_t userId, std::uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabels, std::uint32_t labelCount, const NpEntitlementAccessListParameter* parameter, std::int64_t* requestId); +int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfoList(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessUnifiedInfo* infos, std::uint32_t capacity, std::int32_t* firstResult, std::int32_t* secondResult, std::int32_t* thirdResult); +int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfoList(std::int32_t userId, std::uint32_t serviceLabel, const NpServiceEntitlementLabel* entitlementLabels, std::uint32_t labelCount, const NpEntitlementAccessListParameter* parameter, std::int64_t* requestId); +int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfoList(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessServiceInfo* infos, std::uint32_t capacity, std::int32_t* firstResult, std::int32_t* secondResult, std::int32_t* thirdResult); +int APS5_VABI sceNpEntitlementAccessRequestUnifiedEntitlementInfo(std::int32_t userId, std::uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, std::int64_t* requestId); +int APS5_VABI sceNpEntitlementAccessPollUnifiedEntitlementInfo(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessUnifiedInfo* info); +int APS5_VABI sceNpEntitlementAccessRequestServiceEntitlementInfo(std::int32_t userId, std::uint32_t serviceLabel, const NpServiceEntitlementLabel* entitlementLabel, std::int64_t* requestId); +int APS5_VABI sceNpEntitlementAccessPollServiceEntitlementInfo(std::int64_t requestId, std::int32_t* result, NpEntitlementAccessServiceInfo* info); int APS5_VABI sceRudpInit_nid_postfix(void*, int); int APS5_VABI sceRudpActivate(std::int32_t, const void*, std::uint32_t); int APS5_VABI sceRudpGetStatus(void*, std::size_t); @@ -79,14 +79,14 @@ int main() { Require(sceNpEntitlementAccessGetPftFlag(nullptr) == parameter); constexpr int signedOut = static_cast(0x80550006); - Require(sceNpEntitlementAccessRequestUnifiedEntitlementInfoList() == signedOut); - Require(sceNpEntitlementAccessPollUnifiedEntitlementInfoList() == signedOut); - Require(sceNpEntitlementAccessRequestServiceEntitlementInfoList() == signedOut); - Require(sceNpEntitlementAccessPollServiceEntitlementInfoList() == signedOut); - Require(sceNpEntitlementAccessRequestUnifiedEntitlementInfo() == signedOut); - Require(sceNpEntitlementAccessPollUnifiedEntitlementInfo() == signedOut); - Require(sceNpEntitlementAccessRequestServiceEntitlementInfo() == signedOut); - Require(sceNpEntitlementAccessPollServiceEntitlementInfo() == signedOut); + Require(sceNpEntitlementAccessRequestUnifiedEntitlementInfoList(0, 0, nullptr, 0, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessPollUnifiedEntitlementInfoList(0, nullptr, nullptr, 0, nullptr, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessRequestServiceEntitlementInfoList(0, 0, nullptr, 0, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessPollServiceEntitlementInfoList(0, nullptr, nullptr, 0, nullptr, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessRequestUnifiedEntitlementInfo(0, 0, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessPollUnifiedEntitlementInfo(0, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessRequestServiceEntitlementInfo(0, 0, nullptr, nullptr) == signedOut); + Require(sceNpEntitlementAccessPollServiceEntitlementInfo(0, nullptr, nullptr) == signedOut); Require(sceRudpActivate(-1, nullptr, 0) == static_cast(0x80770001)); std::array status; diff --git a/docs/dev/debt/libSceNpEntitlementAccess.md b/docs/dev/debt/libSceNpEntitlementAccess.md index 2a9d2f3089..d6f56513d4 100644 --- a/docs/dev/debt/libSceNpEntitlementAccess.md +++ b/docs/dev/debt/libSceNpEntitlementAccess.md @@ -4,7 +4,7 @@ - [sceNpEntitlementAccessGetAddcontEntitlementInfoIndividual, sceNpEntitlementAccessPollConsumableEntitlementInfo, sceNpEntitlementAccessRequestConsumableEntitlementInfo, sceNpEntitlementAccessRequestConsumeEntitlement](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp) (libSceNpEntitlementAccess) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpEntitlementAccess.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim - [sceNpEntitlementAccessGetEntitlementKey](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp) - the three-argument signature, 16-byte key and errors `0x817D0002`/`0x817D0007` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libNet.cpp); PPSA01521 passes a service label, entitlement label and output pointer. Only the no-key path is implemented: local entitlement labels carry no keys, and the output is untouched on failure. Console error precedence and key provisioning are unverified -- [sceNpEntitlementAccess{Request,Poll}{Unified,Service}EntitlementInfo{,List}](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp) - signatures unknown; no reference implements them, so they are declared without parameters. The single-item variants copy the behaviour of the list ones, with no source of their own. Like the consume requests in the same library, they report the signed-out NP state with `SCE_NP_ERROR_SIGNED_OUT` (`0x80550006`) and write nothing. PPSA15657 calls the unified request during start-up and continues past the error. Whether the console fails at the request or at the poll when signed out is unverified +- [Entitlement requests and polls](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp): native argument lists are restored for single-item, list and consume operations, plus abort/delete. Request IDs are 64-bit, labels occupy 20 bytes and transaction IDs occupy 72 bytes. List options occupy 32 bytes and results have a 64-byte stride; their field semantics and three list-result scalars remain unresolved. The existing signed-out responses do not reproduce native initialization, validation or asynchronous request handling; abort/delete still report success without tracking requests. ### Functional From bffa030a165416cc326d029fce12107e7db7cda5 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:27:49 +0300 Subject: [PATCH 356/523] fix(npwebapi2): track push handles and restore validation errors --- core/libs/prx/libSceNpWebApi2/Export.cpp | 54 ++++++++++++++++-------- core/libs/tests/GuestNpWebApi2.cpp | 36 +++++++++------- docs/dev/debt/libSceNpWebApi2.md | 6 +-- 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/core/libs/prx/libSceNpWebApi2/Export.cpp b/core/libs/prx/libSceNpWebApi2/Export.cpp index 9a2ebe62dd..ea33832e83 100644 --- a/core/libs/prx/libSceNpWebApi2/Export.cpp +++ b/core/libs/prx/libSceNpWebApi2/Export.cpp @@ -10,6 +10,8 @@ // The PSN web API has no service behind it: handles can be created, but requests fail. static constexpr int SCE_NP_WEBAPI2_ERROR_INVALID_ARGUMENT = static_cast(0x80553402); static constexpr int SCE_NP_WEBAPI2_ERROR_UNAVAILABLE = static_cast(0x80553406); +static constexpr int SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT = static_cast(0x80553404); +static constexpr int SCE_NP_WEBAPI2_ERROR_INVALID_HANDLE = static_cast(0x8055340D); static std::atomic g_nextHandle{1}; struct NpWebApi2PushContextId { @@ -35,6 +37,14 @@ static int CreateLibraryContext(size_t poolSize) { return libCtxId; } +struct PushHandle { + std::int32_t libraryContextId; + std::uint32_t timeout = 0; + bool aborted = false; +}; + +static std::unordered_map pushHandles; + extern "C" { int APS5_VABI sceNpWebApi2AbortRequest(int64_t request_id) { @@ -105,11 +115,11 @@ int APS5_VABI sceNpWebApi2Initialize(int lib_http_ctx_id, size_t pool_size) { return CreateLibraryContext(pool_size); } -int APS5_VABI sceNpWebApi2GetMemoryPoolStats(int lib_ctx_id, NpWebApi2MemoryPoolStats* stats) { - if (stats == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceNpWebApi2GetMemoryPoolStats(int libraryContextId, NpWebApi2MemoryPoolStats* stats) { std::lock_guard lock(g_poolsMutex); - const auto pool = g_pools.find(lib_ctx_id); - if (pool == g_pools.end()) throw std::invalid_argument("sceNpWebApi2GetMemoryPoolStats: unknown context"); + const auto pool = g_pools.find(libraryContextId); + if (pool == g_pools.end()) return SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT; + if (!stats) return 0; *stats = {pool->second, 0, 0, 0}; return 0; } @@ -124,21 +134,29 @@ int APS5_VABI sceNpWebApi2PushEventCreateFilter(int lib_ctx_id, int handle_id, c return g_nextHandle.fetch_add(1, std::memory_order_relaxed); } -int APS5_VABI sceNpWebApi2PushEventCreateHandle(int lib_ctx_id) { - (void)lib_ctx_id; - return g_nextHandle.fetch_add(1, std::memory_order_relaxed); +int APS5_VABI sceNpWebApi2PushEventCreateHandle(int libraryContextId) { + std::lock_guard lock(g_poolsMutex); + if (!g_pools.contains(libraryContextId)) return SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT; + const int handleId = g_nextHandle.fetch_add(1, std::memory_order_relaxed); + pushHandles.emplace(handleId, PushHandle{libraryContextId}); + return handleId; } -int APS5_VABI sceNpWebApi2PushEventAbortHandle(int libCtxId, int handleId) { - (void)handleId; +int APS5_VABI sceNpWebApi2PushEventAbortHandle(int libraryContextId, int handleId) { std::lock_guard lock(g_poolsMutex); - if (!g_pools.contains(libCtxId)) throw std::invalid_argument("sceNpWebApi2PushEventAbortHandle: unknown context"); + if (!g_pools.contains(libraryContextId)) return SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT; + const auto handle = pushHandles.find(handleId); + if (handle == pushHandles.end() || handle->second.libraryContextId != libraryContextId) return SCE_NP_WEBAPI2_ERROR_INVALID_HANDLE; + handle->second.aborted = true; return 0; } -int APS5_VABI sceNpWebApi2PushEventDeleteHandle(int lib_ctx_id, int handle_id) { - (void)lib_ctx_id; - (void)handle_id; +int APS5_VABI sceNpWebApi2PushEventDeleteHandle(int libraryContextId, int handleId) { + std::lock_guard lock(g_poolsMutex); + if (!g_pools.contains(libraryContextId)) return SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT; + const auto handle = pushHandles.find(handleId); + if (handle == pushHandles.end() || handle->second.libraryContextId != libraryContextId) return SCE_NP_WEBAPI2_ERROR_INVALID_HANDLE; + pushHandles.erase(handle); return 0; } @@ -173,6 +191,7 @@ int APS5_VABI sceNpWebApi2SendRequest(int64_t request_id, const void* data, size int APS5_VABI sceNpWebApi2Terminate(int lib_ctx_id) { std::lock_guard lock(g_poolsMutex); + std::erase_if(pushHandles, [lib_ctx_id](const auto& item) { return item.second.libraryContextId == lib_ctx_id; }); g_pools.erase(lib_ctx_id); return 0; } @@ -185,11 +204,12 @@ int APS5_VABI sceNpWebApi2PushEventDeleteFilter(std::int32_t libraryContextId, s return 0; } -int APS5_VABI sceNpWebApi2PushEventSetHandleTimeout(int lib_ctx_id, int handle_id, uint32_t timeout) { - (void)handle_id; - (void)timeout; +int APS5_VABI sceNpWebApi2PushEventSetHandleTimeout(int libraryContextId, int handleId, std::uint32_t timeout) { std::lock_guard lock(g_poolsMutex); - if (!g_pools.contains(lib_ctx_id)) throw std::invalid_argument("sceNpWebApi2PushEventSetHandleTimeout: unknown context"); + if (!g_pools.contains(libraryContextId)) return SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT; + const auto handle = pushHandles.find(handleId); + if (handle == pushHandles.end() || handle->second.libraryContextId != libraryContextId) return SCE_NP_WEBAPI2_ERROR_INVALID_HANDLE; + handle->second.timeout = timeout; return 0; } diff --git a/core/libs/tests/GuestNpWebApi2.cpp b/core/libs/tests/GuestNpWebApi2.cpp index 8ae914ceda..eac1a6321f 100644 --- a/core/libs/tests/GuestNpWebApi2.cpp +++ b/core/libs/tests/GuestNpWebApi2.cpp @@ -20,6 +20,7 @@ int APS5_VABI sceNpWebApi2Terminate(int); int APS5_VABI sceNpWebApi2GetMemoryPoolStats(int, NpWebApi2MemoryPoolStats*); int APS5_VABI sceNpWebApi2PushEventCreateHandle(int); int APS5_VABI sceNpWebApi2PushEventAbortHandle(int, int); +int APS5_VABI sceNpWebApi2PushEventDeleteHandle(int, int); int APS5_VABI sceNpWebApi2PushEventSetHandleTimeout(int, int, std::uint32_t); int APS5_VABI sceNpWebApi2CreateUserContext(int, int); int APS5_VABI sceNpWebApi2CreateRequest(int, const char*, const char*, const char*, const void*, std::int64_t*); @@ -30,17 +31,9 @@ int APS5_VABI sceNpWebApi2DeleteRequest(std::int64_t); static void Require(bool value) { if (!value) std::abort(); } -template -static bool Throws(TCall call) { - try { - call(); - } catch (const std::exception&) { - return true; - } - return false; -} - int main() { + constexpr int invalidContext = static_cast(0x80553404); + constexpr int invalidHandle = static_cast(0x8055340D); const int context = sceNpWebApi2Initialize(1, 0x20000); const int other = sceNpWebApi2Initialize(1, 0x8000); Require(context > 0 && other > 0 && context != other); @@ -48,12 +41,24 @@ int main() { Require(sceNpWebApi2GetMemoryPoolStats(context, &stats) == 0); Require(stats.pool_size == 0x20000 && stats.max_inuse_size == 0 && stats.current_inuse_size == 0 && stats.reserved == 0); Require(sceNpWebApi2GetMemoryPoolStats(other, &stats) == 0 && stats.pool_size == 0x8000); - Require(Throws([&] { sceNpWebApi2GetMemoryPoolStats(context, nullptr); })); + Require(sceNpWebApi2GetMemoryPoolStats(context, nullptr) == 0); + Require(sceNpWebApi2GetMemoryPoolStats(-1, nullptr) == invalidContext); + Require(sceNpWebApi2PushEventCreateHandle(-1) == invalidContext); const int handle = sceNpWebApi2PushEventCreateHandle(context); Require(handle > 0); Require(sceNpWebApi2PushEventAbortHandle(context, handle) == 0); - Require(sceNpWebApi2PushEventAbortHandle(context, handle + 1000) == 0); + Require(sceNpWebApi2PushEventAbortHandle(context, handle + 1000) == invalidHandle); Require(sceNpWebApi2PushEventSetHandleTimeout(context, handle, 30000) == 0); + Require(sceNpWebApi2PushEventAbortHandle(other, handle) == invalidHandle); + Require(sceNpWebApi2PushEventDeleteHandle(other, handle) == invalidHandle); + Require(sceNpWebApi2PushEventSetHandleTimeout(other, handle, 0) == invalidHandle); + Require(sceNpWebApi2PushEventSetHandleTimeout(context, handle, 0) == 0); + Require(sceNpWebApi2PushEventSetHandleTimeout(context, handle, UINT32_MAX) == 0); + Require(sceNpWebApi2PushEventDeleteHandle(context, handle) == 0); + Require(sceNpWebApi2PushEventDeleteHandle(context, handle) == invalidHandle); + Require(sceNpWebApi2PushEventAbortHandle(context, handle) == invalidHandle); + Require(sceNpWebApi2PushEventSetHandleTimeout(context, handle, 1) == invalidHandle); + const int survivingHandle = sceNpWebApi2PushEventCreateHandle(other); const int user = sceNpWebApi2CreateUserContext(context, 1); std::int64_t request = 0; Require(user > 0 && sceNpWebApi2CreateRequest(user, "sessionManager", "/v1/sessions", "GET", nullptr, &request) == 0); @@ -62,9 +67,10 @@ int main() { Require(sceNpWebApi2AddWebTraceTag(request, nullptr) == static_cast(0x80553402)); Require(sceNpWebApi2DeleteRequest(request) == 0); Require(sceNpWebApi2Terminate(context) == 0); - Require(Throws([&] { sceNpWebApi2GetMemoryPoolStats(context, &stats); })); - Require(Throws([&] { sceNpWebApi2PushEventAbortHandle(context, handle); })); - Require(Throws([&] { sceNpWebApi2PushEventSetHandleTimeout(context, handle, 30000); })); + Require(sceNpWebApi2GetMemoryPoolStats(context, &stats) == invalidContext); + Require(sceNpWebApi2PushEventAbortHandle(context, handle) == invalidContext); + Require(sceNpWebApi2PushEventSetHandleTimeout(context, handle, 30000) == invalidContext); + Require(sceNpWebApi2PushEventAbortHandle(other, survivingHandle) == 0); Require(sceNpWebApi2Terminate(other) == 0); constexpr int invalidArgument = static_cast(0x80553402); diff --git a/docs/dev/debt/libSceNpWebApi2.md b/docs/dev/debt/libSceNpWebApi2.md index cc65efef68..7c73e05da4 100644 --- a/docs/dev/debt/libSceNpWebApi2.md +++ b/docs/dev/debt/libSceNpWebApi2.md @@ -11,6 +11,6 @@ - [sceNpWebApi2InitializeForPresence, sceNpWebApi2IntInitialize and sceNpWebApi2IntInitialize2](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - create a library context like `sceNpWebApi2Initialize`, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/8d50471b7dc141dd700b51276b8edd1f293bce26/src/core/libraries/np/np_web_api2/np_web_api2.cpp#L174-L222) does on the PS4: the argument structures (32 and 40 bytes) and the `struct_size` check that returns `SCE_NP_WEBAPI2_ERROR_INVALID_ARGUMENT` follow its [declarations](https://github.com/shadps4-emu/shadPS4/blob/8d50471b7dc141dd700b51276b8edd1f293bce26/src/core/libraries/np/np_web_api2/np_web_api2.h#L15-L31). The context type shadPS4 records (2 for the internal initializers, 3 for presence), the context name and the push configuration group are not kept, and the 0x8000 context limit is not applied. Not measured on a PS5 - [sceNpWebApi2CreateMultipartRequest and sceNpWebApi2SendMultipartRequest](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpWebApi2.c). The signatures follow the PS4 implementations in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_web_api2/np_web_api2.cpp#L52-L221) and [its multipart sender](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_web_api2/np_web_api2.cpp#L388-L399); opaque struct pointers use `const void*` or `void*` to preserve source constness. These prototypes are not measured on a console. - [sceNpWebApi2IntPushEventCreateDestPs4CtxIndFilter](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - no credible prototype was found; it is declared without parameters solely to resolve known imports and makes no ABI claim. -- [sceNpWebApi2PushEventAbortHandle](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2, NID `1OLgvahaSco`) - the two signed 32-bit handle arguments and return type follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/443f022450f6857256e780daa3d5a14029a748f7/src/core/libraries/np/np_web_api2/np_web_api2.cpp) (PS4); the PS5 signature and abort behavior are unverified. PPSA01736 imports it. An unknown library context throws, as in `sceNpWebApi2PushEventSetHandleTimeout`. The handle is not checked and nothing is aborted, as push event handles are not tracked and nothing waits on a handle offline -- [sceNpWebApi2GetMemoryPoolStats](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - imported by PPSA21703's Unity PSN plugin. The signature `(int libCtxId, stats*)` and the layout of the Http2 statistics above (measured on a PS5 for Http2, Ssl and Net) are assumed for this library, not measured. The pool size is the one given to the initializer that created the context, and the peak and current use are 0. The error codes are unknown, so a null output or an unknown context throws -- [sceNpWebApi2PushEventSetHandleTimeout](../../../core/libs/prx/libSceNpWebApi2/Export.cpp) (libSceNpWebApi2) - imported by PPSA01317, which fails to load without it. The signature `(int libCtxId, int handleId, uint32_t timeout)` follows `sceNpWebApiSetHandleTimeout` of the PS4 libSceNpWebApi in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_web_api.cpp) and is unverified on the PS5. An unknown library context throws, as in `sceNpWebApi2GetMemoryPoolStats`, since its error code is unknown. The handle is not checked, as push event handles are not tracked (`sceNpWebApi2PushEventDeleteHandle` accepts any handle), and the timeout is not kept, as nothing waits on a handle offline +- [Push handle lifecycle](../../../core/libs/prx/libSceNpWebApi2/Export.cpp): PS5 create/delete/abort signatures and invalid-context/invalid-handle errors are confirmed. Handles track ownership, timeout and abort state; deletion and context termination remove them. Network push operations and cancellation of active requests remain unimplemented. +- [sceNpWebApi2GetMemoryPoolStats](../../../core/libs/prx/libSceNpWebApi2/Export.cpp): PS5 confirms the context/statistics arguments and 32-byte output. An invalid context returns `0x80553404` without changing output; a valid context with null output succeeds. Allocator accounting remains absent: peak and current usage still report zero instead of native pool allocations. +- [sceNpWebApi2PushEventSetHandleTimeout](../../../core/libs/prx/libSceNpWebApi2/Export.cpp): PS5 confirms three 32-bit arguments, ownership validation and storing the entire timeout value without range checks. The stored timeout is not consumed because asynchronous push operations are unimplemented. From 2b4dc840993f157e3315e0229ff19d39c93f77f4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:27:49 +0300 Subject: [PATCH 357/523] docs(debt): condense remaining font and http2 gaps --- docs/dev/debt/libSceFont.md | 2 +- docs/dev/debt/libSceHttp2.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index f26cf6d061..e6bfcbb5bf 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -21,7 +21,7 @@ - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the other script files of a set are chosen from its type, not measured on a console: Vietnamese for low-byte bit 0x10, Thai for script bit 0x10, Japanese for 0x04, simplified Chinese for 0x80 (before Japanese with 0x08), Hangul for 0x20 and Arabic for low-byte bit 0x80, as the SDK set names (`..._JP_AR`, `..._GBCJK_TH`) listed by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/font/font_internal.cpp) suggest; the file order and the Hangul file name (`SCEPS4Yoongd-Medium.otf`, `SCEPS4Yoongd-Bold.otf`) also come from shadPS4. A character the primary file lacks is taken from the first of them that has it; kerning between two files is 0, and `sceFontGetCharGlyphCode` and `sceFontGetFontGlyphsCount` answer for the primary file only - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans Arabic, Noto Sans CJK with its Korean face for Hangul) is loaded instead; its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [sceFontGetCharGlyphMetrics, sceFontGetRenderCharGlyphMetrics](../../../core/libs/prx/libSceFont/src/Layout.cpp) (libSceFont) report the vertical metrics the way the [FreeType driver](../../../core/libs/prx/libSceFontFt/src/Driver.cpp) reports them for a rendered glyph, not confirmed on a title: FreeType's vertical advance, and for a face without vertical metrics a bearing of half the horizontal advance left of the horizontal bearing and of half the advance left over by the glyph height. -- [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - `sceFontWritingLineWritesOrder`, `sceFontWritingLineRefersRenderStep`, `sceFontWritingLineGetRenderMetrics`, `sceFontWritingLineGetOrderingSpace` and `sceFontWritingLineClear` follow the PS4 11.00 `libSceFont.sprx` (firmware module, read from its disassembly and compared call for call on random sequences): runs are linked in visual order by embedding level, the space at each spaced join is summed for the line, steps walk the visual order from the left (form `0x12`) or from the right (form `0x11`) and the render metrics grow as steps are consumed. The PS5 library was not measured. Not from that module: form `0x10` is accepted and treated as left to right and a detail with id `0x0FD5` is accepted (the PS4 library rejects both); the memory argument of `sceFontCreateWritingLine` and the rest of the detail are not read (the PS4 library validates its library object there); an unknown writing form, another detail id, a null line pointer on creation, unknown and null handles (except in `sceFontWritingLineRefersRenderStep`, which returns null for a null line) and other unchecked outputs throw. PS5 validation for writes, clear and render metrics is confirmed and implemented, including zeroing failed metrics outputs; `sceFontDestroyWritingLine` was not compared. Lines are allocated on the host, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. +- [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): ordering and spacing follow PS4 measurements; full PS5 algorithm equivalence remains unverified. PS5 validation for writes, clear and render metrics is implemented. Creation still accepts form `0x10` and detail `0x0FD5`, ignores font memory and allocates on the host, contrary to the supplied PS5 code. Destruction, step traversal and ordering-space errors still require comparison. Unsupported handles in those paths throw. - [Font graphics and metric signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Metric, outline-profile, style-information and expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged. diff --git a/docs/dev/debt/libSceHttp2.md b/docs/dev/debt/libSceHttp2.md index 74ba9af4d0..73186b8a90 100644 --- a/docs/dev/debt/libSceHttp2.md +++ b/docs/dev/debt/libSceHttp2.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libSceHttp2](../../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and its value is ignored, as for `sceHttp2SslEnableOption` and `sceHttp2SslDisableOption`. The three setters validate `id` against the live handle registry and return `SCE_HTTP2_ERROR_INVALID_ID` (`0x817B1100`) for a handle that was never allocated, was deleted, or belonged to a terminated context, following [prosper](https://github.com/mattias800/prosper). Whether the console restricts the object kind these setters accept, and what it does with an id from a previous init/term cycle, is unverified, so any live handle kind is accepted and ids are never reused. `sceHttp2Term`, `sceHttp2DeleteTemplate`, `sceHttp2DeleteRequest`, `sceHttp2CreateTemplate`, `sceHttp2CreateRequestWithURL` and `sceHttp2SendRequestAsync` also return the invalid-id error for a handle of the wrong kind or no longer live, following prosper (not measured on a console) +- [libSceHttp2](../../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and its value is ignored, as for `sceHttp2SslEnableOption` and `sceHttp2SslDisableOption`. The three setters validate `id` against the live handle registry and return `SCE_HTTP2_ERROR_INVALID_ID` (`0x817B1100`) for an invalid handle, following [prosper](https://github.com/mattias800/prosper). Whether the console restricts the object kind these setters accept, and what it does with an id from a previous init/term cycle, is unverified, so any live handle kind is accepted and ids are never reused. `sceHttp2Term`, `sceHttp2DeleteTemplate`, `sceHttp2DeleteRequest`, `sceHttp2CreateTemplate`, `sceHttp2CreateRequestWithURL` and `sceHttp2SendRequestAsync` also return the invalid-id error for a handle of the wrong kind or no longer live, following prosper (not measured on a console) ### Unknown function info From aca0f63647eec5f2dd34d51abe800b68ca594742 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:30:29 +0300 Subject: [PATCH 358/523] fix(font): allocate writing lines through caller memory callbacks --- core/libs/prx/libSceFont/src/WritingLine.cpp | 112 +++++++++++++------ core/libs/tests/GuestFontWritingLine.cpp | 74 +++++++++--- docs/dev/debt/libSceFont.md | 6 +- 3 files changed, 140 insertions(+), 52 deletions(-) diff --git a/core/libs/prx/libSceFont/src/WritingLine.cpp b/core/libs/prx/libSceFont/src/WritingLine.cpp index c0643b51d0..7365c1f418 100644 --- a/core/libs/prx/libSceFont/src/WritingLine.cpp +++ b/core/libs/prx/libSceFont/src/WritingLine.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -11,24 +11,19 @@ namespace { -constexpr std::int32_t WRITING_FORM_HORIZONTAL = 0x10; constexpr std::int32_t WRITING_FORM_HORIZONTAL_RTL = 0x11; constexpr std::int32_t WRITING_FORM_HORIZONTAL_LTR = 0x12; -constexpr std::uint16_t CREATE_WRITING_LINE_DETAIL_ID = 0x0FD5; constexpr std::uint64_t WRITING_ATTRIBUTE_ID_MASK = 0xFF; constexpr std::uint64_t WRITING_ATTRIBUTE_ID = 0xFA; constexpr std::uint64_t WRITING_ATTRIBUTE_SPACE_BEFORE = std::uint64_t{1} << 30; constexpr std::uint64_t WRITING_ATTRIBUTE_SPACE_AFTER = std::uint64_t{1} << 31; constexpr std::size_t WRITING_LEVEL_COUNT = 0x7F; -struct CreateWritingLineDetail { - std::uint16_t detailId; -}; - struct WritingOrder { void* orderer = nullptr; WritingOrder* prev = nullptr; WritingOrder* next = nullptr; + std::uint64_t reserved = 0; std::uint64_t attribute = 0; FontWritingMetrics metrics{}; }; @@ -39,22 +34,42 @@ struct WritingLevel { }; struct WritingLine { + std::uint16_t kind = 0xF0A; + std::uint16_t reserved = 0; std::int32_t form = 0; + const FontMemory* memory = nullptr; FontWritingMetrics renderMetrics{}; FontWritingLineStep step{}; float advanceX = 0.0f; float advanceY = 0.0f; float orderingSpace = 0.0f; float steppedSpace = 0.0f; - std::size_t lastLevel = 0; + std::uint32_t lastLevel = 0; + std::uint32_t reservedLevel = 0; + WritingOrder* first = nullptr; WritingOrder* last = nullptr; WritingOrder* head = nullptr; WritingOrder* tail = nullptr; WritingOrder* cursor = nullptr; + std::uint64_t reservedCursor = 0; std::array levels{}; - std::deque orders; }; +static_assert(sizeof(WritingOrder) == 64); +static_assert(offsetof(WritingOrder, attribute) == 32); +static_assert(offsetof(WritingOrder, metrics) == 40); +static_assert(sizeof(WritingLine) == 2208); +static_assert(offsetof(WritingLine, step) == 40); +static_assert(offsetof(WritingLine, advanceX) == 104); +static_assert(offsetof(WritingLine, last) == 136); +static_assert(offsetof(WritingLine, head) == 144); +static_assert(offsetof(WritingLine, cursor) == 160); +static_assert(offsetof(WritingLine, levels) == 176); + +bool IsMemoryValid(const FontMemory* memory) { + return memory && memory->mem_kind == 0xF00 && memory->iface && memory->iface->alloc && memory->iface->dealloc; +} + std::mutex linesMutex; std::unordered_set lines; @@ -62,13 +77,6 @@ std::unordered_set lines; throw std::runtime_error(std::string(function) + ": " + what); } -WritingLine* GetLine(const char* function, void* writingLine) { - auto* line = static_cast(writingLine); - std::lock_guard lock(linesMutex); - if (!lines.contains(line)) Unsupported(function, "unknown writing line handle (error code not verified)"); - return line; -} - bool IsRightToLeft(const WritingLine& line) { return line.form == WRITING_FORM_HORIZONTAL_RTL; } @@ -94,7 +102,13 @@ float TailSpace(const WritingLine& line, const WritingOrder& order) { } void ResetOrders(WritingLine& line) { - line.orders.clear(); + for (WritingOrder* order = line.head; order;) { + WritingOrder* next = order->next; + std::destroy_at(order); + line.memory->iface->dealloc(line.memory->mspace_handle, order); + order = next; + } + line.first = nullptr; line.levels.fill({}); line.advanceX = 0.0f; line.advanceY = 0.0f; @@ -217,30 +231,41 @@ void ExtendRenderMetrics(WritingLine& line) { extern "C" { int APS5_VABI sceFontCreateWritingLine(const FontMemory* memory, std::int32_t writingForm, const void* writingLineDetail, void** pWritingLine) { - (void)memory; - if (!pWritingLine) Unsupported(__func__, "null writing line pointer (error code not verified)"); - if (writingForm != WRITING_FORM_HORIZONTAL && writingForm != WRITING_FORM_HORIZONTAL_RTL && writingForm != WRITING_FORM_HORIZONTAL_LTR) - Unsupported(__func__, "writing form " + std::to_string(writingForm) + " is not modelled"); - if (writingLineDetail && static_cast(writingLineDetail)->detailId != CREATE_WRITING_LINE_DETAIL_ID) - Unsupported(__func__, "unknown writing line detail id"); - auto* line = new WritingLine{}; + if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER; + if (!IsMemoryValid(memory)) return SCE_FONT_ERROR_INVALID_MEMORY; + if (!pWritingLine || writingLineDetail || (writingForm != WRITING_FORM_HORIZONTAL_RTL && writingForm != WRITING_FORM_HORIZONTAL_LTR)) return SCE_FONT_ERROR_INVALID_PARAMETER; + void* allocation = memory->iface->alloc(memory->mspace_handle, sizeof(WritingLine)); + if (!allocation) return SCE_FONT_ERROR_ALLOCATION_FAILED; + auto* line = std::construct_at(static_cast(allocation)); + line->memory = memory; line->form = writingForm; { std::lock_guard lock(linesMutex); - lines.insert(line); + try { + lines.insert(line); + } catch (...) { + std::destroy_at(line); + memory->iface->dealloc(memory->mspace_handle, allocation); + throw; + } } *pWritingLine = line; return SCE_FONT_OK; } int APS5_VABI sceFontDestroyWritingLine(void** pWritingLine) { - if (!pWritingLine || !*pWritingLine) return SCE_FONT_OK; - auto* line = GetLine(__func__, *pWritingLine); + if (!pWritingLine || !*pWritingLine) return SCE_FONT_ERROR_INVALID_PARAMETER; + auto* line = static_cast(*pWritingLine); { std::lock_guard lock(linesMutex); + if (!lines.contains(line) || !IsMemoryValid(line->memory)) return SCE_FONT_ERROR_INVALID_WRITING; lines.erase(line); } - delete line; + const FontMemory* memory = line->memory; + ResetOrders(*line); + line->kind = 0; + std::destroy_at(line); + memory->iface->dealloc(memory->mspace_handle, line); *pWritingLine = nullptr; return SCE_FONT_OK; } @@ -252,6 +277,7 @@ int APS5_VABI sceFontWritingLineClear(void* writingLine) { std::lock_guard lock(linesMutex); if (!lines.contains(line)) return SCE_FONT_ERROR_INVALID_WRITING; } + if (!IsMemoryValid(line->memory)) return SCE_FONT_ERROR_INVALID_WRITING; ResetOrders(*line); return SCE_FONT_OK; } @@ -264,8 +290,12 @@ int APS5_VABI sceFontWritingLineWritesOrder(void* writingLine, std::uint64_t wri std::lock_guard lock(linesMutex); if (!lines.contains(line)) return SCE_FONT_ERROR_INVALID_WRITING; } + if (!IsMemoryValid(line->memory)) return SCE_FONT_ERROR_INVALID_PARAMETER; if (line->cursor) ResetOrders(*line); - WritingOrder* order = &line->orders.emplace_back(); + void* allocation = line->memory->iface->alloc(line->memory->mspace_handle, sizeof(WritingOrder)); + if (!allocation) return SCE_FONT_ERROR_ALLOCATION_FAILED; + WritingOrder* order = std::construct_at(static_cast(allocation)); + if (!line->last) line->first = order; order->orderer = writingOrderer; order->attribute = writingAttribute; if (writingMetrics) order->metrics = *writingMetrics; @@ -276,13 +306,17 @@ int APS5_VABI sceFontWritingLineWritesOrder(void* writingLine, std::uint64_t wri line->advanceX += order->metrics.advanceX; line->advanceY += order->metrics.advanceY; line->last = order; - line->lastLevel = level; + line->lastLevel = static_cast(level); return SCE_FONT_OK; } const FontWritingLineStep* APS5_VABI sceFontWritingLineRefersRenderStep(void* writingLine) { if (!writingLine) return nullptr; - auto* line = GetLine(__func__, writingLine); + auto* line = static_cast(writingLine); + { + std::lock_guard lock(linesMutex); + if (!lines.contains(line)) return nullptr; + } if (line->cursor) { ExtendRenderMetrics(*line); } else { @@ -335,7 +369,21 @@ int APS5_VABI sceFontWritingLineGetRenderMetrics(void* writingLine, FontWritingM } int APS5_VABI sceFontWritingLineGetOrderingSpace(void* writingLine, float* advanceSpace, float* headSpace, float* inlineSpace, float* tailSpace) { - auto* line = GetLine(__func__, writingLine); + auto* line = static_cast(writingLine); + int error = SCE_FONT_OK; + { + std::lock_guard lock(linesMutex); + if (!line) error = SCE_FONT_ERROR_INVALID_PARAMETER; + else if (!lines.contains(line)) error = SCE_FONT_ERROR_INVALID_WRITING; + else if (line->form != WRITING_FORM_HORIZONTAL_RTL && line->form != WRITING_FORM_HORIZONTAL_LTR) error = SCE_FONT_ERROR_INVALID_PARAMETER; + } + if (error != SCE_FONT_OK) { + if (advanceSpace) *advanceSpace = 0.0f; + if (headSpace) *headSpace = 0.0f; + if (inlineSpace) *inlineSpace = 0.0f; + if (tailSpace) *tailSpace = 0.0f; + return error; + } if (headSpace && !tailSpace) Unsupported(__func__, "a head space pointer without a tail space pointer is not modelled"); if (advanceSpace) *advanceSpace = line->advanceX; if (headSpace) { diff --git a/core/libs/tests/GuestFontWritingLine.cpp b/core/libs/tests/GuestFontWritingLine.cpp index 019d9aaffe..5c311aede3 100644 --- a/core/libs/tests/GuestFontWritingLine.cpp +++ b/core/libs/tests/GuestFontWritingLine.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { int APS5_VABI sceFontCreateWritingLine(const FontMemory*, std::int32_t, const void*, void**); @@ -32,12 +33,28 @@ static bool Throws(F f) { return false; } -struct WritingLineDetail { - std::uint16_t detailId; - std::uint16_t reserved[3]; - void* reservedPointers[3]; +struct AllocationState { + std::size_t live = 0; + std::uint32_t lastSize = 0; + bool fail = false; }; +static void* APS5_VABI Allocate(void* object, std::uint32_t size) { + auto& state = *static_cast(object); + state.lastSize = size; + if (state.fail) return nullptr; + void* allocation = std::malloc(size); + if (allocation) ++state.live; + return allocation; +} + +static void APS5_VABI Deallocate(void* object, void* allocation) { + auto& state = *static_cast(object); + Require(allocation && state.live > 0); + --state.live; + std::free(allocation); +} + constexpr std::uint64_t SPACE_BEFORE = std::uint64_t{1} << 30; constexpr std::uint64_t SPACE_AFTER = std::uint64_t{1} << 31; @@ -80,15 +97,27 @@ static bool OrderIs(void* line, const std::uint64_t* levels, const std::size_t* } int main() { + AllocationState allocations; + FontMemoryInterface memoryInterface{}; + memoryInterface.alloc = Allocate; + memoryInterface.dealloc = Deallocate; FontMemory memory{}; void* line = nullptr; - Require(Throws([&] { sceFontCreateWritingLine(&memory, 0x10, nullptr, nullptr); })); - Require(Throws([&] { sceFontCreateWritingLine(&memory, 0x13, nullptr, &line); }) && line == nullptr); - Require(Throws([&] { sceFontCreateWritingLine(&memory, 0, nullptr, &line); }) && line == nullptr); - WritingLineDetail detail{0x0FD4, {}, {}}; - Require(Throws([&] { sceFontCreateWritingLine(&memory, 0x10, &detail, &line); }) && line == nullptr); - detail.detailId = 0x0FD5; - Require(sceFontCreateWritingLine(&memory, 0x10, &detail, &line) == SCE_FONT_OK && line != nullptr); + Require(sceFontCreateWritingLine(nullptr, 0x12, nullptr, &line) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontCreateWritingLine(&memory, 0x12, nullptr, &line) == SCE_FONT_ERROR_INVALID_MEMORY); + memory.mem_kind = 0xF00; + memory.mspace_handle = &allocations; + Require(sceFontCreateWritingLine(&memory, 0x12, nullptr, &line) == SCE_FONT_ERROR_INVALID_MEMORY); + memory.iface = &memoryInterface; + Require(sceFontCreateWritingLine(&memory, 0x12, nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + for (int form : {0, 0x10, 0x13}) Require(sceFontCreateWritingLine(&memory, form, nullptr, &line) == SCE_FONT_ERROR_INVALID_PARAMETER && !line); + std::uint16_t detail = 0x0FD5; + Require(sceFontCreateWritingLine(&memory, 0x12, &detail, &line) == SCE_FONT_ERROR_INVALID_PARAMETER && !line); + allocations.fail = true; + Require(sceFontCreateWritingLine(&memory, 0x12, nullptr, &line) == SCE_FONT_ERROR_ALLOCATION_FAILED && !line); + allocations.fail = false; + Require(sceFontCreateWritingLine(&memory, 0x12, nullptr, &line) == SCE_FONT_OK && line); + Require(allocations.lastSize == 2208 && allocations.live == 1); void* ltr = nullptr; Require(sceFontCreateWritingLine(&memory, 0x12, nullptr, <r) == SCE_FONT_OK && ltr != nullptr && ltr != line); void* rtl = nullptr; @@ -116,7 +145,11 @@ int main() { Require(sceFontWritingLineRefersRenderStep(line) == nullptr); Require(sceFontWritingLineWritesOrder(line, 0, &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); + allocations.fail = true; + Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runA, &a) == SCE_FONT_ERROR_ALLOCATION_FAILED); + allocations.fail = false; Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runA, &a) == SCE_FONT_OK); + Require(allocations.lastSize == 64); Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runB, &b) == SCE_FONT_OK); Require(SpaceIs(line, 16.0f, 1.0f, 0.0f, 1.0f)); Require(sceFontWritingLineGetRenderMetrics(line, &metrics) == SCE_FONT_OK && Same(metrics, {})); @@ -222,7 +255,7 @@ int main() { float space = 7.0f; Require(sceFontWritingLineGetOrderingSpace(rtl, nullptr, nullptr, &space, nullptr) == SCE_FONT_OK && space == 15.0f); Require(Throws([&] { sceFontWritingLineGetOrderingSpace(rtl, &space, &space, &space, nullptr); })); - Require(Throws([&] { sceFontWritingLineGetOrderingSpace(nullptr, &space, nullptr, nullptr, nullptr); })); + Require(sceFontWritingLineGetOrderingSpace(nullptr, &space, nullptr, nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER && space == 0.0f); Require(sceFontWritingLineGetRenderMetrics(nullptr, &metrics) == SCE_FONT_ERROR_INVALID_PARAMETER && Same(metrics, {})); Require(sceFontWritingLineGetRenderMetrics(rtl, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); Require(sceFontWritingLineClear(nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); @@ -230,22 +263,29 @@ int main() { int notALine = 0; Require(sceFontWritingLineRefersRenderStep(nullptr) == nullptr); Require(sceFontWritingLineClear(¬ALine) == SCE_FONT_ERROR_INVALID_WRITING); - Require(Throws([&] { sceFontWritingLineRefersRenderStep(¬ALine); })); - Require(Throws([&] { sceFontWritingLineGetOrderingSpace(¬ALine, &space, &space, &space, &space); })); + Require(sceFontWritingLineRefersRenderStep(¬ALine) == nullptr); + space = 7.0f; + Require(sceFontWritingLineGetOrderingSpace(¬ALine, &space, &space, &space, &space) == SCE_FONT_ERROR_INVALID_WRITING && space == 0.0f); metrics = runA; Require(sceFontWritingLineGetRenderMetrics(¬ALine, &metrics) == SCE_FONT_ERROR_INVALID_WRITING && Same(metrics, {})); Require(sceFontWritingLineWritesOrder(¬ALine, 0, nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); Require(sceFontWritingLineWritesOrder(¬ALine, Attribute(0), nullptr, &a) == SCE_FONT_ERROR_INVALID_WRITING); void* bogus = ¬ALine; - Require(Throws([&] { sceFontDestroyWritingLine(&bogus); }) && bogus == ¬ALine); + Require(sceFontDestroyWritingLine(&bogus) == SCE_FONT_ERROR_INVALID_WRITING && bogus == ¬ALine); + memory.mem_kind = 0; + Require(sceFontWritingLineClear(line) == SCE_FONT_ERROR_INVALID_WRITING); + Require(sceFontWritingLineWritesOrder(line, Attribute(0), &runA, &a) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontDestroyWritingLine(&line) == SCE_FONT_ERROR_INVALID_WRITING && line); + memory.mem_kind = 0xF00; void* destroyed = line; Require(sceFontDestroyWritingLine(&line) == SCE_FONT_OK && line == nullptr); - Require(sceFontDestroyWritingLine(&line) == SCE_FONT_OK && line == nullptr); - Require(sceFontDestroyWritingLine(nullptr) == SCE_FONT_OK); + Require(sceFontDestroyWritingLine(&line) == SCE_FONT_ERROR_INVALID_PARAMETER && line == nullptr); + Require(sceFontDestroyWritingLine(nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); Require(sceFontWritingLineClear(destroyed) == SCE_FONT_ERROR_INVALID_WRITING); Require(sceFontWritingLineWritesOrder(destroyed, Attribute(0), &runA, &a) == SCE_FONT_ERROR_INVALID_WRITING); Require(sceFontDestroyWritingLine(<r) == SCE_FONT_OK && ltr == nullptr); Require(sceFontDestroyWritingLine(&rtl) == SCE_FONT_OK && rtl == nullptr); + Require(allocations.live == 0); std::puts("font writing line: ok"); } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index e6bfcbb5bf..585003d2a6 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -8,9 +8,9 @@ - [sceFontDettachDeviceCacheBuffer](../../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. It detaches the cache buffer attached by `sceFontAttachDeviceCacheBuffer`, returns the buffer and its size, and reports `SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER` when no cache buffer is attached - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` - [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title; the outputs are inferred from the names. The resolution is the face's units per EM and the scale pixel the driver's face scale (units per EM / 64 for FreeType), the pixel scale a font has when opened. Either output may be null, as in `sceFontGetResolutionDpi` -- [sceFontCreateWritingLine, sceFontWritingLineWritesOrder](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): supplied PS5 code confirms the four-argument signatures, low attribute byte `0xFA`, embedding levels 0 through 126 and spacing bits 30/31. Invalid attributes, including zero, null orderers and null lines return `0x80460002`; invalid non-null line handles return `0x8046000A`. Metrics are optional. Native creation accepts only forms `0x11`/`0x12` and a null detail, validates font memory and allocates 2208 bytes through its callback; those creation and allocation rules remain unimplemented. +- [Writing-line ABI and allocation](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): PS5 layouts, creation forms, memory validation, allocator callbacks and error handling are recovered. The 2208-byte line and 64-byte orders use caller memory. Host registry bookkeeping still allocates separately; memory lifetime and concurrent destruction require caller synchronization. - [sceFontWritingRefersRenderStepCharacter](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the second argument is an out pointer to the writing's letter step, as in [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1882-L1896) and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings. Measured on PS4 firmware 11.00 `libSceFont.sprx` (not on a PS5): exactly 8 bytes are written, the address of a step stored inside the writing, and a null writing writes null; shadPS4 and prosper treat the argument as a struct, which the measurement contradicts. The letter step lives in the writing, so the pointer stays valid as long as the writing does, and its contents are rebuilt by each call of this function. On the PS4 library repeated calls on one step walk the characters it covers and then return null; here every call returns the step's one character, so the second call differs. Assumed: each step covers one character and one glyph. PPSA06699 (Hot Shots Golf 2) dereferences the returned pointer for its character counts. -- [sceFontWritingLineGetOrderingSpace](../../../core/libs/prx/libSceFont/src/WritingLine.cpp) (libSceFont) - the four outputs are written in the order the PS4 11.00 `libSceFont.sprx` writes them (line advance, leading space, space between spaced runs, trailing space); the parameter names in the [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) IDA type database and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings order them differently (head, inline, tail, advance), and the PS5 library was not measured. PPSA01325's only call uses them in the PS4 order: it computes first - second - fourth - 0.25 * third and compares that, divided by a scale, with its width limit. The PS4 library writes the trailing space through its pointer without a null check whenever the leading-space pointer is set; that call throws here +- [sceFontWritingLineGetOrderingSpace](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): PS5 confirms output order, calculations and zeroed outputs on invalid handles. A non-null head output with a null tail output causes an unchecked native write; the host throws instead. With no head output, the tail output remains untouched. - [sceFontCharacterGetSyllableStringState](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the signature `(textCharacter, int* syllableStringState)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs). Measured on PS4 firmware 11.00 `libSceFont.sprx` (not on a PS5): the state is 0 for Latin, CJK, Hangul, Arabic, Hebrew, Devanagari and the other scripts tried, and the library sets 1, 3 or 4 (never 2 on real text) for Thai while it creates the string; a null character or a null result returns `SCE_FONT_ERROR_INVALID_PARAMETER`, and a null character writes 0 to a non-null result. This returns 0 for every character outside the Thai block (U+0E00 to U+0E7F), as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1805-L1815) does, and throws for Thai characters, whose cluster states are not modelled. SharpProspero's `SceFontSyllableString` names 0 NonDetection, 1 Starting, 2 Following, 3 Depending and 4 Connecting. PPSA06699 (Hot Shots Golf 2) calls it. - [sceFontCharactersRefersTextCodes, sceFontTextCodesStepNext and sceFontTextCodesStepBack](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the signatures follow the [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json#L679) IDA type database and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Font/SceFont.Engine.cs#L395-L406), and the 64-byte `FontTextCodes` with the order pointer at 0 and the code at 8 follows SharpProspero's [SceFontTextCodes](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Font/SceFontStructs.cs#L216-L228). The traversal follows the PS4 11.00 `libSceFont.sprx` disassembly posted in [#4050](https://github.com/boykopovar/AnyPS5/pull/4050#issuecomment-6114504202), not a PS5 measurement: the cursor keeps the current, start and term characters at +0x28, +0x30 and +0x38; `Refers` never compares the character with the term and returns null for a clustered or synthetic character; `StepNext` returns null at the term after visiting it; `StepBack` returns null when the current character is the start; both steps skip synthetic characters. Assumed and not covered by the report, which says `StepNext` returns null only at the term: `StepNext` also returns null at the end of the chain (for example with a null term), and `StepBack` at the head of the chain. A null cursor or output returns null as in [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1696-L1740); a null text character throws, as its console result is unknown @@ -21,7 +21,7 @@ - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the other script files of a set are chosen from its type, not measured on a console: Vietnamese for low-byte bit 0x10, Thai for script bit 0x10, Japanese for 0x04, simplified Chinese for 0x80 (before Japanese with 0x08), Hangul for 0x20 and Arabic for low-byte bit 0x80, as the SDK set names (`..._JP_AR`, `..._GBCJK_TH`) listed by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/font/font_internal.cpp) suggest; the file order and the Hangul file name (`SCEPS4Yoongd-Medium.otf`, `SCEPS4Yoongd-Bold.otf`) also come from shadPS4. A character the primary file lacks is taken from the first of them that has it; kerning between two files is 0, and `sceFontGetCharGlyphCode` and `sceFontGetFontGlyphsCount` answer for the primary file only - [sceFontOpenFontSet](../../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) - the console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans Arabic, Noto Sans CJK with its Korean face for Hangul) is loaded instead; its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for. - [sceFontGetCharGlyphMetrics, sceFontGetRenderCharGlyphMetrics](../../../core/libs/prx/libSceFont/src/Layout.cpp) (libSceFont) report the vertical metrics the way the [FreeType driver](../../../core/libs/prx/libSceFontFt/src/Driver.cpp) reports them for a rendered glyph, not confirmed on a title: FreeType's vertical advance, and for a face without vertical metrics a bearing of half the horizontal advance left of the horizontal bearing and of half the advance left over by the glyph height. -- [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): ordering and spacing follow PS4 measurements; full PS5 algorithm equivalence remains unverified. PS5 validation for writes, clear and render metrics is implemented. Creation still accepts form `0x10` and detail `0x0FD5`, ignores font memory and allocates on the host, contrary to the supplied PS5 code. Destruction, step traversal and ordering-space errors still require comparison. Unsupported handles in those paths throw. +- [Font writing lines](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): visual ordering and spacing originated from PS4 measurements and match the supplied PS5 structure and control flow. Exhaustive floating-point equivalence, including NaN payloads and rounding, has not been verified against a running PS5 module. - [Font graphics and metric signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp): graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Metric, outline-profile, style-information and expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged. From 7b97d7552c264cddacf733ef8be3dbd91e00d475 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:34:58 +0300 Subject: [PATCH 359/523] fix(hmd): restore device queries and reprojection setup signatures --- core/libs/prx/libSceHmd/Export.cpp | 51 ++++++++++++++++++++++-------- docs/dev/debt/libSceHmd.md | 21 ++++-------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 693276b1a2..fdb52dd1fe 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -3,6 +3,24 @@ #include +struct HmdReprojectionInitializeParameter; +struct HmdReprojectionMultilayerParameter; +struct HmdDeviceInformation; +struct HmdDistortionParameters; +struct HmdInertialSensorData; +struct HmdStatusReport; + +struct HmdFieldOfView { + float angles[4]; +}; + +struct HmdAssemblyError { + float offsets[4]; +}; + +static_assert(sizeof(HmdFieldOfView) == 16); +static_assert(sizeof(HmdAssemblyError) == 16); + struct HmdVr2dData; struct HmdPuPositionParameter; struct HmdOpticalParameter; @@ -48,6 +66,13 @@ struct HmdMirroringAspect { std::uint16_t reserved2; }; +struct HmdMirroringAspectDebug { + std::uint16_t values[5]; + std::uint8_t mode; +}; + +static_assert(sizeof(HmdMirroringAspectDebug) == 12); +static_assert(offsetof(HmdMirroringAspectDebug, mode) == 10); static_assert(sizeof(HmdMirroringAspect) == 8); static_assert(offsetof(HmdMirroringAspect, aspect) == 2); @@ -219,7 +244,7 @@ int APS5_VABI sceHmdInternalMirroringModeSetAspect(const HmdMirroringAspect* asp return 0; } -int APS5_VABI sceHmdInternalDfuGetStatus(void) { +int APS5_VABI sceHmdInternalDfuGetStatus(std::uint64_t* status) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -339,7 +364,7 @@ int APS5_VABI sceHmdGetDistortionWorkMemorySize(void) { return 0x20000; } -int APS5_VABI sceHmdReprojectionSetDisplayBuffers(void) { +int APS5_VABI sceHmdReprojectionSetDisplayBuffers(std::int32_t videoOutHandle, std::int32_t firstBufferIndex, std::int32_t secondBufferIndex, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -354,7 +379,7 @@ int APS5_VABI sceHmdInternalCreateSharedMemory(void) { return 0; } -int APS5_VABI sceHmdInternalMirroringModeSetAspectDebug(void) { +int APS5_VABI sceHmdInternalMirroringModeSetAspectDebug(const HmdMirroringAspectDebug* aspect) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -432,13 +457,13 @@ int APS5_VABI sceHmdInternalSetM2LedOn(std::int32_t handle, std::uint32_t ledMas return 0; } -int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView) { +int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, HmdFieldOfView* fieldOfView) { (void)handle; if (!fieldOfView) return Hmd::ParameterNull; return initialized.load() ? Hmd::InvalidHandle : Hmd::NotInitialized; } -int APS5_VABI sceHmdReprojectionAddDisplayBuffer(void) { +int APS5_VABI sceHmdReprojectionAddDisplayBuffer(std::int32_t bufferIndex, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -458,12 +483,12 @@ int APS5_VABI sceHmdInternal3dAudioSendData(std::int32_t port, const void* data, return 0; } -int APS5_VABI sceHmdReprojectionInitialize(void) { +int APS5_VABI sceHmdReprojectionInitialize(const HmdReprojectionInitializeParameter* parameter, std::uint32_t mode, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmdInternalGetDeviceInformationByHandle(void) { +int APS5_VABI sceHmdInternalGetDeviceInformationByHandle(std::int32_t handle, HmdDeviceInformation* information) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -483,7 +508,7 @@ int APS5_VABI sceHmdInternalCrashReportReadData(void* buffer, std::uint32_t* byt return 0; } -int APS5_VABI sceHmdInternalGetDeviceInformation(void) { +int APS5_VABI sceHmdInternalGetDeviceInformation(HmdDeviceInformation* information) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -552,7 +577,7 @@ bool APS5_VABI sceHmdInternalIsGameVr2d(void) { return 0; } -int APS5_VABI sceHmdGetAssyError(void* data) { +int APS5_VABI sceHmdGetAssyError(HmdAssemblyError* data) { if (!data) return Hmd::ParameterNull; return initialized.load() ? Hmd::DeviceDisconnected : Hmd::NotInitialized; } @@ -692,7 +717,7 @@ int APS5_VABI sceHmdGetWideNearDistortionCorrectionCommand(void) { return 0; } -int APS5_VABI sceHmdReprojectionStartMultilayer2(void) { +int APS5_VABI sceHmdReprojectionStartMultilayer2(const HmdReprojectionMultilayerParameter* parameter, std::int32_t mode) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -761,7 +786,7 @@ int APS5_VABI sceHmdReprojectionStartWideNearWithOverlay(void) { return 0; } -int APS5_VABI sceHmdGetDistortionParams(void) { +int APS5_VABI sceHmdGetDistortionParams(HmdDistortionParameters* parameters) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -821,7 +846,7 @@ int APS5_VABI sceHmdInternalSetDefaultLedData(void) { return 0; } -int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count) { +int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, HmdInertialSensorData* data, std::uint32_t count) { (void)handle; (void)data; (void)count; @@ -848,7 +873,7 @@ int APS5_VABI sceHmdInternalSeparateModeSendAudio(const HmdScreenData* data) { return 0; } -int APS5_VABI sceHmdInternalGetStatusReport(void) { +int APS5_VABI sceHmdInternalGetStatusReport(std::int32_t handle, HmdStatusReport* report) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 2c4ba6528c..ab19b62b9b 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -10,33 +10,24 @@ - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count - [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalDfuGetStatus](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetDistortionWorkMemoryAlign](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetDistortionWorkMemorySize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionSetDisplayBuffers](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalMirroringModeSetAspectDebug](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionAddDisplayBuffer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDeviceInformationByHandle](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDeviceInformation](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStartCapture](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetWideNearCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalCrashReportCancel](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSeparateModeOpen](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStartMultilayer2](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetDistortionParams](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetStatusReport](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature + +[Device, field-of-view, assembly-error, distortion, sensor and DFU query signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered from native register use. Field of view and assembly errors each output four floats; device information occupies 64 bytes and distortion output spans 76 bytes. The remaining device/sensor/report field semantics and DFU status bit meanings are unresolved. Assembly confirms integer error returns where decompilation incorrectly inferred void. Protected bodies are unchanged. + +[Reprojection initialization and display-buffer signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered: initialization takes a parameter pointer, 32-bit mode and null reserved pointer; display-buffer setup takes a VideoOut handle, two buffer indices and reserved pointer, while adding a buffer takes an index and reserved pointer. `StartMultilayer2` takes a parameter pointer and 32-bit mode. Parameter fields remain incomplete. Protected bodies are unchanged. + +[Work-memory queries, capture start, separate-mode open and crash-report cancellation](../../../core/libs/prx/libSceHmd/Export.cpp) consume no arguments in the supplied native implementation. The mirroring aspect debug setter takes five 16-bit values followed by a byte mode; the individual scalar meanings remain unresolved. These argument lists are confirmed; protected bodies remain unimplemented. From 166a8aa3e5f6c5b077550ad087c05926801851bc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:36:17 +0300 Subject: [PATCH 360/523] fix(npentitlementaccess): reject unimplemented request lifecycle operations --- .../prx/libSceNpEntitlementAccess/Export.cpp | 6 +++--- core/libs/tests/GuestOfflineImports.cpp | 18 ++++++++++++++++++ docs/dev/debt/libSceNpEntitlementAccess.md | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceNpEntitlementAccess/Export.cpp b/core/libs/prx/libSceNpEntitlementAccess/Export.cpp index ecec40a1a2..717d50a1ed 100644 --- a/core/libs/prx/libSceNpEntitlementAccess/Export.cpp +++ b/core/libs/prx/libSceNpEntitlementAccess/Export.cpp @@ -119,11 +119,11 @@ int APS5_VABI sceNpEntitlementAccessInitialize(const NpEntitlementAccessInitPara } int APS5_VABI sceNpEntitlementAccessAbortRequest(std::int64_t requestId) { - return 0; + throw std::logic_error("sceNpEntitlementAccessAbortRequest: asynchronous request tracking is unimplemented"); } int APS5_VABI sceNpEntitlementAccessDeleteRequest(std::int64_t requestId) { - return 0; + throw std::logic_error("sceNpEntitlementAccessDeleteRequest: asynchronous request tracking is unimplemented"); } int APS5_VABI sceNpEntitlementAccessGenerateTransactionId(NpEntitlementAccessTransactionId* transactionId) { @@ -131,7 +131,7 @@ int APS5_VABI sceNpEntitlementAccessGenerateTransactionId(NpEntitlementAccessTra return SCE_NP_ERROR_SIGNED_OUT; } -int APS5_VABI sceNpEntitlementAccessPollConsumeEntitlement(std::int64_t requestId, std::int32_t* result, std::int32_t* remainingCount) { +int APS5_VABI sceNpEntitlementAccessPollConsumeEntitlement(std::int64_t requestId, std::int32_t* result, std::int32_t* countResult) { return SCE_NP_ERROR_SIGNED_OUT; } diff --git a/core/libs/tests/GuestOfflineImports.cpp b/core/libs/tests/GuestOfflineImports.cpp index 56dadb62ba..f89a850b63 100644 --- a/core/libs/tests/GuestOfflineImports.cpp +++ b/core/libs/tests/GuestOfflineImports.cpp @@ -9,6 +9,8 @@ #include extern "C" { +int APS5_VABI sceNpEntitlementAccessAbortRequest(std::int64_t); +int APS5_VABI sceNpEntitlementAccessDeleteRequest(std::int64_t); int APS5_VABI sceHttpSetCookieEnabled(int, int); int APS5_VABI sceHttpSendRequest(int, const void*, std::size_t); int APS5_VABI sceNpEntitlementAccessGetEntitlementKey( @@ -32,6 +34,22 @@ int APS5_VABI sceRudpEnd(); static void Require(bool value) { if (!value) std::abort(); } int main() { + for (std::int64_t requestId : {std::int64_t{0}, std::int64_t{1}, INT64_MAX}) { + bool abortFailed = false; + bool deleteFailed = false; + try { + sceNpEntitlementAccessAbortRequest(requestId); + } catch (const std::logic_error&) { + abortFailed = true; + } + try { + sceNpEntitlementAccessDeleteRequest(requestId); + } catch (const std::logic_error&) { + deleteFailed = true; + } + Require(abortFailed && deleteFailed); + } + constexpr int invalidValue = static_cast(0x804311FE); constexpr int network = static_cast(0x80431063); Require(sceHttpSetCookieEnabled(1, 0) == 0); diff --git a/docs/dev/debt/libSceNpEntitlementAccess.md b/docs/dev/debt/libSceNpEntitlementAccess.md index d6f56513d4..7d54ea4fe1 100644 --- a/docs/dev/debt/libSceNpEntitlementAccess.md +++ b/docs/dev/debt/libSceNpEntitlementAccess.md @@ -4,7 +4,7 @@ - [sceNpEntitlementAccessGetAddcontEntitlementInfoIndividual, sceNpEntitlementAccessPollConsumableEntitlementInfo, sceNpEntitlementAccessRequestConsumableEntitlementInfo, sceNpEntitlementAccessRequestConsumeEntitlement](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp) (libSceNpEntitlementAccess) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpEntitlementAccess.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim - [sceNpEntitlementAccessGetEntitlementKey](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp) - the three-argument signature, 16-byte key and errors `0x817D0002`/`0x817D0007` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libNet.cpp); PPSA01521 passes a service label, entitlement label and output pointer. Only the no-key path is implemented: local entitlement labels carry no keys, and the output is untouched on failure. Console error precedence and key provisioning are unverified -- [Entitlement requests and polls](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp): native argument lists are restored for single-item, list and consume operations, plus abort/delete. Request IDs are 64-bit, labels occupy 20 bytes and transaction IDs occupy 72 bytes. List options occupy 32 bytes and results have a 64-byte stride; their field semantics and three list-result scalars remain unresolved. The existing signed-out responses do not reproduce native initialization, validation or asynchronous request handling; abort/delete still report success without tracking requests. +- [Entitlement requests and polls](../../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp): native argument lists are restored for single-item, list and consume operations, plus abort/delete. Request IDs are 64-bit, labels occupy 20 bytes and transaction IDs occupy 72 bytes. List options occupy 32 bytes and results have a 64-byte stride; their field semantics and three list-result scalars remain unresolved. The existing signed-out responses do not reproduce native initialization, validation or asynchronous request handling; abort/delete throw until request tracking is implemented. ### Functional From eede6587233d91db03e764e02d192a759bfbb3c9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:39:33 +0300 Subject: [PATCH 361/523] fix(hmd): restore reprojection submission argument slots --- core/libs/prx/libSceHmd/Export.cpp | 20 +++++++++++++------- docs/dev/debt/libSceHmd.md | 10 +++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index fdb52dd1fe..ab37d80f72 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -3,6 +3,12 @@ #include +struct HmdReprojectionFrameParameter; +struct HmdReprojectionWideNearParameter; +struct HmdReprojection2dVrParameter; +struct HmdReprojectionPose; +struct HmdReprojectionOverlayParameter; +struct HmdReprojectionSubmissionParameter; struct HmdReprojectionInitializeParameter; struct HmdReprojectionMultilayerParameter; struct HmdDeviceInformation; @@ -214,7 +220,7 @@ int APS5_VABI sceHmdDistortionGetWorkMemorySize(void) { return 0x20000; } -int APS5_VABI sceHmdReprojectionStartWideNear(void) { +int APS5_VABI sceHmdReprojectionStartWideNear(const HmdReprojectionWideNearParameter* parameter, const HmdReprojectionPose* pose, std::uint64_t flipArg, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -293,7 +299,7 @@ int APS5_VABI sceHmdGetDistortionWorkMemoryAlign(void) { return 0x400; } -int APS5_VABI sceHmdReprojectionStartMultilayer(void) { +int APS5_VABI sceHmdReprojectionStartMultilayer(const HmdReprojectionMultilayerParameter* layers, std::uint32_t layerCount, const HmdReprojectionSubmissionParameter* submission, const HmdReprojectionPose* pose, std::uint64_t flipArg, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -647,7 +653,7 @@ int APS5_VABI sceHmdInternalGetVirtualDisplayHeight(std::uint8_t* height) { return 0; } -int APS5_VABI sceHmdReprojectionStart(void) { +int APS5_VABI sceHmdReprojectionStart(const HmdReprojectionFrameParameter* parameter, const HmdReprojectionPose* pose, std::uint64_t flipArg, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -717,7 +723,7 @@ int APS5_VABI sceHmdGetWideNearDistortionCorrectionCommand(void) { return 0; } -int APS5_VABI sceHmdReprojectionStartMultilayer2(const HmdReprojectionMultilayerParameter* parameter, std::int32_t mode) { +int APS5_VABI sceHmdReprojectionStartMultilayer2(const HmdReprojectionMultilayerParameter* layers, std::uint32_t layerCount, const HmdReprojectionSubmissionParameter* submission, std::uint64_t flipArg, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -761,7 +767,7 @@ int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void) { return 0x810; } -int APS5_VABI sceHmdReprojectionStartWithOverlay(void) { +int APS5_VABI sceHmdReprojectionStartWithOverlay(const HmdReprojectionFrameParameter* parameter, const HmdReprojectionPose* pose, std::uint64_t flipArg, const HmdReprojectionOverlayParameter* overlay, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -781,7 +787,7 @@ int APS5_VABI sceHmdInternalSetDemoMode(std::uint8_t mode) { return 0; } -int APS5_VABI sceHmdReprojectionStartWideNearWithOverlay(void) { +int APS5_VABI sceHmdReprojectionStartWideNearWithOverlay(const HmdReprojectionWideNearParameter* parameter, const HmdReprojectionPose* pose, std::uint64_t flipArg, const HmdReprojectionOverlayParameter* overlay, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -831,7 +837,7 @@ int APS5_VABI sceHmdInternalDfuReset(void) { return 0; } -int APS5_VABI sceHmdReprojectionStart2dVr(void) { +int APS5_VABI sceHmdReprojectionStart2dVr(const HmdReprojection2dVrParameter* parameter, std::uint64_t flipArg, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index ab19b62b9b..5c3af6e4ef 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -9,25 +9,21 @@ - [libSceHmd internal and capture exports](../../../core/libs/prx/libSceHmd/Export.cpp) - input/output directions and argument registers are recovered for shared VR2D data, device binding, optical and serial queries, screen status and capture operations. VR2D records are 128 bytes, optical output 16, serial output 16, PU position output 20, crash-dump output 68, audio/video status 32, and reprojection debug outputs 216 or 1136. Payload field meanings and capture callback contracts remain incomplete; these structures use forward declarations. - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count -- [sceHmdReprojectionStartWideNear](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStartMultilayer](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGetWideNearCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStart](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStartWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStartWideNearWithOverlay](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdReprojectionStart2dVr](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature [Device, field-of-view, assembly-error, distortion, sensor and DFU query signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered from native register use. Field of view and assembly errors each output four floats; device information occupies 64 bytes and distortion output spans 76 bytes. The remaining device/sensor/report field semantics and DFU status bit meanings are unresolved. Assembly confirms integer error returns where decompilation incorrectly inferred void. Protected bodies are unchanged. -[Reprojection initialization and display-buffer signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered: initialization takes a parameter pointer, 32-bit mode and null reserved pointer; display-buffer setup takes a VideoOut handle, two buffer indices and reserved pointer, while adding a buffer takes an index and reserved pointer. `StartMultilayer2` takes a parameter pointer and 32-bit mode. Parameter fields remain incomplete. Protected bodies are unchanged. +[Reprojection initialization and display-buffer signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered: initialization takes a parameter pointer, 32-bit mode and null reserved pointer; display-buffer setup takes a VideoOut handle, two buffer indices and reserved pointer, while adding a buffer takes an index and reserved pointer. Parameter fields remain incomplete. Protected bodies are unchanged. [Work-memory queries, capture start, separate-mode open and crash-report cancellation](../../../core/libs/prx/libSceHmd/Export.cpp) consume no arguments in the supplied native implementation. The mirroring aspect debug setter takes five 16-bit values followed by a byte mode; the individual scalar meanings remain unresolved. These argument lists are confirmed; protected bodies remain unimplemented. + +[Reprojection start signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered, including the five arguments of `StartMultilayer2` hidden by an incomplete decompilation. Standard and wide-near starts take frame parameters, pose, 64-bit flip argument and reserved pointer; overlay variants add an overlay descriptor. The 2D variant takes parameters, flip argument and reserved pointer. Multilayer takes a layer array/count, submission parameters, pose, flip argument and reserved pointer. Frame, pose, overlay and submission field semantics remain incomplete. Protected bodies are unchanged. From 6703b32a3788600e89bd9c9fa6e927e74af0b3ac Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:41:53 +0300 Subject: [PATCH 362/523] fix(hmd): restore distortion command output and export aliases --- core/libs/prx/libSceHmd/Export.cpp | 26 +++++++++++++++++++------- docs/dev/debt/libSceHmd.md | 11 ++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index ab37d80f72..8bed93169b 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -3,6 +3,18 @@ #include +struct HmdDistortionCommandParameter; +struct HmdWideNearDistortionCommandParameter; +struct Hmd2dVrCommandParameter; + +struct HmdCommandBuffer { + const std::uint32_t* commands; + std::uint32_t dwordCount; +}; + +static_assert(sizeof(HmdCommandBuffer) == 16); +static_assert(offsetof(HmdCommandBuffer, dwordCount) == 8); + struct HmdReprojectionFrameParameter; struct HmdReprojectionWideNearParameter; struct HmdReprojection2dVrParameter; @@ -419,7 +431,7 @@ int APS5_VABI sceHmdInternalMmapGetSensorCalibrationData(void* buffer, std::uint return 0; } -int APS5_VABI sceHmdDistortionGetCorrectionCommand(void) { +int APS5_VABI sceHmdDistortionGetCorrectionCommand(std::int32_t handle, const HmdDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -553,7 +565,7 @@ int APS5_VABI sceHmdInternalGetHmuSerialNumber(std::uint8_t* serialNumber) { return 0; } -int APS5_VABI sceHmdDistortionGetWideNearCorrectionCommand(void) { +int APS5_VABI sceHmdDistortionGetWideNearCorrectionCommand(std::int32_t handle, const HmdWideNearDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -608,7 +620,7 @@ int APS5_VABI sceHmdInternalSetVirtualDisplayHeight(std::uint8_t height) { return 0; } -int APS5_VABI sceHmdDistortionInitialize(void) { +int APS5_VABI sceHmdDistortionInitialize(void* workMemory) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -708,7 +720,7 @@ int APS5_VABI sceHmdInternalMmapGetModeId(std::uint32_t* modeId) { return 0; } -int APS5_VABI sceHmdDistortionGet2dVrCommand(void) { +int APS5_VABI sceHmdDistortionGet2dVrCommand(std::int32_t handle, const Hmd2dVrCommandParameter* parameter, HmdCommandBuffer* commandBuffer) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -718,7 +730,7 @@ int APS5_VABI sceHmdInternalDfuSendSize(std::int32_t size) { return 0; } -int APS5_VABI sceHmdGetWideNearDistortionCorrectionCommand(void) { +int APS5_VABI sceHmdGetWideNearDistortionCorrectionCommand(std::int32_t handle, const HmdWideNearDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -728,7 +740,7 @@ int APS5_VABI sceHmdReprojectionStartMultilayer2(const HmdReprojectionMultilayer return 0; } -int APS5_VABI sceHmdGetDistortionCorrectionCommand(void) { +int APS5_VABI sceHmdGetDistortionCorrectionCommand(std::int32_t handle, const HmdDistortionCommandParameter* parameter, HmdCommandBuffer* commandBuffer) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -943,7 +955,7 @@ int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void) { return 0x100000; } -int APS5_VABI sceHmdGet2dVrCommand(void) { +int APS5_VABI sceHmdGet2dVrCommand(std::int32_t handle, const Hmd2dVrCommandParameter* parameter, HmdCommandBuffer* commandBuffer) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 5c3af6e4ef..6c4d37abd8 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -10,15 +10,8 @@ - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionGetCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionGetWideNearCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionInitialize](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdDistortionGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetWideNearDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGetDistortionCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdGet2dVrCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature [Device, field-of-view, assembly-error, distortion, sensor and DFU query signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered from native register use. Field of view and assembly errors each output four floats; device information occupies 64 bytes and distortion output spans 76 bytes. The remaining device/sensor/report field semantics and DFU status bit meanings are unresolved. Assembly confirms integer error returns where decompilation incorrectly inferred void. Protected bodies are unchanged. @@ -27,3 +20,7 @@ [Work-memory queries, capture start, separate-mode open and crash-report cancellation](../../../core/libs/prx/libSceHmd/Export.cpp) consume no arguments in the supplied native implementation. The mirroring aspect debug setter takes five 16-bit values followed by a byte mode; the individual scalar meanings remain unresolved. These argument lists are confirmed; protected bodies remain unimplemented. [Reprojection start signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered, including the five arguments of `StartMultilayer2` hidden by an incomplete decompilation. Standard and wide-near starts take frame parameters, pose, 64-bit flip argument and reserved pointer; overlay variants add an overlay descriptor. The 2D variant takes parameters, flip argument and reserved pointer. Multilayer takes a layer array/count, submission parameters, pose, flip argument and reserved pointer. Frame, pose, overlay and submission field semantics remain incomplete. Protected bodies are unchanged. + +[Distortion and 2D VR command queries](../../../core/libs/prx/libSceHmd/Export.cpp) take a 32-bit HMD handle, input descriptor and command-buffer output. The output contains a command pointer at offset zero and a 32-bit dword count at offset eight. Descriptor fields and GPU resource types remain incomplete; the standard, wide-near and 2D descriptors are distinct. Protected bodies are unchanged. + +The ELF export table aliases the [Distortion correction/2D command and work-memory queries](../../../core/libs/prx/libSceHmd/Export.cpp) to their corresponding HMD queries, confirming identical signatures. Distortion initialization consumes only a work-memory pointer. No shader code or calibration constants are copied; protected bodies remain unchanged. From 43398d5572330c9b55844f6572c0c30297660617 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:46:23 +0300 Subject: [PATCH 363/523] fix(pad): restore internal data and device control signatures --- core/libs/prx/libScePad/Export.cpp | 55 +++++++++++++++++++++--------- docs/dev/debt/libScePad.md | 19 ++--------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 4eab1575c4..b014e4013b 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -39,6 +39,29 @@ struct PadBlasterTrackerData; struct PadAstonTrackerData; struct PadUiData; struct PadUiTuningData; +struct PadLotusButtonData; +struct PadVersionInfo; +struct PadTelemetryInfo; +struct PadPortInfo; +struct PadVrControllerCombinedState; +struct PadLeonardoData; +struct PadLeonardoUniqueData; +struct PadHistoryData; +struct PadUpdateParameter; + +struct PadLightBarBlinkingParameter { + std::uint8_t values[3]; +}; + +struct PadButtonRemappingInfo { + std::uint8_t mapping[14]; + std::uint8_t reserved; + std::uint8_t mode; +}; + +static_assert(sizeof(PadLightBarBlinkingParameter) == 3); +static_assert(sizeof(PadButtonRemappingInfo) == 16); +static_assert(offsetof(PadButtonRemappingInfo, mode) == 15); namespace { @@ -92,7 +115,7 @@ int APS5_VABI scePadVrControllerSetVibrationMode(std::int32_t handle, std::uint3 return 0; } -int APS5_VABI scePadVrControllerReadCombinedState() { +int APS5_VABI scePadVrControllerReadCombinedState(std::int32_t leftHandle, std::int32_t rightHandle, PadVrControllerCombinedState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -381,7 +404,7 @@ int APS5_VABI scePadSetTriggerEffectExt(std::int32_t handle, const PadTriggerEff return 0; } -int APS5_VABI scePadLastLotusButtonData(void) { +int APS5_VABI scePadLastLotusButtonData(std::int32_t handle, std::uint32_t parameter, PadLotusButtonData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -410,17 +433,17 @@ int APS5_VABI scePadEnableSpecificDeviceClass(std::int32_t deviceClass) { return 0; } -int APS5_VABI scePadOpenExt2(void) { +int APS5_VABI scePadOpenExt2(std::uint64_t deviceId, std::int32_t type, const void* reserved) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadDeviceOpen(void) { +int APS5_VABI scePadDeviceOpen(std::uint64_t deviceId, std::int32_t type, std::int32_t index) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadGetDataInternal(void) { +int APS5_VABI scePadGetDataInternal(std::uint8_t port, PadData* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -445,7 +468,7 @@ int APS5_VABI scePadSetLightBarBaseBrightness(std::int32_t handle, std::uint32_t return 0; } -int APS5_VABI scePadSetLightBarBlinking(void) { +int APS5_VABI scePadSetLightBarBlinking(std::int32_t handle, const PadLightBarBlinkingParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -515,7 +538,7 @@ int APS5_VABI scePadSetVibrationForce(std::int32_t handle, const PadVibrationPar return 0; } -int APS5_VABI scePadGetVersionInfo(void) { +int APS5_VABI scePadGetVersionInfo(std::int32_t handle, PadVersionInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -530,7 +553,7 @@ int APS5_VABI scePadDisableDataConversion(std::int32_t handle, bool disabled) { return 0; } -int APS5_VABI scePadSetButtonRemappingInfo(void) { +int APS5_VABI scePadSetButtonRemappingInfo(std::int32_t handle, const PadButtonRemappingInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -695,7 +718,7 @@ int APS5_VABI scePadVrControllerGetPhysicalUsbConnectionStatus(PadVrUsbConnectio return 0; } -int APS5_VABI scePadParseUniqueDataLeonardo(void) { +int APS5_VABI scePadParseUniqueDataLeonardo(std::int32_t handle, const PadLeonardoData* data, PadLeonardoUniqueData* uniqueData) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -785,7 +808,7 @@ int APS5_VABI scePadRegisterCollaboInputMgrProcess(void) { return 0; } -int APS5_VABI scePadGetTelemetryInfo(void) { +int APS5_VABI scePadGetTelemetryInfo(std::int32_t handle, PadTelemetryInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -800,7 +823,7 @@ int APS5_VABI scePadGetDeviceId(std::int32_t handle, std::int32_t* deviceId) { return 0; } -int APS5_VABI scePadGetInfoByPortType(void) { +int APS5_VABI scePadGetInfoByPortType(std::int32_t type, PadPortInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -995,7 +1018,7 @@ int APS5_VABI scePadGetLicenseControllerInformation(std::int32_t handle, PadLice return 0; } -int APS5_VABI scePadReadHistory(void) { +int APS5_VABI scePadReadHistory(std::int32_t handle, PadHistoryData* data, std::int32_t count) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1010,12 +1033,12 @@ int APS5_VABI scePadStopRecording(void) { return 0; } -int APS5_VABI scePadRemoteControlInsertData(void) { +int APS5_VABI scePadRemoteControlInsertData(const std::uint8_t* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadRemoteControlInsertDataForAston(void) { +int APS5_VABI scePadRemoteControlInsertDataForAston(const std::uint8_t* data) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1045,7 +1068,7 @@ int APS5_VABI scePadBondUpdateIsReady(std::int32_t deviceId, std::int32_t* ready return 0; } -int APS5_VABI scePadBondUpdateStart(void) { +int APS5_VABI scePadBondUpdateStart(std::int32_t handle, const PadUpdateParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1090,7 +1113,7 @@ int APS5_VABI scePadControllerUpdateIsReady(std::int32_t deviceId, std::int32_t* return 0; } -int APS5_VABI scePadControllerUpdateStart(void) { +int APS5_VABI scePadControllerUpdateStart(std::int32_t handle, const PadUpdateParameter* parameter) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 81f69445a0..1a6563e40e 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -10,33 +10,20 @@ - [scePadSetAngularVelocityDeadbandState](../../../core/libs/prx/libScePad/src/PadState.cpp) (libScePad) - with the deadband on, each angular velocity axis whose magnitude is below 0.017453292 rad/s (1 degree per second) reads as 0, before the orientation is integrated; it starts off. Threshold, per-axis test and default follow [duaLib](https://github.com/WujekFoliarz/duaLib/blob/03bad1bea2a36561b520846776f2a07ced6773e0/src/source/duaLib.cpp#L44), which reimplements the PC `libScePad.dll`; the console's threshold is not measured, and the deadband is kept per library rather than per handle since one pad handle exists - [scePadOpen and scePadOpenExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - special ports (type 2) return `DEVICE_NOT_CONNECTED` (0x80920007) because the host has no special controller, and `scePadGetHandle` has no handle for them; whether the console opens a handle and reports the device as disconnected later is unknown. scePadOpenExt handles only special ports. PPSA01317 passes a 16-byte param that names the device by vendor and product id (`b7 0e 04 0e ...` for 0x0eb7:0x0e04) and skips the user on any negative result. Other port types throw - [scePadVrControllerReadState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `iA-DdobUen8`) - the name hashes to the imported NID; the `(int handle, void* state)` signature follows [PS5VR](https://github.com/theghostonline/PS5VR/blob/0e408909ce7a39891a2e06eee3438d07a09e11e3/app/src/app_vr.c#L326-L369) (PPSA99177), which passes a PS VR2 Sense controller handle from `scePadOpen` type 3 or 4 and a 0x68-byte state it reads as buttons at 0, stick bytes at 4 and a connected byte at 0x40. The title calls it only after `sceHmd2Initialize` succeeds, which it does not here, so every call throws; the state layout is otherwise unknown -- PPSA07411 PS VR2 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [scePadVrControllerReadCombinedState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) and [sceVrTracker2ResetLocalCoordinate](../../../core/libs/prx/libSceVrTracker2/Export.cpp) (libSceVrTracker2) +- [scePadVrControllerReadCombinedState](../../../core/libs/prx/libScePad/Export.cpp) - the two handle arguments and 128-byte output are recovered; output field semantics remain incomplete. - [scePadSetVibrationMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - modes 1 to 3 and 5 are accepted and stored. On a PS5 (firmware 13.60) the call returns `0x80920005` before `scePadInit`, then rejects a mode of 0 or below with `INVALID_ARG` before it checks the handle, and returns `INVALID_HANDLE` for a bad handle with any higher mode; whether an open handle accepts modes above 3 was not measured, since the payload could not open a pad; 5 is accepted because PPSA25286 passes it on its open handle at start-up; the meanings of modes 3 and 5 are unknown (PPSA21480 calls it with 3 right after `scePadOpen` and aborts on failure; [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Pad/Pad.cs) names only 1 and 2). The stored mode has no other effect - [scePadIsRemoteController](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false - [scePadSetAngularVelocityBiasCorrectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled - [scePadGetAccessibilityControllerState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the value a console reports for a DualSense is not measured; the emulated pad, which is not an Access controller, reports 0. The name hashes to NID `zBsgF0q8DIM`. In the OS 10.01 system module the `(int handle, uint32_t* state)` function returns `0x80920005` before `scePadInit`, then `INVALID_ARG` for a NULL `state` and `INVALID_HANDLE` for a bad handle, and otherwise asks the pad driver for a per-device value of the device behind the handle: 0 when no device is behind it, 1 when the driver has no answer, else the driver's value. What the values mean is unknown -- [scePadLastLotusButtonData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadOpenExt2](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadDeviceOpen](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetDataInternal](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetLightBarBlinking](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetVersionInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetButtonRemappingInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProfileHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadParseUniqueDataLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetTelemetryInfo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadGetInfoByPortType](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVertualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadVirtualDeviceInsertTriggerInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [scePadSetVirtualDeviceComponentInformation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadReadHistory](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadRemoteControlInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadRemoteControlInsertDataForAston](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadBondUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadControllerUpdateStart](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature + +- [libScePad internal data and device control](../../../core/libs/prx/libScePad/Export.cpp) - kernel-owned version, telemetry, Lotus-button and update payload fields remain unresolved. Lotus-button parameter units and the three blinking byte meanings are unknown. Port information occupies 152 bytes; history records occupy 72 bytes; Leonardo unique data occupies 24 bytes. Their complete field semantics remain unresolved. ### Functional From 4a84bfe06bd48db9fcb455189b5528e114e748f4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:47:43 +0300 Subject: [PATCH 364/523] fix(pad): restore profile writes and virtual device parameters --- core/libs/prx/libScePad/Export.cpp | 27 ++++++++++++++++++++------- docs/dev/debt/libScePad.md | 9 ++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index b014e4013b..d66adfa338 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -48,6 +48,19 @@ struct PadLeonardoData; struct PadLeonardoUniqueData; struct PadHistoryData; struct PadUpdateParameter; +struct PadVirtualDeviceParameter; +struct PadVirtualDeviceId; +struct PadVirtualDeviceComponentInformation; + +struct PadVirtualTriggerInternalState { + std::uint32_t states[2]; + std::uint8_t mode; + std::uint8_t flags[2]; +}; + +static_assert(sizeof(PadVirtualTriggerInternalState) == 12); +static_assert(offsetof(PadVirtualTriggerInternalState, mode) == 8); +static_assert(offsetof(PadVirtualTriggerInternalState, flags) == 9); struct PadLightBarBlinkingParameter { std::uint8_t values[3]; @@ -668,7 +681,7 @@ int APS5_VABI scePadGetProfileHunt(std::int32_t handle, std::int32_t profileId, return 0; } -int APS5_VABI scePadSetProfileHunt(void) { +int APS5_VABI scePadSetProfileHunt(std::int32_t handle, std::int32_t profileId, PadHuntProfile* profile, std::uint32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -738,7 +751,7 @@ int APS5_VABI scePadGetProfileLeonardo(std::int32_t handle, std::int32_t profile return 0; } -int APS5_VABI scePadSetProfileLeonardo(void) { +int APS5_VABI scePadSetProfileLeonardo(std::int32_t handle, std::int32_t profileId, PadLeonardoProfile* profile, std::uint32_t* result) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -953,12 +966,12 @@ int APS5_VABI scePadSetProcessFocus(const PadProcessFocusParameter* parameter) { return 0; } -int APS5_VABI scePadVertualDeviceAddDevice(void) { +int APS5_VABI scePadVertualDeviceAddDevice(const PadVirtualDeviceParameter* parameter, PadVirtualDeviceId* deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVirtualDeviceAddDevice(void) { +int APS5_VABI scePadVirtualDeviceAddDevice(const PadVirtualDeviceParameter* parameter, PadVirtualDeviceId* deviceId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -968,12 +981,12 @@ int APS5_VABI scePadVirtualDeviceDeleteDevice(std::int32_t deviceId) { return 0; } -int APS5_VABI scePadVirtualDeviceInsertData(void) { +int APS5_VABI scePadVirtualDeviceInsertData(std::int32_t deviceId, const PadData* data) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadVirtualDeviceInsertTriggerInternalState(void) { +int APS5_VABI scePadVirtualDeviceInsertTriggerInternalState(std::int32_t deviceId, const PadVirtualTriggerInternalState* state) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -1003,7 +1016,7 @@ int APS5_VABI scePadSetPS4BcVibrationMode(std::uint8_t mode) { return 0; } -int APS5_VABI scePadSetVirtualDeviceComponentInformation(void) { +int APS5_VABI scePadSetVirtualDeviceComponentInformation(std::int32_t deviceId, const PadVirtualDeviceComponentInformation* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 1a6563e40e..10146571bc 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -15,16 +15,11 @@ - [scePadIsRemoteController](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false - [scePadSetAngularVelocityBiasCorrectionState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled - [scePadGetAccessibilityControllerState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the value a console reports for a DualSense is not measured; the emulated pad, which is not an Access controller, reports 0. The name hashes to NID `zBsgF0q8DIM`. In the OS 10.01 system module the `(int handle, uint32_t* state)` function returns `0x80920005` before `scePadInit`, then `INVALID_ARG` for a NULL `state` and `INVALID_HANDLE` for a bad handle, and otherwise asks the pad driver for a per-device value of the device behind the handle: 0 when no device is behind it, 1 when the driver has no answer, else the driver's value. What the values mean is unknown -- [scePadSetProfileHunt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetProfileLeonardo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVertualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceAddDevice](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceInsertData](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadVirtualDeviceInsertTriggerInternalState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature -- [scePadSetVirtualDeviceComponentInformation](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown signature - [libScePad internal data and device control](../../../core/libs/prx/libScePad/Export.cpp) - kernel-owned version, telemetry, Lotus-button and update payload fields remain unresolved. Lotus-button parameter units and the three blinking byte meanings are unknown. Port information occupies 152 bytes; history records occupy 72 bytes; Leonardo unique data occupies 24 bytes. Their complete field semantics remain unresolved. +- [libScePad profile writes and virtual devices](../../../core/libs/prx/libScePad/Export.cpp) - profile writes take a handle, profile ID, profile pointer and a 32-bit result pointer; the profile payload direction and fields remain unresolved. Virtual-device creation takes an input record and an output ID pointer, whose width is not exposed by the wrapper. Component information copies 16 bytes; field meanings remain unknown. Virtual trigger state contains two 32-bit states and three control bytes, but the control semantics remain incomplete. + ### Functional - [Touchpad resolution](../../../core/libs/prx/libScePad/include/PadState.hpp) (libScePad) - the library reports a DualSense to every title, so the controller information reports its touchpad, 1920 by 1080 at 44.86 pixels per cm, and the host controller's fingers and the keyboard touch contacts are placed in that range. [duaLib](https://github.com/WujekFoliarz/duaLib/blob/0b39f92261e2ff0e77eb0dac00943be6970e0dbf/src/source/duaLib.cpp#L847-L849) reports these values for a DualSense and 1920 by 943 at 44 for a DualShock 4, and the Linux kernel's [hid-playstation](https://github.com/torvalds/linux/blob/cd4f494ba535e848e2d299707d363de84e61a518/drivers/hid/hid-playstation.c#L177-L178) holds 1920 by 1080 against 1920 by 942 for the DualShock 4. A DualShock 4 on the host reports through the DualSense range too, so its last touchpad rows are never reached. The console's values are not measured From bd7a73eb58fd66b4d2d301b11faa54deeb1575bf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:49:23 +0300 Subject: [PATCH 365/523] fix(http): restore cookie and socket callback signatures --- core/libs/prx/libSceHttp/Export.cpp | 6 ++++-- docs/dev/debt/libSceHttp.md | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index e6e88e47a5..1cde24b012 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -27,6 +27,8 @@ static std::atomic g_nextHandle{1}; struct HttpConnectionStat; struct HttpRequestStat; +using HttpCookieSendCallback = int (APS5_VABI *)(std::int32_t, const char*, const char*, void*); +using HttpSocketCreationCallback = void (APS5_VABI *)(std::int32_t, std::int32_t, void*); using HttpRequestStatusCallback = void (APS5_VABI *)(int, int, void*); struct RequestStatusRegistration { HttpRequestStatusCallback callback; @@ -759,7 +761,7 @@ int APS5_VABI sceHttpAddCookie(int contextId, const char* url, const char* cooki return 0; } -int APS5_VABI sceHttpSetCookieSendCallback(void) { +int APS5_VABI sceHttpSetCookieSendCallback(std::int32_t id, HttpCookieSendCallback callback, void* userArg) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -825,7 +827,7 @@ int APS5_VABI sceHttpSetProxy(int id, int mode, int authentication, const char* return 0; } -int APS5_VABI sceHttpSetSocketCreationCallback(void) { +int APS5_VABI sceHttpSetSocketCreationCallback(std::int32_t id, HttpSocketCreationCallback callback, void* userArg) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 59b4ddb5b2..58871b5572 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -29,8 +29,6 @@ - [sceHttpDbgShowConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. - [sceHttpDbgShowRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. - [sceHttpDbgShowMemoryPoolStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. -- [sceHttpSetCookieSendCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpSetSocketCreationCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature ### Functional From cd175c3aad990a30166314107af75e8f61caaedf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:49:24 +0300 Subject: [PATCH 366/523] fix(hmd): restore default led data setter arguments --- core/libs/prx/libSceHmd/Export.cpp | 8 +++++++- docs/dev/debt/libSceHmd.md | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 8bed93169b..6f28a18447 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -116,6 +116,12 @@ static_assert(offsetof(HmdScreenData, size) == 8); static_assert(sizeof(HmdEyeDistances) == 8); static_assert(sizeof(HmdEyeOffset) == 12); +struct HmdLedData { + std::uint8_t values[9]; +}; + +static_assert(sizeof(HmdLedData) == 9); + extern "C" { std::int32_t APS5_VABI sceHmdInitialize(const Hmd::InitializeParam* param) { @@ -859,7 +865,7 @@ int APS5_VABI sceHmdInternalMmapGetCount(void) { return 0; } -int APS5_VABI sceHmdInternalSetDefaultLedData(void) { +int APS5_VABI sceHmdInternalSetDefaultLedData(std::int32_t handle, std::uint32_t mask, const HmdLedData* data) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 6c4d37abd8..76f7c2767d 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -11,7 +11,6 @@ - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count - [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature - [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalSetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature [Device, field-of-view, assembly-error, distortion, sensor and DFU query signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered from native register use. Field of view and assembly errors each output four floats; device information occupies 64 bytes and distortion output spans 76 bytes. The remaining device/sensor/report field semantics and DFU status bit meanings are unresolved. Assembly confirms integer error returns where decompilation incorrectly inferred void. Protected bodies are unchanged. From ea086b23745cb167780f5aed0a3b9600a4e8dd0a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:52:48 +0300 Subject: [PATCH 367/523] fix(netctl): restore initialization and callback validation --- core/libs/SceTypes.hpp | 7 +++- core/libs/prx/libSceNetCtl/Export.cpp | 34 ++++++++++++------ core/libs/tests/GuestNetCtl.cpp | 50 ++++++++++++++++++++++++--- core/libs/tests/GuestSceNet.cpp | 2 ++ docs/dev/debt/libSceNetCtl.md | 6 ++-- 5 files changed, 79 insertions(+), 20 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 9c81747059..a85ce12b51 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1334,7 +1334,12 @@ struct NetEpollEvent { NetEpollData data; }; -struct NetCtlNatInfo { std::uint8_t opaque[128]; }; +struct NetCtlNatInfo { + std::uint32_t size; + std::uint8_t opaque[12]; +}; + +static_assert(sizeof(NetCtlNatInfo) == 16); union NetCtlInfo { std::uint8_t opaque[256]; }; diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index c3a558cda9..aea249a719 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -19,6 +19,7 @@ #include #endif #endif +#include #include #include #include @@ -45,6 +46,9 @@ struct NetCtlNetEvConfiguration; struct NetBweInfo; struct NetCtlWifiInfo; +static constexpr int SCE_NET_CTL_ERROR_NOT_INITIALIZED = static_cast(0x80412101); +static constexpr int SCE_NET_CTL_ERROR_ID_NOT_FOUND = static_cast(0x80412104); +static constexpr int SCE_NET_CTL_ERROR_INVALID_SIZE = static_cast(0x80412111); static constexpr int SCE_NET_CTL_ERROR_CALLBACK_MAX = static_cast(0x80412103); static constexpr int SCE_NET_CTL_ERROR_INVALID_ID = static_cast(0x80412105); static constexpr int SCE_NET_CTL_ERROR_INVALID_CODE = static_cast(0x80412106); @@ -91,11 +95,13 @@ struct CallbackSlot { int lastEvent = 0; }; +static std::atomic_bool g_initialized{false}; static std::mutex g_callbackLock; static CallbackSlot g_callbacks[MAX_CALLBACKS] = {}; static CallbackSlot g_callbacksV6[MAX_CALLBACKS] = {}; static int RegisterCallback(CallbackSlot (&callbacks)[MAX_CALLBACKS], NetCtlCallback func, void* arg, int* cid) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (!func || !cid) return SCE_NET_CTL_ERROR_INVALID_ADDR; std::lock_guard lock(g_callbackLock); for (int index = 0; index < MAX_CALLBACKS; ++index) { @@ -109,8 +115,10 @@ static int RegisterCallback(CallbackSlot (&callbacks)[MAX_CALLBACKS], NetCtlCall } static int UnregisterCallback(CallbackSlot (&callbacks)[MAX_CALLBACKS], int cid) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (cid < 0 || cid >= MAX_CALLBACKS) return SCE_NET_CTL_ERROR_INVALID_ID; std::lock_guard lock(g_callbackLock); + if (!callbacks[cid].func) return SCE_NET_CTL_ERROR_ID_NOT_FOUND; callbacks[cid] = {}; return 0; } @@ -420,6 +428,7 @@ static void HostTraffic(std::uint64_t& sent, std::uint64_t& received) { extern "C" { int APS5_VABI sceNetCtlCheckCallback(void) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (!HasIpv4Callback()) return 0; const int event = HostHasAddress(AF_UNSPEC) ? NET_CTL_EVENT_IPOBTAINED : NET_CTL_EVENT_DISCONNECTED; CallbackSlot due[MAX_CALLBACKS]; @@ -437,6 +446,7 @@ int APS5_VABI sceNetCtlCheckCallback(void) { } int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat* stat) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (!stat) return SCE_NET_CTL_ERROR_INVALID_ADDR; if (!HostHasAddress(AF_UNSPEC)) return SCE_NET_CTL_ERROR_NOT_CONNECTED; *stat = {}; @@ -445,6 +455,7 @@ int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat* stat) { } int APS5_VABI sceNetCtlGetInfo(int code, NetCtlInfo* info) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (!info) return SCE_NET_CTL_ERROR_INVALID_ADDR; if (code < NET_CTL_INFO_DEVICE || code > NET_CTL_INFO_HTTP_PROXY_PORT) return SCE_NET_CTL_ERROR_INVALID_CODE; HostIpv4Interface host; @@ -476,31 +487,35 @@ int APS5_VABI sceNetCtlGetInfo(int code, NetCtlInfo* info) { return 0; } -int APS5_VABI sceNetCtlGetNatInfo(NetCtlNatInfo* nat_info) { - if (!nat_info) return SCE_NET_CTL_ERROR_INVALID_ADDR; - return SCE_NET_CTL_ERROR_NOT_CONNECTED; +int APS5_VABI sceNetCtlGetNatInfo(NetCtlNatInfo* info) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; + if (!info) return SCE_NET_CTL_ERROR_INVALID_ADDR; + if (info->size != sizeof(NetCtlNatInfo)) return SCE_NET_CTL_ERROR_INVALID_SIZE; + throw std::logic_error("sceNetCtlGetNatInfo: NAT discovery is not implemented"); } -int APS5_VABI sceNetCtlGetResult(int event_type, int* error_code) { - (void)event_type; - if (!error_code) return SCE_NET_CTL_ERROR_INVALID_ADDR; - *error_code = 0; - return 0; +int APS5_VABI sceNetCtlGetResult(int event, int* result) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; + if (!result) return SCE_NET_CTL_ERROR_INVALID_ADDR; + throw std::logic_error("sceNetCtlGetResult: service event results are not implemented"); } int APS5_VABI sceNetCtlGetState(int* state) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (!state) return SCE_NET_CTL_ERROR_INVALID_ADDR; *state = HostHasAddress(AF_UNSPEC) ? NET_CTL_STATE_IPOBTAINED : NET_CTL_STATE_DISCONNECTED; return 0; } int APS5_VABI sceNetCtlGetStateV6(int* state) { + if (!g_initialized.load()) return SCE_NET_CTL_ERROR_NOT_INITIALIZED; if (!state) return SCE_NET_CTL_ERROR_INVALID_ADDR; *state = HostHasAddress(AF_INET6) ? NET_CTL_STATE_IPOBTAINED : NET_CTL_STATE_DISCONNECTED; return 0; } int APS5_VABI sceNetCtlInit(void) { + g_initialized.store(true); return 0; } @@ -520,9 +535,6 @@ int APS5_VABI sceNetCtlGetInfoV6(int code, void* info) { } void APS5_VABI sceNetCtlTerm(void) { - std::lock_guard lock(g_callbackLock); - for (auto& slot : g_callbacks) slot = {}; - for (auto& slot : g_callbacksV6) slot = {}; } int APS5_VABI sceNetCtlUnregisterCallback(int cid) { diff --git a/core/libs/tests/GuestNetCtl.cpp b/core/libs/tests/GuestNetCtl.cpp index ea68f8eb02..aa2c54300b 100644 --- a/core/libs/tests/GuestNetCtl.cpp +++ b/core/libs/tests/GuestNetCtl.cpp @@ -32,6 +32,7 @@ struct NetCtlDefaultRoute { #endif extern "C" { +int APS5_VABI sceNetCtlInit(void); int APS5_VABI sceNetCtlCheckCallback(void); int APS5_VABI sceNetCtlGetInfo(int, NetCtlInfo*); int APS5_VABI sceNetCtlGetNatInfo(NetCtlNatInfo*); @@ -141,13 +142,19 @@ void CheckCallbacks() { Require(sceNetCtlRegisterCallback(RecordSecond, &secondProbe, &second) == 0 && second == 1); sceNetCtlTerm(); Require(sceNetCtlCheckCallback() == 0); - Require(g_first.calls == 1 && g_second.calls == 1); + Require(g_first.calls == 1 && g_second.calls == 2); + Require(sceNetCtlInit() == 0); + Require(sceNetCtlUnregisterCallback(first) == 0); + Require(sceNetCtlUnregisterCallback(second) == 0); + Require(sceNetCtlUnregisterCallbackV6(v6) == 0); + Require(sceNetCtlUnregisterCallback(first) == static_cast(0x80412104)); + Require(sceNetCtlUnregisterCallbackV6(v6) == static_cast(0x80412104)); Require(sceNetCtlRegisterCallbackV6(RecordV6, &v6Probe, &v6) == 0 && v6 == 0); Require(sceNetCtlRegisterCallback(RecordFirst, &secondProbe, &first) == 0 && first == 0); Require(sceNetCtlCheckCallback() == 0); Require(g_first.calls == 2 && g_first.event == event && g_first.arg == &secondProbe); - Require(g_second.calls == 1 && g_self.calls == 1 && g_v6.calls == 0); + Require(g_second.calls == 2 && g_self.calls == 1 && g_v6.calls == 0); Require(sceNetCtlUnregisterCallback(first) == 0); Require(sceNetCtlUnregisterCallbackV6(v6) == 0); } @@ -263,6 +270,20 @@ int main() { #ifdef __APPLE__ CheckDefaultRoutes(); #endif + constexpr int notInitialized = static_cast(0x80412101); + Require(sceNetCtlCheckCallback() == notInitialized); + Require(sceNetCtlGetState(nullptr) == notInitialized); + Require(sceNetCtlGetStateV6(nullptr) == notInitialized); + Require(sceNetCtlGetInfo(0, nullptr) == notInitialized); + Require(sceNetCtlGetResult(0, nullptr) == notInitialized); + Require(sceNetCtlGetNatInfo(nullptr) == notInitialized); + Require(sceNetCtlRegisterCallback(nullptr, nullptr, nullptr) == notInitialized); + Require(sceNetCtlRegisterCallbackV6(nullptr, nullptr, nullptr) == notInitialized); + Require(sceNetCtlUnregisterCallback(-1) == notInitialized); + Require(sceNetCtlUnregisterCallbackV6(-1) == notInitialized); + sceNetCtlTerm(); + Require(sceNetCtlCheckCallback() == notInitialized); + Require(sceNetCtlInit() == 0); CheckCallbacks(); Require(sceNetCtlGetStateV6(nullptr) == kErrInvalidAddr); @@ -271,7 +292,13 @@ int main() { Require(stateV6 == 0 || stateV6 == kStateIpObtained); Require(sceNetCtlGetResult(0, nullptr) == kErrInvalidAddr); int errorCode = -1; - Require(sceNetCtlGetResult(0, &errorCode) == 0 && errorCode == 0); + bool resultUnsupported = false; + try { + sceNetCtlGetResult(0, &errorCode); + } catch (const std::logic_error&) { + resultUnsupported = true; + } + Require(resultUnsupported && errorCode == -1); Require(sceNetCtlGetResultV6(kEventIpObtained, nullptr) == kErrInvalidAddr); for (const int eventType : {kEventDisconnected, 2, kEventIpObtained}) { int errorCodeV6 = -1; @@ -335,6 +362,19 @@ int main() { } Require(sceNetCtlGetNatInfo(nullptr) == kErrInvalidAddr); - NetCtlNatInfo nat_info{}; - Require(sceNetCtlGetNatInfo(&nat_info) == kErrNotConnected); + NetCtlNatInfo natInfo{}; + for (const auto size : {0u, 15u, 17u, 128u, 0xffffffffu}) { + natInfo.size = size; + Require(sceNetCtlGetNatInfo(&natInfo) == static_cast(0x80412111)); + } + natInfo.size = sizeof(natInfo); + std::memset(natInfo.opaque, kFill, sizeof(natInfo.opaque)); + bool natUnsupported = false; + try { + sceNetCtlGetNatInfo(&natInfo); + } catch (const std::logic_error&) { + natUnsupported = true; + } + Require(natUnsupported && natInfo.size == sizeof(natInfo)); + for (const auto value : natInfo.opaque) Require(value == kFill); } diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 3f775bd82e..fd59160c0c 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -75,6 +75,7 @@ int APS5_VABI sceNetResolverDestroy(int); int APS5_VABI sceNetResolverGetError(int, int*); int APS5_VABI sceNetClearDnsCache(int); int APS5_VABI sceNetGetDnsInfo(std::uint32_t*, int); +int APS5_VABI sceNetCtlInit(void); int APS5_VABI sceNetCtlGetState(int*); int APS5_VABI sceNetCtlGetInfo(int, NetCtlInfo*); int APS5_VABI sceNetCtlGetIfStat(NetCtlIfStat*); @@ -1874,6 +1875,7 @@ int main() { if (!ipv6Unavailable) CheckStreamSender(28); int state = -1; + Require(sceNetCtlInit() == 0); Require(sceNetCtlGetState(&state) == 0); Require(state == 0 || state == 3); Require(sceNetCtlGetIfStat(nullptr) == static_cast(0x80412107)); diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 5c9e32df16..5c6533f6ff 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -1,8 +1,8 @@ # libSceNetCtl technical debt -### Silent stubs +### Functional -- [sceNetCtlGetNatInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - `sceNetCtlGetNatInfo` validates null pointers and returns `SCE_NET_CTL_ERROR_NOT_CONNECTED` (`0x80412108`) without performing NAT discovery. +- [sceNetCtlGetNatInfo and sceNetCtlGetResult](../../../core/libs/prx/libSceNetCtl/Export.cpp) - NAT discovery and service event results are not implemented and throw after native initialization and argument checks. NAT information is 16 bytes with a 32-bit size prefix; the remaining fields are unresolved. ### Unknown function info @@ -25,6 +25,6 @@ ### Functional -- [sceNetCtlCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - the state comes from the host interfaces, as in `sceNetCtlGetState`: `IPOBTAINED` (3) while the host has a non-loopback IPv4 or IPv6 address, `DISCONNECTED` (1) otherwise; the event values follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/network/net_ctl_codes.h#L27-L29), and no other event is delivered. Each callback registered with `sceNetCtlRegisterCallback` is called on the checking thread with its registration argument when the state differs from the last one it received, so a new registration receives the current state at the next check. This edge-triggered model follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3648-L3661); [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/network/net_ctl_obj.cpp#L47-L57) delivers the state on every check instead, and neither is measured on hardware. KytyPS5 calls the callbacks under its lock; here they are called after the lock is released, from a copy of their slots, so a `sceNetCtlUnregisterCallback` or `sceNetCtlTerm` on another thread that returns while a check is in flight does not stop that check from calling the callback once more with its old argument. Callbacks registered with `sceNetCtlRegisterCallbackV6` keep their argument but receive no event, because no source says which call delivers them +- [sceNetCtlCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - the state comes from the host interfaces, as in `sceNetCtlGetState`: `IPOBTAINED` (3) while the host has a non-loopback IPv4 or IPv6 address, `DISCONNECTED` (1) otherwise; the event values follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/network/net_ctl_codes.h#L27-L29), and no other event is delivered. Each callback registered with `sceNetCtlRegisterCallback` is called on the checking thread with its registration argument when the state differs from the last one it received, so a new registration receives the current state at the next check. This edge-triggered model follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3648-L3661); [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/network/net_ctl_obj.cpp#L47-L57) delivers the state on every check instead, and neither is measured on hardware. KytyPS5 calls the callbacks under its lock; here they are called after the lock is released, from a copy of their slots, so a `sceNetCtlUnregisterCallback` on another thread that returns while a check is in flight does not stop that check from calling the callback once more with its old argument. Callbacks registered with `sceNetCtlRegisterCallbackV6` keep their argument but receive no event: native `sceNetCtlCheckCallback` also drains the IPv6 service queue, which the host backend does not model - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) finds the host's default route in `/proc/net/route` on Linux and, on macOS, in the routing table read with `sysctl(NET_RT_FLAGS, RTF_GATEWAY)`, where an interface-scoped default route ranks after the unscoped one - [sceNetCtlGetIfStat](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) throws on macOS: an unprivileged process reads the interface byte counters (`getifaddrs` and `sysctl(NET_RT_IFLIST2)` alike) reduced to their low 32 bits, so a total would wrap every 4 GiB From 3985ae95cb2d712d6a688178b1cfdd994cbf6846 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:54:35 +0300 Subject: [PATCH 368/523] fix(http): match native header parsing and uri decoding --- core/libs/prx/libSceHttp/src/Uri.cpp | 52 ++++++++++++++++------------ core/libs/tests/GuestHttp.cpp | 17 +++++++-- docs/dev/debt/libSceHttp.md | 2 +- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceHttp/src/Uri.cpp b/core/libs/prx/libSceHttp/src/Uri.cpp index 670a29e7eb..b03920909b 100644 --- a/core/libs/prx/libSceHttp/src/Uri.cpp +++ b/core/libs/prx/libSceHttp/src/Uri.cpp @@ -228,34 +228,40 @@ int APS5_VABI sceHttpUriEscape(char* out, size_t* require, size_t prepare, const return copyOut(escaped, out, require, prepare); } -int APS5_VABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) { - if (!in || (!out && !require)) return ERROR_INVALID_VALUE; +int APS5_VABI sceHttpUriUnescape(char* out, size_t* requiredSize, size_t capacity, const char* input) { + if (!input || (!out && !requiredSize)) return ERROR_INVALID_VALUE; - const auto hexDigit = [](char c) -> int { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; - return -1; + const std::string_view source(input); + const auto decode = [](const char* digits, unsigned int& value) { + const auto result = std::from_chars(digits, digits + 2, value, 16); + return result.ec == std::errc{} && result.ptr == digits + 2; }; - const std::string_view input(in); - std::string decoded; - for (size_t i = 0; i < input.size(); ++i) { - if (input[i] != '%') { - decoded.push_back(input[i]); - continue; + for (size_t index = 0; index < source.size(); ++index) { + if (source[index] != '%') continue; + unsigned int value = 0; + if (source.size() - index < 3 || !decode(source.data() + index + 1, value)) return ERROR_INVALID_URL; + index += 2; + } + + size_t written = 0; + for (size_t index = 0; index < source.size(); ++index) { + if (out && written >= capacity) return ERROR_OUT_OF_MEMORY; + char value = source[index]; + if (value == '%') { + unsigned int decoded = 0; + decode(source.data() + index + 1, decoded); + value = static_cast(decoded); + index += 2; } - if (input.size() - i < 3) return ERROR_INVALID_URL; - const int high = hexDigit(input[i + 1]); - const int low = hexDigit(input[i + 2]); - if (high < 0 || low < 0) return ERROR_INVALID_URL; - decoded.push_back(static_cast(high * 16 + low)); - i += 2; + if (out) out[written] = value; + ++written; } - if (out && prepare < decoded.size() + 1) { - std::memcpy(out, decoded.data(), std::min(prepare, decoded.size())); - return ERROR_OUT_OF_MEMORY; + if (out) { + if (written >= capacity) return ERROR_OUT_OF_MEMORY; + out[written] = '\0'; } - return copyOut(decoded, out, require, prepare); + if (requiredSize) *requiredSize = written + 1; + return 0; } int APS5_VABI sceHttpUriMerge(char* merged_url, const char* url, const char* relative_uri, size_t* require, size_t prepare, uint32_t option) { diff --git a/core/libs/tests/GuestHttp.cpp b/core/libs/tests/GuestHttp.cpp index 93eb0119d3..0e7dba5121 100644 --- a/core/libs/tests/GuestHttp.cpp +++ b/core/libs/tests/GuestHttp.cpp @@ -209,9 +209,9 @@ int main() { const std::size_t size = std::strlen(row[1]) + 1; Require(sceHttpUriUnescape(nullptr, &required, 0, row[0]) == 0 && required == size); std::memset(escaped, 'Z', sizeof(escaped)); - required = 77; - Require(sceHttpUriUnescape(escaped, &required, size - 1, row[0]) == outOfMemory && required == 77); - Require(std::string_view(escaped, size - 1) == row[1] && escaped[size - 1] == 'Z'); + required = 123; + Require(sceHttpUriUnescape(escaped, &required, size - 1, row[0]) == outOfMemory && required == 123); + Require(std::memcmp(escaped, row[1], size - 1) == 0 && escaped[size - 1] == 'Z'); Require(sceHttpUriUnescape(escaped, nullptr, size, row[0]) == 0); Require(Equal(escaped, row[1]) && escaped[size] == 'Z'); } @@ -231,6 +231,17 @@ int main() { Require(sceHttpUriUnescape(escaped, &required, sizeof(escaped), nullptr) == invalidValue); Require(required == 123 && Equal(escaped, "A/%20")); + for (const char* malformed : {"%", "%1", "%1g", "%gg", "%+1", "%-1", "prefix%4", "prefix%xy"}) { + std::memset(escaped, 'Z', sizeof(escaped)); + required = 123; + Require(sceHttpUriUnescape(escaped, &required, sizeof(escaped), malformed) == static_cast(0x80433060)); + Require(required == 123); + for (char value : escaped) Require(value == 'Z'); + } + std::memset(escaped, 'Z', sizeof(escaped)); + Require(sceHttpUriUnescape(escaped, &required, 2, "a%20b") == outOfMemory); + Require(required == 123 && escaped[0] == 'a' && escaped[1] == ' ' && escaped[2] == 'Z'); + char bytes[256], encodedBytes[766], decodedBytes[256]; for (std::size_t i = 1; i < 256; ++i) bytes[i - 1] = static_cast(i); bytes[255] = '\0'; diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 58871b5572..798393a60b 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -15,7 +15,7 @@ - [sceHttpParseResponseHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); the result, value bounds and error codes follow a payload process on a PS5 (firmware 13.60). Unmeasured there: a bare carriage return, and vertical tab or form feed around the value, which are skipped as blanks before it as before. A value end above `INT_MAX` throws instead of wrapping - [sceHttpParseStatusLine](../../../core/libs/prx/libSceHttp/src/Parse.cpp) (libSceHttp) - behaviour and error codes follow the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation of the PS4 library, not confirmed on a PS5 title; unlike it, no byte past `lineLen` is read - [sceHttpUriMerge](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); validation, size formula, absolute/relative split and path join follow the disassembly-like body in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp), not confirmed on a PS5 title or hardware. The base URL goes through our `sceHttpUriParse`, which removes dot segments from the base path, while the relative part is appended unswept; base query and fragment are dropped. The relative reference is only classified by its scheme and a leading `//`, and only references with `//` are validated; as in shadPS4, `//host/x` is returned unchanged instead of taking the base scheme, a reference with a scheme but no `//` (`mailto:x`) is joined onto the base directory, and `?q` and `#f` are appended to the base directory instead of the base path. -- [sceHttpUriUnescape](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); decoding and error codes follow a payload process on a PS5 (firmware 13.60). A buffer one byte short of the decoded size got the decoded bytes without a terminator and left `require` alone; a shorter buffer is assumed to get the first `prepare` bytes the same way +- [sceHttpUriUnescape](../../../core/libs/prx/libSceHttp/src/Uri.cpp) - the legacy SDK character whitelist is not selected by the host backend. Percent validation, decoding, partial writes on insufficient capacity and success-only size output follow the supplied PS5 module; arbitrary non-percent bytes follow its modern SDK path. - [sceHttpUriSweepPath](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - follows a payload process on a PS5 (firmware 13.60), which agrees with the [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp) reimplementation of the PS4 library: it is not RFC 3986 5.2.4, a path that doesn't start with `/` is copied unchanged and a trailing `.` or `..` is kept. Like the console, the byte at `srcSize - 1` is read as the `/` that ends a `..` segment, and `srcSize == 1` with a leading `/` writes `/`; whether it also ends a `.` segment was not measured, so a `.` there is still kept as before - [sceHttpSetResponseHeaderMaxSize, sceHttpRedirectCacheFlush, sceHttpsUnloadCert](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp) (PS4); no PS5 reference has them - [sceHttpSetRequestStatusCallback](../../../core/libs/prx/libSceHttp/Export.cpp) - the three-argument ABI follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp). Registrations and user data are retained and removed on request deletion; the offline backend never dispatches callbacks. Invalid ids return `0x80431100`, an uncited assumption, not a verified console error. Handle hierarchy, inheritance and deleted-handle validation remain incomplete; callback statuses and dispatch timing are unknown. From 7eac0fbbac59a79f244bac6e638d9928172e90ed Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 20 Oct 2026 23:55:55 +0300 Subject: [PATCH 369/523] fix(http): restore uri builder limits and output rules --- core/libs/prx/libSceHttp/src/Uri.cpp | 45 +++++++++++++++++----------- core/libs/tests/GuestHttp.cpp | 26 +++++++++++++++- docs/dev/debt/libSceHttp.md | 1 - 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceHttp/src/Uri.cpp b/core/libs/prx/libSceHttp/src/Uri.cpp index b03920909b..0f0e8d3660 100644 --- a/core/libs/prx/libSceHttp/src/Uri.cpp +++ b/core/libs/prx/libSceHttp/src/Uri.cpp @@ -181,31 +181,42 @@ int copyOut(const std::string& value, char* out, size_t* require, size_t prepare extern "C" { -int APS5_VABI sceHttpUriBuild(char* out, size_t* require, size_t prepare, const SceHttpUriElement* src_element, uint32_t option) { - if (!src_element) return ERROR_INVALID_URL; - if (!out && !require) return ERROR_INVALID_VALUE; +int APS5_VABI sceHttpUriBuild(char* out, size_t* requiredSize, size_t capacity, const SceHttpUriElement* element, uint32_t option) { + if (!element) return ERROR_INVALID_URL; + if (!out && !requiredSize) return ERROR_INVALID_VALUE; - auto field = [](const char* value) { return value ? std::string_view(value) : std::string_view(); }; - const std::string_view scheme = field(src_element->scheme); - const std::string_view username = option & URI_BUILD_WITH_USERNAME ? field(src_element->username) : std::string_view(); - const std::string_view password = option & URI_BUILD_WITH_PASSWORD ? field(src_element->password) : std::string_view(); - const std::string_view hostname = option & URI_BUILD_WITH_HOSTNAME ? field(src_element->hostname) : std::string_view(); + const auto field = [](const char* value, size_t limit) { + size_t length = 0; + if (value) while (length < limit && value[length] != '\0') ++length; + return value ? std::string_view(value, length) : std::string_view(); + }; + const auto scheme = option & (URI_BUILD_WITH_SCHEME | URI_BUILD_WITH_PORT) ? field(element->scheme, 32) : std::string_view(); + const auto username = option & URI_BUILD_WITH_USERNAME ? field(element->username, 256) : std::string_view(); + const auto password = option & URI_BUILD_WITH_PASSWORD ? field(element->password, 256) : std::string_view(); + const auto hostname = option & URI_BUILD_WITH_HOSTNAME ? field(element->hostname, 255) : std::string_view(); std::string uri; if (option & URI_BUILD_WITH_SCHEME && !scheme.empty()) uri.append(scheme).push_back(':'); - if (!src_element->opaque) uri.append("//"); + if (element->opaque != 1) uri.append("//"); uri.append(username); if (!password.empty()) uri.append(":").append(password); if (!username.empty() || !password.empty()) uri.push_back('@'); - if (hostname.find(':') != std::string_view::npos) uri.append("[").append(hostname).append("]"); - else uri.append(hostname); - if (option & URI_BUILD_WITH_PORT && src_element->port != 0 && src_element->port != defaultPort(scheme)) { - uri.append(":").append(std::to_string(src_element->port)); + uri.append(hostname); + if (option & URI_BUILD_WITH_PORT && element->port != 0) { + std::uint16_t port = 0; + if (scheme.size() >= 5 && equalsIgnoreCase(scheme.substr(0, 5), "https")) port = 443; + else if ((scheme.size() >= 4 && equalsIgnoreCase(scheme.substr(0, 4), "http")) || (scheme.size() >= 3 && equalsIgnoreCase(scheme.substr(0, 3), "ttp"))) port = 80; + if (element->port != port) uri.append(":").append(std::to_string(element->port)); } - if (option & URI_BUILD_WITH_PATH) uri.append(field(src_element->path)); - if (option & URI_BUILD_WITH_QUERY) uri.append(field(src_element->query)); - if (option & URI_BUILD_WITH_FRAGMENT) uri.append(field(src_element->fragment)); - return copyOut(uri, out, require, prepare); + if (option & URI_BUILD_WITH_PATH) uri.append(field(element->path, URI_MAX_LENGTH)); + if (option & URI_BUILD_WITH_QUERY) uri.append(field(element->query, URI_MAX_LENGTH)); + if (option & URI_BUILD_WITH_FRAGMENT) uri.append(field(element->fragment, URI_MAX_LENGTH)); + const size_t size = uri.size() + 1 + ((option & URI_BUILD_WITH_SCHEME) && element->scheme && scheme.empty()); + if (requiredSize) *requiredSize = size; + if (!out) return 0; + if (capacity < size) return ERROR_OUT_OF_MEMORY; + std::memcpy(out, uri.c_str(), uri.size() + 1); + return 0; } int APS5_VABI sceHttpUriEscape(char* out, size_t* require, size_t prepare, const char* in) { diff --git a/core/libs/tests/GuestHttp.cpp b/core/libs/tests/GuestHttp.cpp index 0e7dba5121..b340eb5a64 100644 --- a/core/libs/tests/GuestHttp.cpp +++ b/core/libs/tests/GuestHttp.cpp @@ -153,7 +153,7 @@ int main() { Require(sceHttpUriParse(&element, "http://[::1]:8080/index.html", pool, nullptr, sizeof(pool)) == 0); Require(Equal(element.hostname, "::1") && element.port == 8080 && Equal(element.path, "/index.html")); Require(sceHttpUriBuild(built, nullptr, sizeof(built), &element, buildAll) == 0); - Require(Equal(built, "http://[::1]:8080/index.html")); + Require(Equal(built, "http://::1:8080/index.html")); Require(sceHttpUriParse(&element, "mailto:someone@example.com", pool, nullptr, sizeof(pool)) == 0); Require(element.opaque != 0 && element.port == 0); @@ -177,6 +177,30 @@ int main() { Require(sceHttpUriBuild(built, nullptr, sizeof(built), nullptr, buildAll) == invalidUrl); Require(sceHttpUriBuild(nullptr, nullptr, 0, &element, buildAll) == invalidValue); + { + SceHttpUriElement bounded{}; + bounded.opaque = 2; + char scheme[33]; + char hostname[256]; + std::memset(scheme, 's', sizeof(scheme)); + std::memset(hostname, 'h', sizeof(hostname)); + bounded.scheme = scheme; + bounded.hostname = hostname; + char output[512]; + Require(sceHttpUriBuild(output, &required, sizeof(output), &bounded, 3) == 0); + Require(required == 32 + 1 + 2 + 255 + 1); + Require(output[32] == ':' && output[33] == '/' && output[34] == '/'); + Require(output[required - 1] == '\0'); + scheme[0] = '\0'; + Require(sceHttpUriBuild(output, &required, sizeof(output), &bounded, 1) == 0); + Require(required == 4 && Equal(output, "//")); + char prefix[] = "HTTPS-extra"; + bounded.scheme = prefix; + bounded.port = 443; + Require(sceHttpUriBuild(output, &required, sizeof(output), &bounded, 4) == 0); + Require(required == 3 && Equal(output, "//")); + } + char escaped[64]; Require(sceHttpUriEscape(nullptr, &required, 0, "a b/~\xC3\xA9") == 0); Require(required == std::strlen("a%20b%2F~%C3%A9") + 1); diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 798393a60b..b65ff78602 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -10,7 +10,6 @@ - [HTTP object options](../../../core/libs/prx/libSceHttp/src/Options.cpp) (libSceHttp) - a template starts with redirects followed, blocking calls and no `Accept-Encoding: gzip`, and a connection or request copies its parent's options when it is created. The signatures of the option calls, these defaults, the copying and the order of the checks (`BEFORE_INIT` first; the last `sceHttpTerm` ends every object's options) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/network/http.cpp) (PS4) and are not measured on a PS5. `sceHttpSetDefaultAcceptEncodingGZIPEnabled` sets the gzip value for templates created later in that context, where shadPS4 keeps one value for the library, and `sceHttpUnsetEpoll` takes a template or connection id, where shadPS4 looks only at requests. The options change no request, as none reaches the network - [sceHttpGetConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - connection and request diagnostic signatures are recovered; their outputs contain four and three 32-bit words, respectively. Field semantics remain incomplete. Epoll setters/getters distinguish a 32-bit epoll ID from a pointer-sized handle, and use a pointer-sized user argument. The supplied firmware getter dispatch does not handle the pointer-handle selector used by GetEpoll, so its intended successful behavior is not recoverable from this implementation. -- [sceHttpUriBuild](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) retains `//` for a non-opaque URI independently of the selected fields, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a8a70da595da2a96e0410309472ba4a7eb558144/src/core/libraries/network/http.cpp#L3551-L3558). Black Myth: Wukong builds its `Scheme` header with option 1 and expects `http://` or `https://`. Other partial field combinations follow the PS4 reference and remain unverified on PS5 hardware. - [sceHttpSetCookieEnabled](../../../core/libs/prx/libSceHttp/Export.cpp) - the two-argument signature and 0/1 enable values follow [VitaSDK](https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h) and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp); PPSA01521 passes 0 to disable cookies. Disabling succeeds because the offline HTTP backend has no cookie engine; enabling throws. Invalid enable values use the same error as `sceHttpSetInflateGZIPEnabled`. Like the existing offline HTTP setters, the handle is not validated; requests still fail with the network error. Console behavior is unverified - [sceHttpParseResponseHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); the result, value bounds and error codes follow a payload process on a PS5 (firmware 13.60). Unmeasured there: a bare carriage return, and vertical tab or form feed around the value, which are skipped as blanks before it as before. A value end above `INT_MAX` throws instead of wrapping - [sceHttpParseStatusLine](../../../core/libs/prx/libSceHttp/src/Parse.cpp) (libSceHttp) - behaviour and error codes follow the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation of the PS4 library, not confirmed on a PS5 title; unlike it, no byte past `lineLen` is read From 3fff17d08ec6dab71a920f59f264bf498960f105 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:04:30 +0300 Subject: [PATCH 370/523] fix(vrtracker2): restore memory modes and initialization layout --- core/libs/prx/libSceVrTracker2/Export.cpp | 62 +++++++++++++++++++---- core/libs/tests/GuestVrTracker2.cpp | 27 ++++++++++ docs/dev/debt/libSceVrTracker2.md | 5 +- 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index b850d89f71..3e2444861a 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -4,8 +4,43 @@ #include #include #include "prx/libc/include/General.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" -struct VrTracker2InitializeParameter; +struct VrTracker2InitializeParameter { + std::uint32_t size; + std::uint32_t mode; + std::int32_t threadPriorities[2]; + std::uint64_t threadAffinityMasks[2]; + void* workMemory; + std::uint32_t workMemorySize; + std::uint32_t workMemoryAlignment; + std::uint32_t option; + std::uint32_t coordinateSystem; + std::uint8_t disableOption; + std::int8_t optionMode; + std::uint8_t reserved[14]; +}; + +struct VrTracker2MemoryParameter { + std::uint32_t size; + std::uint32_t mode; + std::uint8_t reserved[24]; +}; + +struct VrTracker2MemoryRequirement { + std::uint32_t size; + std::uint32_t memorySize; + std::uint32_t memoryAlignment; + std::uint8_t reserved[20]; +}; + +static_assert(sizeof(VrTracker2InitializeParameter) == 72); +static_assert(offsetof(VrTracker2InitializeParameter, threadAffinityMasks) == 16); +static_assert(offsetof(VrTracker2InitializeParameter, workMemory) == 32); +static_assert(offsetof(VrTracker2InitializeParameter, coordinateSystem) == 52); +static_assert(offsetof(VrTracker2InitializeParameter, reserved) == 58); +static_assert(sizeof(VrTracker2MemoryParameter) == 32); +static_assert(sizeof(VrTracker2MemoryRequirement) == 32); struct VrTracker2ResultParameter; struct VrTracker2Result; struct VrTracker2CoordinateLocation; @@ -84,17 +119,22 @@ std::int32_t APS5_VABI sceVrTracker2UnregisterDevice(std::int32_t deviceHandle) return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -std::int32_t APS5_VABI sceVrTracker2QueryMemory(const void* parameter, void* requirement) { +std::int32_t APS5_VABI sceVrTracker2QueryMemory(const VrTracker2MemoryParameter* parameter, VrTracker2MemoryRequirement* requirement) { if (parameter == nullptr || requirement == nullptr) return VR_TRACKER2_ERROR_INVALID_ARGUMENT; - const auto* bytes = static_cast(parameter); - std::uint32_t size = 0; - std::memcpy(&size, bytes, sizeof size); - if (size != 0x20) return VR_TRACKER2_ERROR_INVALID_ARGUMENT; - for (std::size_t offset = sizeof size; offset < 0x20; ++offset) - if (bytes[offset] != 0) return VR_TRACKER2_ERROR_INVALID_ARGUMENT; - const std::uint32_t fields[3] = {0x20, 32u << 20, 64u << 10}; - std::memset(requirement, 0, 0x20); - std::memcpy(requirement, fields, sizeof fields); + if (parameter->size != sizeof(VrTracker2MemoryParameter)) return VR_TRACKER2_ERROR_INVALID_ARGUMENT; + std::uint32_t memorySize = 32u << 20; + if (parameter->mode == 1000 || parameter->mode == 1001) { + const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t sdkVersion; + std::memcpy(&sdkVersion, process + 0x10, sizeof(sdkVersion)); + if (sdkVersion < 0x1000000) return VR_TRACKER2_ERROR_INVALID_ARGUMENT; + memorySize = parameter->mode == 1000 ? 7u << 20 : 3u << 20; + } else if (parameter->mode != 0) { + return VR_TRACKER2_ERROR_INVALID_ARGUMENT; + } + for (const auto value : parameter->reserved) + if (value != 0) return VR_TRACKER2_ERROR_INVALID_ARGUMENT; + *requirement = {sizeof(VrTracker2MemoryRequirement), memorySize, 64u << 10, {}}; return SCE_OK; } diff --git a/core/libs/tests/GuestVrTracker2.cpp b/core/libs/tests/GuestVrTracker2.cpp index 7b43bc31d0..0bbe3a869e 100644 --- a/core/libs/tests/GuestVrTracker2.cpp +++ b/core/libs/tests/GuestVrTracker2.cpp @@ -1,4 +1,5 @@ #include "prx/libc/include/general/VabiMacros.hpp" +#include "ProcessParametersFixture.hpp" #include #include @@ -23,6 +24,8 @@ std::int32_t APS5_VABI sceVrTracker2Initialize(const VrTracker2InitializeParamet namespace { +alignas(16) std::array process{}; + constexpr std::int32_t NotInitialized = static_cast(0x8A740001u); constexpr std::int32_t InvalidArgument = static_cast(0x8A740006u); @@ -108,6 +111,30 @@ int main() { Require(sceVrTracker2QueryMemory(rejected.data(), requirement.data()) == InvalidArgument, "query reserved byte"); Require(AllEqual(requirement.data(), requirement.data() + 40, 0xAA), "query rejection leaves output"); } + const std::uint64_t processSize = process.size(); + const std::uint32_t magic = 0x4942524f; + std::memcpy(process.data(), &processSize, sizeof(processSize)); + std::memcpy(process.data() + 8, &magic, sizeof(magic)); + Require(ProcessParametersFixture::Install(process.data(), process.size()), "process parameters"); + for (const std::uint32_t version : {0u, 0xffffffu, 0x1000000u, 0x7000000u}) { + std::memcpy(process.data() + 0x10, &version, sizeof(version)); + for (const std::uint32_t mode : {1000u, 1001u}) { + std::memcpy(parameter.data() + 4, &mode, sizeof(mode)); + requirement.fill(0xAA); + const auto result = sceVrTracker2QueryMemory(parameter.data(), requirement.data()); + if (version < 0x1000000) { + Require(result == InvalidArgument, "query SDK restriction"); + Require(AllEqual(requirement.data(), requirement.data() + requirement.size(), 0xAA), "SDK rejection preserves output"); + } else { + Require(result == 0, "query alternate mode"); + std::uint32_t memorySize; + std::memcpy(&memorySize, requirement.data() + 4, sizeof(memorySize)); + Require(memorySize == (mode == 1000 ? 7u << 20 : 3u << 20), "query alternate memory size"); + Require(AllEqual(requirement.data() + 12, requirement.data() + 32, 0), "query alternate reserved fields"); + Require(AllEqual(requirement.data() + 32, requirement.data() + 40, 0xAA), "query alternate output boundary"); + } + } + } parameter[0] = 0x21; Require(sceVrTracker2QueryMemory(parameter.data(), requirement.data()) == InvalidArgument, "query size"); diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 72d3209092..69e3f5cd62 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -2,10 +2,9 @@ ### Silent stubs -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes; initialized tracking behavior is absent. Its uninitialized responses were measured on firmware 13.60 without a headset: most exports return 0x8A740001 without writes, while specific output/reset helpers clear buffers or write defaults. These helpers still lack complete signatures. Named export signatures and play-area output layouts are recovered; initialization and result fields need reconstruction. Anonymous exports remain grouped by their observed response rather than their individual contracts. +- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes; initialized tracking behavior is absent. Its uninitialized responses were measured on firmware 13.60 without a headset: most exports return 0x8A740001 without writes, while specific output/reset helpers clear buffers or write defaults. These helpers still lack complete signatures. Named export signatures and play-area output layouts are recovered; initialization option semantics and result fields need reconstruction. Anonymous exports remain grouped by their observed response rather than their individual contracts. ### Unknown function info -- [sceVrTracker2QueryMemory](../../../core/libs/prx/libSceVrTracker2/Export.cpp) (libSceVrTracker2, NID `TwqZnaIjWv4`) - accepts only a parameter whose first `uint32` is 0x20 and whose bytes 4 to 31 are zero, found by searching sizes 8 to 0x400 and setting each field and byte; it then writes 32 bytes, `uint32` 0x20, 32 MiB and 64 KiB followed by zeros, and returns 0, and otherwise returns 0x8A740006 without writing, as it does for a null argument. The meaning of the parameter's reserved bytes and of the written fields is assumed from their values -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - initialization (`6Jy73SRfG-o`) takes one parameter of 0x48 bytes. PPSA05666 passes the size 0x48 at offset 0, `uint32` 0x100 and 0x2BC at 0x8 and 0xC, `uint64` 0x1FFF at 0x10 and 0x18, a pointer to its work memory at 0x20, and the memory query's 32 MiB and 64 KiB as `uint32` at 0x28 and 0x2C, with the other bytes zero; the priority and CPU mask meanings of 0x8 to 0x1F are assumed from the values. Called with that parameter and 32 MiB of anonymous memory from a homebrew payload on a PS5 (firmware 13.60) with no PS VR2 headset, it did not return within a minute, so what it returns without a headset is unknown and it throws +- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - initialization (`6Jy73SRfG-o`) takes one parameter of 0x48 bytes. PPSA05666 passes the size 0x48 at offset 0, `uint32` 0x100 and 0x2BC at 0x8 and 0xC, `uint64` 0x1FFF at 0x10 and 0x18, a pointer to its work memory at 0x20, and the memory query's 32 MiB and 64 KiB as `uint32` at 0x28 and 0x2C, with the other bytes zero; thread setup confirms the priority and CPU mask meanings of 0x8 to 0x1F. The 72-byte layout also contains memory size/alignment at 0x28/0x2C, a 32-bit option at 0x30, coordinate system at 0x34 and two byte options at 0x38/0x39. The three option meanings remain unresolved. Called with that parameter and 32 MiB of anonymous memory from a homebrew payload on a PS5 (firmware 13.60) with no PS VR2 headset, it did not return within a minute, so what it returns without a headset is unknown and it throws - [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - eight exports throw. `6Jy73SRfG-o` (initialization) and `9R6W33Au4B4` return 0x8A740006 on the console for null arguments, for zeroed, 0xAA- and 0x01-filled buffers and for a size field from 8 to 0x400 in any of the first three arguments; initialization also returned it for the memory query's parameter with 32 MiB aligned to 64 KiB passed inside the parameter or as further arguments, so the parameter layout it accepts is unknown. `NBhWy+Gl0tY` returns 0x8A740006 for those buffers but crashes for null arguments; `jRmuXjsknsI` returns 0x8A740006 for null arguments and crashes for buffers, also when every 8-byte slot points at readable memory; `O8ZkTFtvUL4`, `940WPg+WwIk`, `oZDm1bynCaA` and `U1VYczwqukY` crash for both From 5b01f0555019b42b41348b314bf33d38a80a6baf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:04:31 +0300 Subject: [PATCH 371/523] fix(http): apply sdk character rules to uri decoding --- core/libs/prx/libSceHttp/src/Uri.cpp | 17 +++++++++++++++++ core/libs/tests/GuestHttp.cpp | 26 ++++++++++++++++++++++++++ docs/dev/debt/libSceHttp.md | 1 - 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceHttp/src/Uri.cpp b/core/libs/prx/libSceHttp/src/Uri.cpp index 0f0e8d3660..b1cc8db443 100644 --- a/core/libs/prx/libSceHttp/src/Uri.cpp +++ b/core/libs/prx/libSceHttp/src/Uri.cpp @@ -7,6 +7,7 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libSceHttp/src/HttpErrors.hpp" namespace { @@ -254,6 +255,22 @@ int APS5_VABI sceHttpUriUnescape(char* out, size_t* requiredSize, size_t capacit index += 2; } + if (!source.empty()) { + std::uint32_t sdkVersion = 0; + const auto* parameters = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::memcpy(&sdkVersion, parameters + 0x10, sizeof(sdkVersion)); + if (sdkVersion < 0x1000000) { + for (size_t index = 0; index < source.size(); ++index) { + const char value = source[index]; + if (value == '%') { + index += 2; + continue; + } + if (!isAsciiAlpha(value) && !isAsciiDigit(value) && value != '*' && value != '-' && value != '.' && value != '_' && value != '+') return ERROR_INVALID_URL; + } + } + } + size_t written = 0; for (size_t index = 0; index < source.size(); ++index) { if (out && written >= capacity) return ERROR_OUT_OF_MEMORY; diff --git a/core/libs/tests/GuestHttp.cpp b/core/libs/tests/GuestHttp.cpp index b340eb5a64..2d0060ef46 100644 --- a/core/libs/tests/GuestHttp.cpp +++ b/core/libs/tests/GuestHttp.cpp @@ -1,4 +1,6 @@ #include "SceTypes.hpp" +#include "ProcessParametersFixture.hpp" +#include #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -46,6 +48,8 @@ int APS5_VABI sceHttpSetAuthInfoCallback(int, HttpAuthInfoCallback, void*); int APS5_VABI sceHttpParseStatusLine(const char*, std::size_t, std::int32_t*, std::int32_t*, std::int32_t*, const char**, std::size_t*); } +alignas(16) static std::array processParameters{}; + static void Require(bool value) { if (!value) std::abort(); } static bool Equal(const char* left, const char* right) { return std::strcmp(left, right) == 0; } @@ -98,6 +102,28 @@ static void APS5_VABI RequestStatusCallback(int, int, void* argument) { } int main() { + const std::uint64_t processSize = processParameters.size(); + const std::uint32_t processMagic = 0x4942524f; + std::uint32_t sdkVersion = 0x1000000; + std::memcpy(processParameters.data(), &processSize, sizeof(processSize)); + std::memcpy(processParameters.data() + 8, &processMagic, sizeof(processMagic)); + std::memcpy(processParameters.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(ProcessParametersFixture::Install(processParameters.data(), processParameters.size())); + for (const std::uint32_t version : {0u, 0xffffffu, 0x1000000u}) { + std::memcpy(processParameters.data() + 16, &version, sizeof(version)); + for (unsigned value = 1; value < 256; ++value) { + if (value == '%') continue; + const char input[]{static_cast(value), 0}; + char output[]{'x', 'x'}; + std::size_t required = 123; + const bool allowed = version >= 0x1000000 || (value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z') || (value >= '0' && value <= '9') || value == '*' || value == '-' || value == '.' || value == '_' || value == '+'; + Require(sceHttpUriUnescape(output, &required, sizeof(output), input) == (allowed ? 0 : static_cast(0x80433060))); + Require(allowed ? output[0] == input[0] && output[1] == 0 && required == 2 : output[0] == 'x' && output[1] == 'x' && required == 123); + } + char output[2]{}; + Require(sceHttpUriUnescape(output, nullptr, sizeof(output), "%7E") == 0 && output[0] == '~'); + } + std::memcpy(processParameters.data() + 16, &sdkVersion, sizeof(sdkVersion)); int callbackCount = 0; const auto request = sceHttpCreateRequest2(1, "GET", "/", 0); Require(sceHttpSetRequestStatusCallback(request, RequestStatusCallback, &callbackCount) == 0); diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index b65ff78602..3df2c812ff 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -14,7 +14,6 @@ - [sceHttpParseResponseHeader](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); the result, value bounds and error codes follow a payload process on a PS5 (firmware 13.60). Unmeasured there: a bare carriage return, and vertical tab or form feed around the value, which are skipped as blanks before it as before. A value end above `INT_MAX` throws instead of wrapping - [sceHttpParseStatusLine](../../../core/libs/prx/libSceHttp/src/Parse.cpp) (libSceHttp) - behaviour and error codes follow the [shadPS4](https://github.com/shadps4-emu/shadPS4) reimplementation of the PS4 library, not confirmed on a PS5 title; unlike it, no byte past `lineLen` is read - [sceHttpUriMerge](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Net/Http.cs); validation, size formula, absolute/relative split and path join follow the disassembly-like body in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp), not confirmed on a PS5 title or hardware. The base URL goes through our `sceHttpUriParse`, which removes dot segments from the base path, while the relative part is appended unswept; base query and fragment are dropped. The relative reference is only classified by its scheme and a leading `//`, and only references with `//` are validated; as in shadPS4, `//host/x` is returned unchanged instead of taking the base scheme, a reference with a scheme but no `//` (`mailto:x`) is joined onto the base directory, and `?q` and `#f` are appended to the base directory instead of the base path. -- [sceHttpUriUnescape](../../../core/libs/prx/libSceHttp/src/Uri.cpp) - the legacy SDK character whitelist is not selected by the host backend. Percent validation, decoding, partial writes on insufficient capacity and success-only size output follow the supplied PS5 module; arbitrary non-percent bytes follow its modern SDK path. - [sceHttpUriSweepPath](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - follows a payload process on a PS5 (firmware 13.60), which agrees with the [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp) reimplementation of the PS4 library: it is not RFC 3986 5.2.4, a path that doesn't start with `/` is copied unchanged and a trailing `.` or `..` is kept. Like the console, the byte at `srcSize - 1` is read as the `/` that ends a `..` segment, and `srcSize == 1` with a leading `/` writes `/`; whether it also ends a `.` segment was not measured, so a `.` there is still kept as before - [sceHttpSetResponseHeaderMaxSize, sceHttpRedirectCacheFlush, sceHttpsUnloadCert](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp) (PS4); no PS5 reference has them - [sceHttpSetRequestStatusCallback](../../../core/libs/prx/libSceHttp/Export.cpp) - the three-argument ABI follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp). Registrations and user data are retained and removed on request deletion; the offline backend never dispatches callbacks. Invalid ids return `0x80431100`, an uncited assumption, not a verified console error. Handle hierarchy, inheritance and deleted-handle validation remain incomplete; callback statuses and dispatch timing are unknown. From 4a57f814cc0f60e68c799ee05f08798a81961eed Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:04:31 +0300 Subject: [PATCH 372/523] docs(netctl): clarify remaining callback behavior --- docs/dev/debt/libSceNetCtl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 5c6533f6ff..5e377b9392 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -25,6 +25,6 @@ ### Functional -- [sceNetCtlCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - the state comes from the host interfaces, as in `sceNetCtlGetState`: `IPOBTAINED` (3) while the host has a non-loopback IPv4 or IPv6 address, `DISCONNECTED` (1) otherwise; the event values follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/network/net_ctl_codes.h#L27-L29), and no other event is delivered. Each callback registered with `sceNetCtlRegisterCallback` is called on the checking thread with its registration argument when the state differs from the last one it received, so a new registration receives the current state at the next check. This edge-triggered model follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3648-L3661); [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/network/net_ctl_obj.cpp#L47-L57) delivers the state on every check instead, and neither is measured on hardware. KytyPS5 calls the callbacks under its lock; here they are called after the lock is released, from a copy of their slots, so a `sceNetCtlUnregisterCallback` on another thread that returns while a check is in flight does not stop that check from calling the callback once more with its old argument. Callbacks registered with `sceNetCtlRegisterCallbackV6` keep their argument but receive no event: native `sceNetCtlCheckCallback` also drains the IPv6 service queue, which the host backend does not model +- [sceNetCtlCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) - host address changes stand in for the native service event queues. IPv4 callbacks receive connected/disconnected transitions and an initial event; intermediate connection states and errors are not represented. Native CheckCallback drains both IPv4 and IPv6 queues, but the host backend never delivers IPv6 events. Dispatch copies slots before invoking callbacks, so unregistering concurrently may leave one callback in flight; native dispatch holds its registration lock. Initialization, slot limits, invalid/deleted IDs and the no-op Term follow the supplied module. - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) finds the host's default route in `/proc/net/route` on Linux and, on macOS, in the routing table read with `sysctl(NET_RT_FLAGS, RTF_GATEWAY)`, where an interface-scoped default route ranks after the unscoped one - [sceNetCtlGetIfStat](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) throws on macOS: an unprivileged process reads the interface byte counters (`getifaddrs` and `sysctl(NET_RT_IFLIST2)` alike) reduced to their low 32 bits, so a total would wrap every 4 GiB From 6aae4e187f4f1d3066accb8575843e2ac2ab0d4a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:05:48 +0300 Subject: [PATCH 373/523] fix(vrtracker2): restore anonymous utility export signatures --- core/libs/prx/libSceVrTracker2/Export.cpp | 36 ++++++++++++++++++----- docs/dev/debt/libSceVrTracker2.md | 4 +-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 3e2444861a..55924bd629 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -41,6 +41,28 @@ static_assert(offsetof(VrTracker2InitializeParameter, coordinateSystem) == 52); static_assert(offsetof(VrTracker2InitializeParameter, reserved) == 58); static_assert(sizeof(VrTracker2MemoryParameter) == 32); static_assert(sizeof(VrTracker2MemoryRequirement) == 32); +struct VrTracker2CalibrationResult; +struct VrTracker2TelemetryData; +struct VrTracker2PoseParameter; +struct VrTracker2PoseResult; + +struct VrTracker2MeshRequirement { + std::uint32_t vertexCount; + std::uint32_t indexCount; + std::uint32_t vertexDataSize; + std::uint32_t indexDataSize; +}; + +struct VrTracker2Pose { + float position[3]; + std::uint8_t padding[4]; + float orientation[4]; +}; + +static_assert(sizeof(VrTracker2MeshRequirement) == 16); +static_assert(sizeof(VrTracker2Pose) == 32); +static_assert(offsetof(VrTracker2Pose, orientation) == 16); + struct VrTracker2ResultParameter; struct VrTracker2Result; struct VrTracker2CoordinateLocation; @@ -280,43 +302,43 @@ std::int32_t APS5_VABI VrTracker2ClearWord(void* output) { } APS5_EXPORT("jRmuXjsknsI", VrTracker2Unknown01); -std::int32_t APS5_VABI VrTracker2Unknown01() { +std::int32_t APS5_VABI VrTracker2Unknown01(VrTracker2CalibrationResult* result, std::uint32_t capacity) { NotImplemented_nid_no_patch("jRmuXjsknsI"); return 0; } APS5_EXPORT("NBhWy+Gl0tY", VrTracker2Unknown02); -std::int32_t APS5_VABI VrTracker2Unknown02() { +std::int32_t APS5_VABI VrTracker2Unknown02(VrTracker2MeshRequirement* requirement, std::uint32_t format) { NotImplemented_nid_no_patch("NBhWy+Gl0tY"); return 0; } APS5_EXPORT("9R6W33Au4B4", VrTracker2Unknown03); -std::int32_t APS5_VABI VrTracker2Unknown03() { +std::int32_t APS5_VABI VrTracker2Unknown03(std::uint32_t mode, void* output, std::size_t capacity, std::size_t* writtenSize) { NotImplemented_nid_no_patch("9R6W33Au4B4"); return 0; } APS5_EXPORT("O8ZkTFtvUL4", VrTracker2Unknown04); -std::int32_t APS5_VABI VrTracker2Unknown04() { +std::int32_t APS5_VABI VrTracker2Unknown04(const VrTracker2ResultParameter* parameter, VrTracker2Result* result) { NotImplemented_nid_no_patch("O8ZkTFtvUL4"); return 0; } APS5_EXPORT("940WPg+WwIk", VrTracker2Unknown05); -std::int32_t APS5_VABI VrTracker2Unknown05() { +std::int32_t APS5_VABI VrTracker2Unknown05(VrTracker2Pose* pose, std::uint64_t coordinateId) { NotImplemented_nid_no_patch("940WPg+WwIk"); return 0; } APS5_EXPORT("oZDm1bynCaA", VrTracker2Unknown06); -std::int32_t APS5_VABI VrTracker2Unknown06() { +std::int32_t APS5_VABI VrTracker2Unknown06(const VrTracker2TelemetryData* data) { NotImplemented_nid_no_patch("oZDm1bynCaA"); return 0; } APS5_EXPORT("U1VYczwqukY", VrTracker2Unknown07); -std::int32_t APS5_VABI VrTracker2Unknown07() { +std::int32_t APS5_VABI VrTracker2Unknown07(const VrTracker2PoseParameter* parameter, VrTracker2PoseResult* result) { NotImplemented_nid_no_patch("U1VYczwqukY"); return 0; } diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 69e3f5cd62..a292162167 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -6,5 +6,5 @@ ### Unknown function info -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - initialization (`6Jy73SRfG-o`) takes one parameter of 0x48 bytes. PPSA05666 passes the size 0x48 at offset 0, `uint32` 0x100 and 0x2BC at 0x8 and 0xC, `uint64` 0x1FFF at 0x10 and 0x18, a pointer to its work memory at 0x20, and the memory query's 32 MiB and 64 KiB as `uint32` at 0x28 and 0x2C, with the other bytes zero; thread setup confirms the priority and CPU mask meanings of 0x8 to 0x1F. The 72-byte layout also contains memory size/alignment at 0x28/0x2C, a 32-bit option at 0x30, coordinate system at 0x34 and two byte options at 0x38/0x39. The three option meanings remain unresolved. Called with that parameter and 32 MiB of anonymous memory from a homebrew payload on a PS5 (firmware 13.60) with no PS VR2 headset, it did not return within a minute, so what it returns without a headset is unknown and it throws -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - eight exports throw. `6Jy73SRfG-o` (initialization) and `9R6W33Au4B4` return 0x8A740006 on the console for null arguments, for zeroed, 0xAA- and 0x01-filled buffers and for a size field from 8 to 0x400 in any of the first three arguments; initialization also returned it for the memory query's parameter with 32 MiB aligned to 64 KiB passed inside the parameter or as further arguments, so the parameter layout it accepts is unknown. `NBhWy+Gl0tY` returns 0x8A740006 for those buffers but crashes for null arguments; `jRmuXjsknsI` returns 0x8A740006 for null arguments and crashes for buffers, also when every 8-byte slot points at readable memory; `O8ZkTFtvUL4`, `940WPg+WwIk`, `oZDm1bynCaA` and `U1VYczwqukY` crash for both +- [sceVrTracker2Initialize](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the 72-byte parameter layout is recovered, including thread priorities, CPU affinity masks, work memory, size/alignment and coordinate system. Meanings of the 32-bit option at 0x30 and byte options at 0x38/0x39 remain unresolved; initialization remains unimplemented. +- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the seven anonymous throwing exports now have their consumed argument slots restored. Calibration output (`jRmuXjsknsI`) uses a 32-bit capacity with 608/1216-byte variants; its fields remain unresolved. Mesh requirements (`NBhWy+Gl0tY`) contain four 32-bit counts/sizes. The mode/buffer/capacity/size export (`9R6W33Au4B4`) uses 64-bit sizes. Result and pose parameters still need full layouts; the telemetry input (`oZDm1bynCaA`) spans 7024 bytes. Their bodies remain unimplemented. From e1d99c4631cca6d26be6bb7e5bfbd37a6f5595d3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:08:10 +0300 Subject: [PATCH 374/523] fix(vrtracker2): define versioned tracking query parameters --- core/libs/prx/libSceVrTracker2/Export.cpp | 28 ++++++++++++++++++++++- docs/dev/debt/libSceVrTracker2.md | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 55924bd629..61ccdcc8fd 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -63,7 +63,33 @@ static_assert(sizeof(VrTracker2MeshRequirement) == 16); static_assert(sizeof(VrTracker2Pose) == 32); static_assert(offsetof(VrTracker2Pose, orientation) == 16); -struct VrTracker2ResultParameter; +struct VrTracker2ResultParameter { + std::uint32_t size; + std::int32_t deviceHandle; + std::uint32_t reserved; + std::uint8_t padding[4]; + std::uint64_t timestamp; + std::uint32_t reserved2; + std::uint8_t padding2[4]; + std::uint64_t userTag; + std::uint32_t reserved3[2]; +}; + +struct VrTracker2LegacyResultParameter { + std::uint32_t size; + std::int32_t deviceHandle; + std::uint32_t reserved; + std::uint8_t padding[4]; + std::uint64_t timestamp; + std::uint32_t userTag; + std::uint8_t padding2[4]; +}; + +static_assert(sizeof(VrTracker2ResultParameter) == 48); +static_assert(offsetof(VrTracker2ResultParameter, timestamp) == 16); +static_assert(offsetof(VrTracker2ResultParameter, userTag) == 32); +static_assert(sizeof(VrTracker2LegacyResultParameter) == 32); +static_assert(offsetof(VrTracker2LegacyResultParameter, userTag) == 24); struct VrTracker2Result; struct VrTracker2CoordinateLocation; diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index a292162167..ce9f91ccc8 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -7,4 +7,4 @@ ### Unknown function info - [sceVrTracker2Initialize](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the 72-byte parameter layout is recovered, including thread priorities, CPU affinity masks, work memory, size/alignment and coordinate system. Meanings of the 32-bit option at 0x30 and byte options at 0x38/0x39 remain unresolved; initialization remains unimplemented. -- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the seven anonymous throwing exports now have their consumed argument slots restored. Calibration output (`jRmuXjsknsI`) uses a 32-bit capacity with 608/1216-byte variants; its fields remain unresolved. Mesh requirements (`NBhWy+Gl0tY`) contain four 32-bit counts/sizes. The mode/buffer/capacity/size export (`9R6W33Au4B4`) uses 64-bit sizes. Result and pose parameters still need full layouts; the telemetry input (`oZDm1bynCaA`) spans 7024 bytes. Their bodies remain unimplemented. +- [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the seven anonymous throwing exports now have their consumed argument slots restored. Calibration output (`jRmuXjsknsI`) uses a 32-bit capacity with 608/1216-byte variants; its fields remain unresolved. Mesh requirements (`NBhWy+Gl0tY`) contain four 32-bit counts/sizes. The mode/buffer/capacity/size export (`9R6W33Au4B4`) uses 64-bit sizes. The 32/48-byte result parameters are recovered; result output and pose parameters still need full layouts; the telemetry input (`oZDm1bynCaA`) spans 7024 bytes. Their bodies remain unimplemented. From ac2d5f49cbea08d0fbb7cfbdd7e6eed3b6853728 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:08:11 +0300 Subject: [PATCH 375/523] fix(hmd2): restore reprojection submission arguments --- core/libs/prx/libSceHmd2/Export.cpp | 35 ++++++++++++++++++++++++++--- docs/dev/debt/libSceHmd2.md | 2 ++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index 15afad777e..373c4d1032 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -16,6 +16,35 @@ namespace Hmd2 { struct ReprojectionStatus; struct ReprojectionOutput; struct ReprojectionInitializeParameter; +struct ReprojectionLayer; +struct ReprojectionBufferedLayer; + +struct ReprojectionOption { + std::uint32_t size; + std::uint32_t flags; + std::uint32_t reserved[30]; +}; + +struct ReprojectionParameter { + const ReprojectionLayer* layers; + std::int32_t layerCount; + std::uint8_t padding[4]; + const ReprojectionOption* option; +}; + +struct ReprojectionBufferedParameter { + const ReprojectionBufferedLayer* layers; + std::int32_t layerCount; + std::uint8_t padding[4]; + const ReprojectionOption* option; +}; + +static_assert(sizeof(ReprojectionOption) == 128); +static_assert(sizeof(ReprojectionParameter) == 24); +static_assert(sizeof(ReprojectionBufferedParameter) == 24); +static_assert(offsetof(ReprojectionParameter, option) == 16); +static_assert(offsetof(ReprojectionBufferedParameter, option) == 16); + struct MirroringParameter { void* workMemory; @@ -270,17 +299,17 @@ int APS5_VABI sceHmd2ReprojectionSetMirroringOption(const Hmd2::MirroringOption* return 0; } -int APS5_VABI sceHmd2ReprojectionSetParam() { +int APS5_VABI sceHmd2ReprojectionSetParam(std::uint32_t bufferSetId, std::uint64_t frameId, const Hmd2::ReprojectionParameter* parameter, std::uint64_t reserved) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2ReprojectionSetParamWithBuffer() { +int APS5_VABI sceHmd2ReprojectionSetParamWithBuffer(std::uint32_t bufferSetId, std::uint64_t frameId, const Hmd2::ReprojectionBufferedParameter* parameter, std::uint64_t reserved) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceHmd2ReprojectionSetRenderConfig() { +int APS5_VABI sceHmd2ReprojectionSetRenderConfig(std::uint64_t flags, std::uint64_t reserved, std::uint64_t reserved2) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index 41deb3e6e3..d3f462f050 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -86,3 +86,5 @@ - [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte parameter and a 56-byte or 24-byte output. Object selection uses a 120-byte parameter and 56-byte result whose fields remain incomplete. Its initialization and memory-query structures are recovered, including the 32-bit object count and memory size, memory pointer and 64-bit alignment. Calibration data descriptors contain a pointer and signed 32-bit size; calibration status contains two 32-bit selectors and three floats. The underlying calibration blob formats remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. - [libSceHmd2 reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - frame identifiers and predicted display times are 64-bit. Event setters accept a kernel event queue and 32-bit event ID, consumed by sceKernelTriggerUserEvent. Mirroring uses two memory pointers and a separate 32-byte mode/reserved option. Status and current-output queries write 104 and 32 bytes; those field layouts and the reprojection initialization fields remain incomplete. Positional-reprojection mode is a byte with values 0 through 5, not a boolean. + +- [Reprojection parameter submission](../../../core/libs/prx/libSceHmd2/Export.cpp) - SetParam and SetParamWithBuffer take a 32-bit buffer-set ID, 64-bit frame ID, parameter pointer and reserved register slot. The parameter contains a layer pointer, signed count and optional 128-byte flags/reserved structure; plain and buffered layer strides are 328 and 472 bytes. Layer union fields remain incomplete. SetRenderConfig consumes three 64-bit slots; only bits 0 and 32 of the first can be nonzero. These exports remain unimplemented. From 462d45adf099622f3380f52f931ed30f5b1952b4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:09:21 +0300 Subject: [PATCH 376/523] fix(npwebapi2): clear the complete memory statistics record --- core/libs/prx/libSceNpWebApi2/Export.cpp | 6 +++++- core/libs/tests/GuestNpWebApi2.cpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpWebApi2/Export.cpp b/core/libs/prx/libSceNpWebApi2/Export.cpp index ea33832e83..daef37aa53 100644 --- a/core/libs/prx/libSceNpWebApi2/Export.cpp +++ b/core/libs/prx/libSceNpWebApi2/Export.cpp @@ -3,6 +3,7 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include +#include #include #include #include @@ -27,6 +28,8 @@ struct NpWebApi2MemoryPoolStats { int32_t reserved; }; +static_assert(sizeof(NpWebApi2MemoryPoolStats) == 32); + static std::mutex g_poolsMutex; static std::unordered_map g_pools; @@ -120,7 +123,8 @@ int APS5_VABI sceNpWebApi2GetMemoryPoolStats(int libraryContextId, NpWebApi2Memo const auto pool = g_pools.find(libraryContextId); if (pool == g_pools.end()) return SCE_NP_WEBAPI2_ERROR_INVALID_CONTEXT; if (!stats) return 0; - *stats = {pool->second, 0, 0, 0}; + std::memset(stats, 0, sizeof(*stats)); + stats->pool_size = pool->second; return 0; } diff --git a/core/libs/tests/GuestNpWebApi2.cpp b/core/libs/tests/GuestNpWebApi2.cpp index eac1a6321f..7bd8327e2b 100644 --- a/core/libs/tests/GuestNpWebApi2.cpp +++ b/core/libs/tests/GuestNpWebApi2.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include struct NpWebApi2MemoryPoolStats { @@ -37,8 +38,11 @@ int main() { const int context = sceNpWebApi2Initialize(1, 0x20000); const int other = sceNpWebApi2Initialize(1, 0x8000); Require(context > 0 && other > 0 && context != other); - NpWebApi2MemoryPoolStats stats{1, 1, 1, 1}; + NpWebApi2MemoryPoolStats stats; + std::memset(&stats, 0xa5, sizeof(stats)); Require(sceNpWebApi2GetMemoryPoolStats(context, &stats) == 0); + const auto* statsBytes = reinterpret_cast(&stats); + for (std::size_t index = sizeof(std::size_t); index < sizeof(stats); ++index) Require(statsBytes[index] == 0); Require(stats.pool_size == 0x20000 && stats.max_inuse_size == 0 && stats.current_inuse_size == 0 && stats.reserved == 0); Require(sceNpWebApi2GetMemoryPoolStats(other, &stats) == 0 && stats.pool_size == 0x8000); Require(sceNpWebApi2GetMemoryPoolStats(context, nullptr) == 0); From 82f6f998834424edb84759b0d2c93d766f44159f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:10:03 +0300 Subject: [PATCH 377/523] fix(hmd2): define gaze query and foveated result layouts --- core/libs/prx/libSceHmd2/Export.cpp | 17 +++++++++++++++-- docs/dev/debt/libSceHmd2.md | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index 373c4d1032..d348fcefe5 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -59,9 +59,22 @@ struct MirroringOption { static_assert(sizeof(MirroringParameter) == 16); static_assert(sizeof(MirroringOption) == 32); -struct GazeResultParameter; +struct GazeResultParameter { + std::uint32_t size; + std::int32_t handle; + std::uint8_t reserved[8]; +}; struct GazeResult; -struct FoveatedRenderingResult; +struct FoveatedRenderingResult { + std::uint32_t size; + std::int32_t status; + float direction[3]; + std::uint8_t reserved[4]; +}; + +static_assert(sizeof(GazeResultParameter) == 16); +static_assert(sizeof(FoveatedRenderingResult) == 24); +static_assert(offsetof(FoveatedRenderingResult, direction) == 8); struct GazeCalibrationData { void* data; std::int32_t size; diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index d3f462f050..36737886e2 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -83,7 +83,7 @@ - [Hmd2IndexZeroCheckSecondThenZero](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console returns 0 for a first argument of 0 and `0x8a72100f` for 1 to 5, 8, 16, 255 and -1; a null second argument returns `0x8a720008` even with a nonzero first argument. Its third to sixth arguments answered `0x8a72100f` when null with a nonzero first argument, which does not separate them from the first-argument check, so they are not looked at: `wiv8+7ogrq0`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [FBUEhH9tQ1A](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - left unimplemented: before initialization it writes the 32-bit value 9 through its second argument and returns 0 when its first argument points at zeroed memory, but returns `0x8a720001` without writing when that memory holds 0xAA bytes, and its fourth to sixth arguments answer `0x8a720001` when null, so the first argument is an in-out structure whose fields are not known -- [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte parameter and a 56-byte or 24-byte output. Object selection uses a 120-byte parameter and 56-byte result whose fields remain incomplete. Its initialization and memory-query structures are recovered, including the 32-bit object count and memory size, memory pointer and 64-bit alignment. Calibration data descriptors contain a pointer and signed 32-bit size; calibration status contains two 32-bit selectors and three floats. The underlying calibration blob formats remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. +- [libSceHmd2 gaze exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - gaze argument registers and input/output directions are recovered. Result queries take a 16-byte size/handle parameter. The 24-byte foveated result contains size, status and a three-float direction; its trailing four bytes are untouched. The 56-byte full result still needs its eye-field semantics. Object selection uses a 120-byte parameter and 56-byte result whose fields remain incomplete. Its initialization and memory-query structures are recovered, including the 32-bit object count and memory size, memory pointer and 64-bit alignment. Calibration data descriptors contain a pointer and signed 32-bit size; calibration status contains two 32-bit selectors and three floats. The underlying calibration blob formats remain incomplete. Raw gaze input is mutable. CalibrationPoint is three floats passed by value in two SIMD registers; SetIpdOffset takes a byte and a float. - [libSceHmd2 reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - frame identifiers and predicted display times are 64-bit. Event setters accept a kernel event queue and 32-bit event ID, consumed by sceKernelTriggerUserEvent. Mirroring uses two memory pointers and a separate 32-byte mode/reserved option. Status and current-output queries write 104 and 32 bytes; those field layouts and the reprojection initialization fields remain incomplete. Positional-reprojection mode is a byte with values 0 through 5, not a boolean. From 9047c23198eb41c7cc3db444631ba0198bb0400d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:12:25 +0300 Subject: [PATCH 378/523] fix(audioin): match native hq and async open validation --- core/libs/prx/libSceAudioIn/Export.cpp | 20 +++++----- core/libs/tests/GuestAudioIn.cpp | 51 ++++++++++++-------------- docs/dev/debt/libSceAudioIn.md | 6 +-- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/core/libs/prx/libSceAudioIn/Export.cpp b/core/libs/prx/libSceAudioIn/Export.cpp index 68fa257250..3f4d0837fb 100644 --- a/core/libs/prx/libSceAudioIn/Export.cpp +++ b/core/libs/prx/libSceAudioIn/Export.cpp @@ -17,12 +17,12 @@ constexpr int AUDIO_IN_ERROR_INVALID_HANDLE = static_cast(0x80260101); constexpr int AUDIO_IN_ERROR_INVALID_SIZE = static_cast(0x80260102); constexpr int AUDIO_IN_ERROR_INVALID_FREQ = static_cast(0x80260103); constexpr int AUDIO_IN_ERROR_INVALID_TYPE = static_cast(0x80260104); +constexpr int AUDIO_IN_ERROR_INVALID_POINTER = static_cast(0x80260105); constexpr int AUDIO_IN_ERROR_INVALID_PARAM = static_cast(0x80260106); constexpr int AUDIO_IN_ERROR_PORT_FULL = static_cast(0x80260107); constexpr int AUDIO_IN_ERROR_BUSY = static_cast(0x8026010A); constexpr int AUDIO_IN_SILENT_STATE_DEVICE_NONE = 1; constexpr std::uint32_t MAX_QUEUED_BLOCKS = 4; -constexpr std::uint32_t MAX_ASYNC_LEN = 384; using Clock = std::chrono::steady_clock; @@ -175,18 +175,18 @@ int32_t APS5_VABI sceAudioInClose(int32_t handle) { return 0; } -int32_t APS5_VABI sceAudioInHqOpen(int32_t user_id, uint32_t type, uint32_t index, uint32_t len, uint32_t freq, uint32_t param) { - (void)user_id; - if (const int error = checkArguments(type, index, len, freq); error != 0) return error; - if (len != 128) return AUDIO_IN_ERROR_INVALID_SIZE; +int32_t APS5_VABI sceAudioInHqOpen(int32_t userId, uint32_t type, uint32_t index, uint32_t len, uint32_t freq, uint32_t param) { if (freq != 48000) return AUDIO_IN_ERROR_INVALID_FREQ; + if (len != 128) return AUDIO_IN_ERROR_INVALID_SIZE; + if (param != 2) return AUDIO_IN_ERROR_INVALID_PARAM; + if (const int error = checkArguments(type, index, len, freq); error != 0) return error; return openPort(len, freq, param); } -int32_t APS5_VABI sceAudioInAsyncOpen(int32_t user_id, uint32_t type, uint32_t index, uint32_t len, uint32_t freq, uint32_t param) { - (void)user_id; - if (const int error = checkArguments(type, index, len, freq); error != 0 && error != AUDIO_IN_ERROR_INVALID_SIZE) return error; - if (len != 128 && len != 256 && len != MAX_ASYNC_LEN) return AUDIO_IN_ERROR_INVALID_SIZE; +int32_t APS5_VABI sceAudioInAsyncOpen(int32_t userId, uint32_t type, uint32_t index, uint32_t len, uint32_t freq, uint32_t param) { + if (freq != 48000) return AUDIO_IN_ERROR_INVALID_FREQ; + if (len != 128) return AUDIO_IN_ERROR_INVALID_SIZE; + if (const int error = checkArguments(type, index, len, freq); error != 0) return error; return openPort(len, freq, param); } @@ -197,7 +197,7 @@ int32_t APS5_VABI sceAudioInIsPadEmbeddedMic(int32_t handle) { } int32_t APS5_VABI sceAudioInGetGain(int32_t handle, float* gain) { - if (!gain) throw std::runtime_error("sceAudioInGetGain: null gain"); + if (!gain) return AUDIO_IN_ERROR_INVALID_POINTER; *gain = 1.0f; std::lock_guard lock(g_mutex); return find(handle) ? 0 : AUDIO_IN_ERROR_INVALID_HANDLE; diff --git a/core/libs/tests/GuestAudioIn.cpp b/core/libs/tests/GuestAudioIn.cpp index 124bc9a880..05b2eac559 100644 --- a/core/libs/tests/GuestAudioIn.cpp +++ b/core/libs/tests/GuestAudioIn.cpp @@ -36,16 +36,6 @@ static bool ReportsNotImplemented(const char* name, TCall call) { return false; } -template -static bool ThrowsRuntimeError(TCall call) { - try { - call(); - } catch (const std::runtime_error&) { - return true; - } - return false; -} - static void SetEnvironment(const char* name, const std::string& value) { #ifdef _WIN32 _putenv_s(name, value.c_str()); @@ -63,6 +53,7 @@ void TestValidation() { constexpr int invalidSize = static_cast(0x80260102); constexpr int invalidFreq = static_cast(0x80260103); constexpr int invalidType = static_cast(0x80260104); + constexpr int invalidPointer = static_cast(0x80260105); constexpr int invalidParam = static_cast(0x80260106); Require(sceAudioInOpen(user, 2, 0, 256, 48000, 2) == invalidType); Require(sceAudioInOpen(user, 0, 1, 256, 48000, 2) == invalidParam); @@ -80,37 +71,40 @@ void TestValidation() { Require(sceAudioInAsyncOpen(user, 0, 0, 385, 48000, 2) == invalidSize); Require(sceAudioInAsyncOpen(user, 0, 0, 200, 48000, 2) == invalidSize); Require(sceAudioInAsyncOpen(user, 0, 0, 256, 44100, 2) == invalidFreq); - Require(sceAudioInAsyncOpen(user, 0, 0, 256, 48000, 0) == invalidParam); + Require(sceAudioInAsyncOpen(user, 0, 0, 256, 48000, 0) == invalidSize); Require(sceAudioInHqOpen(user, 0, 0, 256, 48000, 2) == invalidSize); Require(sceAudioInHqOpen(user, 0, 0, 128, 16000, 2) == invalidFreq); for (const int badHandle : {-1, 0, 1, 9}) { float gain = -2.5f; Require(sceAudioInGetGain(badHandle, &gain) == invalidHandle && gain == 1.0f); Require(sceAudioInGetRerouteCount(badHandle) == invalidHandle); - Require(ThrowsRuntimeError([badHandle] { sceAudioInGetGain(badHandle, nullptr); })); + Require(sceAudioInGetGain(badHandle, nullptr) == invalidPointer); } - int port = sceAudioInAsyncOpen(user, 0, 0, 384, 48000, 2); + int port = sceAudioInAsyncOpen(user, 0, 0, 128, 48000, 2); float gain = 0.0f; Require(port > 0 && sceAudioInGetGain(port, &gain) == 0 && gain == 1.0f && sceAudioInGetRerouteCount(port) == 0); - Require(ThrowsRuntimeError([port] { sceAudioInGetGain(port, nullptr); })); + Require(sceAudioInGetGain(port, nullptr) == invalidPointer); Require(sceAudioInClose(port) == 0); gain = 0.25f; Require(sceAudioInGetGain(port, &gain) == invalidHandle && gain == 1.0f && sceAudioInGetRerouteCount(port) == invalidHandle); port = sceAudioInOpen(user, 0, 0, 256, 48000, 0x10); gain = -1.0f; Require(port > 0 && sceAudioInGetGain(port, &gain) == 0 && gain == 1.0f && sceAudioInGetRerouteCount(port) == 0 && sceAudioInClose(port) == 0); - port = sceAudioInHqOpen(user, 0, 0, 128, 48000, 0x10); - gain = 2.0f; - Require(port > 0 && sceAudioInGetGain(port, &gain) == 0 && gain == 1.0f && sceAudioInGetRerouteCount(port) == 0 && sceAudioInClose(port) == 0); - port = sceAudioInAsyncOpen(user, 0, 0, 384, 48000, 0x10); + for (const std::uint32_t format : {0u, 1u, 3u, 0x10u, 0x11u, 0x12u}) Require(sceAudioInHqOpen(user, 0, 0, 128, 48000, format) == invalidParam); + for (const std::uint32_t size : {0u, 127u, 129u, 256u, 384u}) Require(sceAudioInAsyncOpen(user, 0, 0, size, 48000, 2) == invalidSize); + Require(sceAudioInHqOpen(user, 2, 1, 256, 16000, 0) == invalidFreq); + Require(sceAudioInAsyncOpen(user, 2, 1, 256, 16000, 0) == invalidFreq); + Require(sceAudioInHqOpen(user, 2, 1, 256, 48000, 0) == invalidSize); + Require(sceAudioInAsyncOpen(user, 2, 1, 256, 48000, 0) == invalidSize); + port = sceAudioInAsyncOpen(user, 0, 0, 128, 48000, 0x10); gain = 0.5f; Require(port > 0 && sceAudioInGetGain(port, &gain) == 0 && gain == 1.0f && sceAudioInGetRerouteCount(port) == 0 && sceAudioInClose(port) == 0); Require(ReportsNotImplemented("sceAudioInIsPadEmbeddedMic", [] { sceAudioInIsPadEmbeddedMic(1); })); } -void InputSound(int handle, std::vector& block) { +void InputSound(int handle, std::vector& block, int samples = 256) { for (int attempt = 0; attempt < 200; ++attempt) { - Require(sceAudioInInput(handle, block.data()) == 256); + Require(sceAudioInInput(handle, block.data()) == samples); if (std::any_of(block.begin(), block.end() - 1, [](std::int16_t sample) { return sample != 0; })) return; } Require(false); @@ -138,9 +132,10 @@ void TestCapture() { Require(sceAudioInInput(handle, block.data()) == 256); for (std::size_t i = 0; i < 256 * 2; ++i) Require(block[i] == 0); Require(sceAudioInClose(handle) == 0); - const int async = sceAudioInAsyncOpen(user, 0, 0, 256, 48000, 2); + const int async = sceAudioInAsyncOpen(user, 0, 0, 128, 48000, 2); Require(async == handle && sceAudioInGetSilentState(async) == 0); - InputSound(async, block); + block.assign(128 * 2 + 1, 0x5555); + InputSound(async, block, 128); Require(std::equal(block.begin(), block.end() - 1, recorded.begin()) && block.back() == 0x5555); Require(sceAudioInClose(async) == 0); std::filesystem::remove(path); @@ -165,7 +160,7 @@ void TestNoDevice() { Require(buffer[256 * 8] == 0xCC); Require(sceAudioInInput(handle, nullptr) == 0); const int hq = sceAudioInHqOpen(user, 0, 0, 128, 48000, 2); - const int async = sceAudioInAsyncOpen(user, 1, 0, 256, 16000, 0x11); + const int async = sceAudioInAsyncOpen(user, 1, 0, 128, 48000, 0x11); Require(hq > 0 && async > 0 && hq != handle && async != handle && hq != async); Require(sceAudioInGetSilentState(hq) == 1 && sceAudioInGetSilentState(async) == 1); std::fill(buffer.begin(), buffer.end(), 0xCC); @@ -173,13 +168,13 @@ void TestNoDevice() { for (std::size_t i = 0; i < 128 * 4; ++i) Require(buffer[i] == 0); Require(buffer[128 * 4] == 0xCC); std::fill(buffer.begin(), buffer.end(), 0xCC); - Require(sceAudioInInput(async, buffer.data()) == 256); - for (std::size_t i = 0; i < 256 * 4; ++i) Require(buffer[i] == 0); - Require(buffer[256 * 4] == 0xCC); + Require(sceAudioInInput(async, buffer.data()) == 128); + for (std::size_t i = 0; i < 128 * 4; ++i) Require(buffer[i] == 0); + Require(buffer[128 * 4] == 0xCC); Require(sceAudioInInput(async, nullptr) == 0); for (int port = 3; port < 8; ++port) Require(sceAudioInOpen(user, 1, 0, 128, 48000, 1) > 0); Require(sceAudioInOpen(user, 1, 0, 128, 48000, 1) == portFull); - Require(sceAudioInHqOpen(user, 1, 0, 128, 48000, 1) == portFull); + Require(sceAudioInHqOpen(user, 1, 0, 128, 48000, 2) == portFull); Require(sceAudioInAsyncOpen(user, 1, 0, 128, 48000, 1) == portFull); Require(sceAudioInClose(hq) == 0); Require(sceAudioInClose(hq) == invalidHandle); @@ -187,7 +182,7 @@ void TestNoDevice() { Require(sceAudioInAsyncOpen(user, 0, 0, 128, 48000, 2) == hq); Require(sceAudioInClose(async) == 0); Require(sceAudioInInput(async, buffer.data()) == invalidHandle); - Require(sceAudioInHqOpen(user, 1, 0, 128, 48000, 0x12) == async); + Require(sceAudioInHqOpen(user, 1, 0, 128, 48000, 2) == async); Require(sceAudioInClose(handle) == 0); Require(sceAudioInClose(handle) == invalidHandle); Require(sceAudioInInput(handle, buffer.data()) == invalidHandle); diff --git a/docs/dev/debt/libSceAudioIn.md b/docs/dev/debt/libSceAudioIn.md index 04f555333b..5f2f688821 100644 --- a/docs/dev/debt/libSceAudioIn.md +++ b/docs/dev/debt/libSceAudioIn.md @@ -6,7 +6,7 @@ ### Unknown function info -- [sceAudioInIsPadEmbeddedMic](../../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - name from its NID (`X+4jdIS75P0`); signature `int32_t(int32_t handle)` from the PPSA26344 call site in #2355; the result's meaning is unconfirmed, so it still throws rather than invent one +- [sceAudioInIsPadEmbeddedMic](../../../core/libs/prx/libSceAudioIn/Export.cpp) - the PS5 module uses the low handle byte as a port index and returns whether its resolved device type equals 8, propagating negative device errors. Host capture devices have no corresponding controller-microphone classification; the protected throwing body remains unchanged. - [sceAudioInDeviceIdOpen](../../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - unknown signature - [sceAudioInDeviceIdAsyncOpen](../../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - unknown signature - [sceAudioInSetUsbGain](../../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - unknown signature @@ -42,5 +42,5 @@ ### Functional -- [sceAudioInHqOpen, sceAudioInAsyncOpen](../../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) open the same port as `sceAudioInOpen`, so `sceAudioInInput`, `sceAudioInGetSilentState` and `sceAudioInClose` accept their handles. The signatures agree across [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/fc5d2cc219c43c8c8f354153e7b3d0c04a5d506e/src/core/libraries/audio/audioin.cpp), [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Audio/AudioIn.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/c86c8b4d9333a766cc8c4495f89f893bf35e6199/src/libs/audio.cpp). HqOpen is limited to 128 samples at 48 kHz, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/c86c8b4d9333a766cc8c4495f89f893bf35e6199/src/libs/audio.cpp) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Audio/AudioIn.cs) describe the higher-quality path: a longer grain returns the invalid-size error and a lower rate the invalid-frequency error. AsyncOpen also accepts the 384-sample grain, the largest [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Audio/AudioIn.cs) documents for the low-latency path, and returns the invalid-size error for every other size; the console's shorter average return interval of an async port's blocks is not modelled, so like a synchronous port `sceAudioInInput` paces one grain at a time. Param 0x10 opens a float mono port like 0x11, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/c86c8b4d9333a766cc8c4495f89f893bf35e6199/src/libs/audio.cpp) accepts both, while SharpProspero documents only 0x11. Host capture pacing and console behavior remain unverified. -- [sceAudioInGetGain, sceAudioInGetRerouteCount](../../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - signatures and semantics follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Audio/AudioIn.cs#L92-L110). `sceAudioInGetGain` writes the unattenuated linear gain `1.0f` into `*gain` before validating `handle`, returning `0` for an open port and `0x80260101` (`SCE_AUDIO_IN_ERROR_INVALID_HANDLE`) otherwise; a null `gain` pointer throws because the console result is unverified, and host or system input attenuation is not modelled. `sceAudioInGetRerouteCount` returns `0` for an open port and `0x80260101` otherwise, as each port opens its SDL capture device once and capture-device hot-swapping is not modelled. +- [sceAudioInHqOpen, sceAudioInAsyncOpen](../../../core/libs/prx/libSceAudioIn/Export.cpp) - PS5 wrapper validation requires 48000 Hz followed by exactly 128 samples for both calls; HqOpen then requires format 2. The host uses the same SDL capture path for both, so native asynchronous scheduling and high-quality processing are not reproduced. Common port validation, device selection and host capture pacing remain incomplete. +- [sceAudioInGetGain, sceAudioInGetRerouteCount](../../../core/libs/prx/libSceAudioIn/Export.cpp) - GetGain writes 1.0f before handle validation and returns 0x80260105 for a null output, as confirmed by the PS5 module. Native handle bit checks and uninitialized/inactive port errors differ from the host port table. Native reroute counters follow device reassignment; the host opens each SDL device once and reports zero. From f7a7be1a271fb673069808af310e2416e8180965 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 00:14:05 +0300 Subject: [PATCH 379/523] fix(audioout2): restore optional attribute outputs and errors --- .../prx/libSceAudioOut/src/AudioOut2User.cpp | 8 ++++---- .../tests/AudioOut2PortLayouts.cpp | 16 +++++++++++----- docs/dev/debt/libSceAudioOut.md | 6 +++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp index a8c42717bf..1a514be246 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp @@ -32,13 +32,13 @@ int APS5_VABI sceAudioOut2UserDestroy(AudioOut2UserHandle handle) { } int APS5_VABI sceAudioOut2UserGetSupportedAttributes(AudioOut2UserHandle handle, uint32_t* contextAttributes, uint32_t* portAttributes) { - if (!contextAttributes || !portAttributes) - throw std::runtime_error("sceAudioOut2UserGetSupportedAttributes: null output"); + if (handle == 0 || handle == std::numeric_limits::max()) return static_cast(0x80268010); + if (!contextAttributes && !portAttributes) return static_cast(0x80268001); std::lock_guard lock(g_usersLock); if (!g_users.contains(handle)) throw std::runtime_error("sceAudioOut2UserGetSupportedAttributes: unknown user handle"); - *contextAttributes = 0; - *portAttributes = (1u << 0) | (1u << 1); + if (contextAttributes) *contextAttributes = 0; + if (portAttributes) *portAttributes = (1u << 0) | (1u << 1); return 0; } diff --git a/core/libs/prx/libSceAudioOut/tests/AudioOut2PortLayouts.cpp b/core/libs/prx/libSceAudioOut/tests/AudioOut2PortLayouts.cpp index 1cad3a7422..7fcfa50334 100644 --- a/core/libs/prx/libSceAudioOut/tests/AudioOut2PortLayouts.cpp +++ b/core/libs/prx/libSceAudioOut/tests/AudioOut2PortLayouts.cpp @@ -77,11 +77,17 @@ void TestUserAttributes() { } Require(threw && context.value == 0xFFFFFFFFu && port.value == 0xFFFFFFFFu); }; - rejects(first, true, false); - rejects(first, false, true); - rejects(first, true, true); - rejects(0, false, false); - rejects(std::numeric_limits::max(), false, false); + context.value = port.value = 0xFFFFFFFFu; + Require(sceAudioOut2UserGetSupportedAttributes(first, nullptr, &port.value) == 0); + Require(context.value == 0xFFFFFFFFu && port.value == 3); + Require(sceAudioOut2UserGetSupportedAttributes(first, &context.value, nullptr) == 0 && context.value == 0); + context.value = port.value = 0xFFFFFFFFu; + Require(sceAudioOut2UserGetSupportedAttributes(first, nullptr, nullptr) == static_cast(0x80268001)); + for (const AudioOut2UserHandle handle : {AudioOut2UserHandle{0}, std::numeric_limits::max()}) { + Require(sceAudioOut2UserGetSupportedAttributes(handle, &context.value, &port.value) == static_cast(0x80268010)); + Require(sceAudioOut2UserGetSupportedAttributes(handle, nullptr, nullptr) == static_cast(0x80268010)); + Require(context.value == 0xFFFFFFFFu && port.value == 0xFFFFFFFFu); + } Require(sceAudioOut2UserDestroy(first) == 0); rejects(first, false, false); std::thread([&] { diff --git a/docs/dev/debt/libSceAudioOut.md b/docs/dev/debt/libSceAudioOut.md index dd5aa7cfb3..1e7b243b57 100644 --- a/docs/dev/debt/libSceAudioOut.md +++ b/docs/dev/debt/libSceAudioOut.md @@ -4,11 +4,11 @@ - [sceAudioOut2ContextAdvance and sceAudioOut2ContextPush](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) - with an SDL device, Advance snapshots each port's PCM and gain; Push consumes completed grains in order and a blocking Push waits for Advance when none is ready. Inferred from PPSA26358, which rewrites the same 512-frame, 12-channel float buffer on a producer thread and pushes on another thread. Console buffer ownership, snapshot timing and empty-queue errors are unverified. Completed grains remain queued until Push consumes them, so memory use grows with an unconsumed PCM backlog. The console backlog limit is unverified. A nonblocking Push without a completed grain throws; this is a runtime restriction, not a console measurement. Context destruction concurrent with active calls remains unsupported. Without an SDL device the existing wall-clock path remains. -- [sceAudioOut2UserGetSupportedAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp) (libSceAudioOut) - the user handle and two uint32 output pointers follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs) and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. The masks describe this runtime: no context attributes and port attributes PCM (bit 0) and gain (bit 1). Null outputs and unknown or destroyed user handles throw; console validation order and error values are unknown. Users are tracked for this query, but port ownership and busy-user destruction are not implemented. +- [sceAudioOut2UserGetSupportedAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp) - the PS5 module confirms a pointer-sized user handle and two optional uint32 outputs. Handles 0/-1 return 0x80268010 before checking outputs; two null outputs return 0x80268001. Host masks still describe only implemented PCM/gain support, unlike native SDK- and device-dependent masks. Unknown or destroyed host handles throw; native arbitrary handles are pointers and cannot be validated by the host registry. Port ownership and busy-user destruction remain unimplemented. - [sceAudioOutExOpen, sceAudioOutExConfigureOutput, sceAudioOutExClose](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - the HDMI bitstream calls follow [PS5-Audio-Passthrough-Research](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/docs/FINDINGS.md#L31-L80), which disassembles firmware 12.70's libSceAudioOut and logs the calls on a console. `sceAudioOutExOpen(user, mode)` returns `0x80260015` for a mode above 10 or outside mask `0x61F`; otherwise it opens a port of internal type 6 in S16 stereo, 256 frames at 48000 Hz for modes 0, 1, 2 and 9 and 1024 frames at 192000 Hz for modes 3, 4 and 10. `sceAudioOutExConfigureOutput(control, flags, mode, target, option)` returns `0x80268009` when `control` is not 0, takes modes 0 to 10 and 0xFF and targets 1 to 3 and 0xFF, and returned 0 on the console; `sceAudioOutExClose` returned 0. The argument registers match [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L26-L28) and PPSA99051's [bitstream_out.cpp](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/sample-app/src/platform/ps5/bitstream_out.cpp#L29-L32). Audio reaches the host through SDL as PCM and the host has no HDMI bitstream output, so an Ex port has no host device: `sceAudioOutOutput` paces it at its own rate and drops its IEC 61937 bursts, and `sceAudioOutExConfigureOutput` changes nothing. Nonzero `flags` or `option`, other modes or targets, `sceAudioOutExClose` of a handle that is not an Ex port, and `sceAudioOutGetPortState` on an Ex port throw, since the console's answers are unknown - [sceAudioOutExGetMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) reports a sink without audio descriptors: it zeroes the buffer and returns 0, as `sceAudioOutSysGetHdmiMonitorInfo` does, since audio reaches the host through SDL as PCM and there is no EDID to describe. The arguments `(0, 0, info, size)` and the descriptor count as a 32-bit value at +0x98, with 8-byte descriptors from +0xA0, follow [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L62-L117) (PPSA99505), which passes a 0x400-byte buffer and read that layout from one TV; with no descriptor listed it decodes the audio itself instead of opening a bitstream port. The meaning of the first two arguments and the structure's size are unknown: nonzero first or second arguments and sizes below 0xA0, which cannot hold the count, throw, and every byte of the declared size is written as 0. A null buffer returns `0x80260004` like the other AudioOut calls; the console's code is not measured - [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - the two 32-bit arguments and selector validation match the system library: selector 0 accepts outputs 0 through 2; selector 255 accepts outputs 1 and 2. No third latency argument exists. The host has no system 3D rendering stage, so the call still does not apply latency and does not reproduce the system initialization and output-availability checks. -- [sceAudioOut2MasteringInit](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) - flag values unknown; only 0 is accepted. Whether the console reports an error for a repeated `sceAudioOut2MasteringInit` or for `sceAudioOut2MasteringTerm` without a matching initialization is unknown; both return 0. PPSA01289 creates the audio device of its next world before it destroys the previous one, so it calls Init twice before Term twice, and only logs a non-zero result +- [sceAudioOut2MasteringInit](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) - native initialization requires AudioOut2 initialization (0x80268006 otherwise), returns success immediately when already initialized, and otherwise rejects nonzero flags with 0x80268201. Term returns 0x80268203 without mastering initialization and clears the mastering state after a successful service call. The protected Init body remains unchanged; host Term still returns success without this lifecycle or native mastering allocations. - [sceAudioOut2MasteringGetState and sceAudioOut2MasteringSetParam](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) - the state layout, the states ids 1 (default) and 2 (V2), the descriptor ids `0x01010001`, `0x01020001` and `0x01010003` and the errors `0x80268201` and `0x80268204` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. GetState zeroes the states and writes the two descriptors; with no mastering stage the compressor and limiter values stay zero. SetParam accepts any parameter set and records nothing, as KytyPS5 does; a null parameter returns `0x80268201`, the mastering invalid-API-parameter error KytyPS5 returns for a null state, which is an inference rather than a measurement. The meaning of `output` and `user` is unknown and both are ignored - [sceAudioOut2SetSystemDebugState](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - records the debug state id in the trace and returns 0, as [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp) does; the host has no system mix to apply it to. The debug state ids are unknown, so every id is accepted, and a null param returns `0x80260502` like the other AudioOut2 calls. Not measured on a console - [sceAudioOut2GetSpeakerArrayCoefficients](../../../core/libs/prx/libSceAudioOut/src/AudioOut2SpeakerArray.cpp) (libSceAudioOut) - validates the handle and the coefficient count like the ambisonics call, then writes the front pair at full gain and silence for every other speaker, ignoring the position, spread, height awareness and downmix spread radius: [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp) and [prosper](https://github.com/mattias800/prosper/blob/main/prosper/src/hle/audio/hle_audio.cpp) both return that pair. The console's VBAP gains over the array's directions are not measured @@ -110,7 +110,7 @@ ### Functional -- [sceAudioOut2EnableChat](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) takes no arguments and returns 0, as declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs#L460-L464) ("Allows chat audio alongside the application's own"). The host mixes no chat audio and AudioOut2 has no chat port, so the call only records that chat audio is allowed. Whether the console fails it without a chat session, when repeated or before `sceAudioOut2Initialize` is unknown. [sceAudioOut2DisableChat](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) is the counterpart, imported by PPSA18216: it takes no arguments, returns 0 and records that chat audio is no longer allowed; its signature is not confirmed by a console measurement, and whether the console fails it without a chat session or when repeated is unknown. +- [sceAudioOut2EnableChat and sceAudioOut2DisableChat](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) - both PS5 exports take no arguments, return 0x80268006 before AudioOut2 initialization, and invoke the system chat service with selector 3. A negative service result maps to 0x80268000, otherwise they return zero. The host only records a local enabled flag, without initialization validation or a chat service; repeated-call and no-session behavior therefore still depends on the missing service. - [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - the two 32-bit arguments and selector validation match the system library: selector 0 accepts outputs 0 through 2; selector 255 accepts outputs 1 and 2. No third latency argument exists. The host has no system 3D rendering stage, so the call still does not apply latency and does not reproduce the system initialization and output-availability checks. - [AudioOut2 contexts](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) with an SDL device and no `sceAudioOut2ContextAdvance` calls read a grain's port buffers at the next push, at least 100 us after the grain was pushed, and play the mix behind a 40 ms cushion that the queue level leaves out. When the hardware reads the buffers is not known. The timing follows a title whose mixer fills a buffer after pushing it, reuses it two pushes later, and may still be writing it when the next push follows within microseconds. The last grain pushed before a title stops pushing is never read or played. - [sceAudioOut2ContextGetQueueLevel](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) with an SDL device counts the grains that `sceAudioOut2ContextAdvance` completed and no push has consumed as queued, together with the pushed grains beyond the 40 ms cushion, up to `queue_depth`. Whether the console counts them is unknown. Pushes that fill the cushion leave the level at 0, so a title that renders as many grains as the level leaves room for and pushes them on another thread rendered a full queue again after each of those pushes: PPSA23226's Wwise sink passes the available count to the sound engine as the number of frames to render, advances once per frame and pushes on a separate thread, and exceeded 16 completed grains. Counted, the completed grains keep such a title at most `queue_depth` grains ahead of its pushes. From b5d626bc218984d1682135ad60fbfc92b5adfb79 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:19:50 +0300 Subject: [PATCH 380/523] fix(audioout): enforce native monitor buffer validation --- core/libs/prx/libSceAudioOut/src/AudioOut.cpp | 29 +++++--------- .../tests/AudioOutExMonitorInfo.cpp | 39 ++++++------------- .../tests/AudioOutSysHdmiMonitorInfo.cpp | 15 +++---- docs/dev/debt/libSceAudioOut.md | 4 +- 4 files changed, 31 insertions(+), 56 deletions(-) diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp index 41c2ad30a8..9faeaef957 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp @@ -40,7 +40,7 @@ static constexpr std::uint64_t DRAIN_TIMEOUT_US = 200000; static constexpr std::uint64_t DRAIN_SLEEP_US = 1000; static constexpr int MONITOR_TYPE_HDMI = 1; static constexpr std::uint32_t HDMI_MONITOR_INFO_SIZE = 0x180; -static constexpr std::uint32_t EX_MONITOR_INFO_MIN_SIZE = 0xA0; +static constexpr std::uint32_t EX_MONITOR_INFO_SIZE = 0x400; static constexpr std::uint32_t EX_OPEN_MODE_MAX = 10; static constexpr std::uint32_t EX_OPEN_MODES = 0x61F; static constexpr std::uint32_t EX_CONFIGURE_MODE_DEFAULT = 0xFF; @@ -635,15 +635,10 @@ int APS5_VABI sceAudioOutSysConfigureOutput(int type, std::uint64_t flags, std:: } int APS5_VABI sceAudioOutSysGetHdmiMonitorInfo(int type, void* info, std::uint32_t size) { - if (type != MONITOR_TYPE_HDMI) { - throw std::runtime_error("sceAudioOutSysGetHdmiMonitorInfo: monitor type " + std::to_string(type) + " not supported"); - } - if (size != HDMI_MONITOR_INFO_SIZE) { - throw std::runtime_error("sceAudioOutSysGetHdmiMonitorInfo: size " + std::to_string(size) + " not supported"); - } - if (info == nullptr) { - return -2144993276; - } + if (info == nullptr) return static_cast(0x80260004); + if (size != HDMI_MONITOR_INFO_SIZE) return static_cast(0x80260006); + if (type != MONITOR_TYPE_HDMI && type != 0x10) return static_cast(0x8026000C); + if (type == 0x10) throw std::runtime_error("sceAudioOutSysGetHdmiMonitorInfo: monitor type 16 not supported"); std::memset(info, 0, HDMI_MONITOR_INFO_SIZE); return 0; } @@ -703,16 +698,10 @@ int APS5_VABI sceAudioOutExConfigureOutput(int control, std::uint64_t flags, std } int APS5_VABI sceAudioOutExGetMonitorInfo(int type, int index, void* info, std::uint32_t size) { - if (type != 0 || index != 0) { - throw std::runtime_error("sceAudioOutExGetMonitorInfo: monitor " + std::to_string(type) + ", " + std::to_string(index) + " not supported"); - } - if (size < EX_MONITOR_INFO_MIN_SIZE) { - throw std::runtime_error("sceAudioOutExGetMonitorInfo: size " + std::to_string(size) + " not supported"); - } - if (info == nullptr) { - return -2144993276; - } - std::memset(info, 0, size); + if (type != 0) return static_cast(0x80260003); + if (info == nullptr) return static_cast(0x80260004); + if (size != EX_MONITOR_INFO_SIZE) return static_cast(0x80260006); + std::memset(info, 0, EX_MONITOR_INFO_SIZE); return 0; } diff --git a/core/libs/prx/libSceAudioOut/tests/AudioOutExMonitorInfo.cpp b/core/libs/prx/libSceAudioOut/tests/AudioOutExMonitorInfo.cpp index fecdf065e1..6fcced4ffb 100644 --- a/core/libs/prx/libSceAudioOut/tests/AudioOutExMonitorInfo.cpp +++ b/core/libs/prx/libSceAudioOut/tests/AudioOutExMonitorInfo.cpp @@ -5,8 +5,6 @@ #include #include #include -#include -#include extern "C" { int APS5_VABI sceAudioOutExGetMonitorInfo(int, int, void*, std::uint32_t); @@ -21,28 +19,16 @@ static void Require(bool value, const char* message) { namespace { constexpr std::uint32_t titleSize = 0x400; -constexpr std::uint32_t minimumSize = 0xA0; constexpr std::size_t descriptorCountOffset = 0x98; constexpr std::size_t guardSize = 16; constexpr std::uint8_t untouched = 0xA5; constexpr int invalidPointer = static_cast(0x80260004); -bool Throws(int type, int index, std::uint32_t size, const std::string& expected) { - std::array info{}; - try { - sceAudioOutExGetMonitorInfo(type, index, info.data(), size); - } catch (const std::runtime_error& error) { - if (std::string(error.what()) == expected) return true; - std::fprintf(stderr, "threw: %s\n", error.what()); - return false; - } - return false; -} - -void CheckEmpty(std::uint32_t size) { +void CheckEmpty(int index) { + constexpr auto size = titleSize; std::array buffer{}; buffer.fill(untouched); - Require(sceAudioOutExGetMonitorInfo(0, 0, buffer.data(), size) == 0, "the monitor query must succeed"); + Require(sceAudioOutExGetMonitorInfo(0, index, buffer.data(), size) == 0, "the monitor query must succeed"); for (std::uint32_t i = 0; i < size; i++) { Require(buffer[i] == 0, "the monitor info must describe a sink without audio descriptors"); } @@ -57,15 +43,14 @@ void CheckEmpty(std::uint32_t size) { } int main() { - CheckEmpty(titleSize); - CheckEmpty(minimumSize); - Require(sceAudioOutExGetMonitorInfo(0, 0, nullptr, titleSize) == invalidPointer, - "a null buffer must report an invalid pointer"); - Require(Throws(0, 0, minimumSize - 1, "sceAudioOutExGetMonitorInfo: size 159 not supported"), - "a buffer without room for the descriptor count must throw"); - Require(Throws(1, 0, titleSize, "sceAudioOutExGetMonitorInfo: monitor 1, 0 not supported"), - "a nonzero first argument must throw"); - Require(Throws(0, 1, titleSize, "sceAudioOutExGetMonitorInfo: monitor 0, 1 not supported"), - "a nonzero second argument must throw"); + for (const int index : {0, 1, -1, 100}) CheckEmpty(index); + std::array buffer; + buffer.fill(untouched); + for (const std::uint32_t size : {0u, 0xA0u, titleSize - 1, titleSize + 1, 0xffffffffu}) { + Require(sceAudioOutExGetMonitorInfo(0, 0, buffer.data(), size) == static_cast(0x80260006), "exact size required"); + Require(sceAudioOutExGetMonitorInfo(0, 0, nullptr, size) == invalidPointer, "pointer checked before size"); + Require(sceAudioOutExGetMonitorInfo(1, 0, nullptr, size) == static_cast(0x80260003), "type checked before pointer"); + for (const auto value : buffer) Require(value == untouched, "failure preserves output"); + } return 0; } diff --git a/core/libs/prx/libSceAudioOut/tests/AudioOutSysHdmiMonitorInfo.cpp b/core/libs/prx/libSceAudioOut/tests/AudioOutSysHdmiMonitorInfo.cpp index ce23a664d7..7eb1547ce7 100644 --- a/core/libs/prx/libSceAudioOut/tests/AudioOutSysHdmiMonitorInfo.cpp +++ b/core/libs/prx/libSceAudioOut/tests/AudioOutSysHdmiMonitorInfo.cpp @@ -59,13 +59,14 @@ int main() { Require(sceAudioOutSysGetHdmiMonitorInfo(monitorTypeHdmi, nullptr, infoSize) == invalidPointer, "a null buffer must report an invalid pointer"); - Require(Throws(monitorTypeHdmi, infoSize - 1, - "sceAudioOutSysGetHdmiMonitorInfo: size 383 not supported"), "a short buffer must throw"); - Require(Throws(monitorTypeHdmi, infoSize + 1, - "sceAudioOutSysGetHdmiMonitorInfo: size 385 not supported"), "a long buffer must throw"); + buffer.fill(untouched); + for (const std::uint32_t size : {0u, infoSize - 1, infoSize + 1, 0xffffffffu}) { + Require(sceAudioOutSysGetHdmiMonitorInfo(monitorTypeHdmi, buffer.data(), size) == static_cast(0x80260006), "exact size required"); + Require(sceAudioOutSysGetHdmiMonitorInfo(0, nullptr, size) == invalidPointer, "pointer checked before size and type"); + for (const auto value : buffer) Require(value == untouched, "failure preserves output"); + } + for (const int type : {0, 2, -1, 17}) Require(sceAudioOutSysGetHdmiMonitorInfo(type, buffer.data(), infoSize) == static_cast(0x8026000C), "unsupported type"); Require(Throws(0x10, infoSize, - "sceAudioOutSysGetHdmiMonitorInfo: monitor type 16 not supported"), "monitor type 0x10 must throw"); - Require(Throws(0, infoSize, - "sceAudioOutSysGetHdmiMonitorInfo: monitor type 0 not supported"), "monitor type 0 must throw"); + "sceAudioOutSysGetHdmiMonitorInfo: monitor type 16 not supported"), "unimplemented device query"); return 0; } diff --git a/docs/dev/debt/libSceAudioOut.md b/docs/dev/debt/libSceAudioOut.md index 1e7b243b57..792d6e1a9e 100644 --- a/docs/dev/debt/libSceAudioOut.md +++ b/docs/dev/debt/libSceAudioOut.md @@ -6,7 +6,7 @@ - [sceAudioOut2UserGetSupportedAttributes](../../../core/libs/prx/libSceAudioOut/src/AudioOut2User.cpp) - the PS5 module confirms a pointer-sized user handle and two optional uint32 outputs. Handles 0/-1 return 0x80268010 before checking outputs; two null outputs return 0x80268001. Host masks still describe only implemented PCM/gain support, unlike native SDK- and device-dependent masks. Unknown or destroyed host handles throw; native arbitrary handles are pointers and cannot be validated by the host registry. Port ownership and busy-user destruction remain unimplemented. - [sceAudioOutExOpen, sceAudioOutExConfigureOutput, sceAudioOutExClose](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - the HDMI bitstream calls follow [PS5-Audio-Passthrough-Research](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/docs/FINDINGS.md#L31-L80), which disassembles firmware 12.70's libSceAudioOut and logs the calls on a console. `sceAudioOutExOpen(user, mode)` returns `0x80260015` for a mode above 10 or outside mask `0x61F`; otherwise it opens a port of internal type 6 in S16 stereo, 256 frames at 48000 Hz for modes 0, 1, 2 and 9 and 1024 frames at 192000 Hz for modes 3, 4 and 10. `sceAudioOutExConfigureOutput(control, flags, mode, target, option)` returns `0x80268009` when `control` is not 0, takes modes 0 to 10 and 0xFF and targets 1 to 3 and 0xFF, and returned 0 on the console; `sceAudioOutExClose` returned 0. The argument registers match [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L26-L28) and PPSA99051's [bitstream_out.cpp](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/cf3c921d79bf00db4847f31b68a7bd95a8037cd7/sample-app/src/platform/ps5/bitstream_out.cpp#L29-L32). Audio reaches the host through SDL as PCM and the host has no HDMI bitstream output, so an Ex port has no host device: `sceAudioOutOutput` paces it at its own rate and drops its IEC 61937 bursts, and `sceAudioOutExConfigureOutput` changes nothing. Nonzero `flags` or `option`, other modes or targets, `sceAudioOutExClose` of a handle that is not an Ex port, and `sceAudioOutGetPortState` on an Ex port throw, since the console's answers are unknown -- [sceAudioOutExGetMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) reports a sink without audio descriptors: it zeroes the buffer and returns 0, as `sceAudioOutSysGetHdmiMonitorInfo` does, since audio reaches the host through SDL as PCM and there is no EDID to describe. The arguments `(0, 0, info, size)` and the descriptor count as a 32-bit value at +0x98, with 8-byte descriptors from +0xA0, follow [Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L62-L117) (PPSA99505), which passes a 0x400-byte buffer and read that layout from one TV; with no descriptor listed it decodes the audio itself instead of opening a bitstream port. The meaning of the first two arguments and the structure's size are unknown: nonzero first or second arguments and sizes below 0xA0, which cannot hold the count, throw, and every byte of the declared size is written as 0. A null buffer returns `0x80260004` like the other AudioOut calls; the console's code is not measured +- [sceAudioOutExGetMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) - native validation requires first argument zero, a non-null buffer and exactly 1024 bytes, in that order (errors 0x80260003/04/06); the second argument is ignored. The host still reports no audio descriptors by zeroing the record. Native content comes from monitor/device services and system audio configuration, which the SDL backend does not reproduce. - [sceAudioOut2Set3DLatency](../../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) (libSceAudioOut) - the two 32-bit arguments and selector validation match the system library: selector 0 accepts outputs 0 through 2; selector 255 accepts outputs 1 and 2. No third latency argument exists. The host has no system 3D rendering stage, so the call still does not apply latency and does not reproduce the system initialization and output-availability checks. - [sceAudioOut2MasteringInit](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) - native initialization requires AudioOut2 initialization (0x80268006 otherwise), returns success immediately when already initialized, and otherwise rejects nonzero flags with 0x80268201. Term returns 0x80268203 without mastering initialization and clears the mastering state after a successful service call. The protected Init body remains unchanged; host Term still returns success without this lifecycle or native mastering allocations. - [sceAudioOut2MasteringGetState and sceAudioOut2MasteringSetParam](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) - the state layout, the states ids 1 (default) and 2 (V2), the descriptor ids `0x01010001`, `0x01020001` and `0x01010003` and the errors `0x80268201` and `0x80268204` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/df5b9b92be7dfd463b47756b707d6d356a9f0550/src/libs/libAudio2.cpp), not a console measurement. GetState zeroes the states and writes the two descriptors; with no mastering stage the compressor and limiter values stay zero. SetParam accepts any parameter set and records nothing, as KytyPS5 does; a null parameter returns `0x80268201`, the mastering invalid-API-parameter error KytyPS5 returns for a null state, which is an inference rather than a measurement. The meaning of `output` and `user` is unknown and both are ignored @@ -115,4 +115,4 @@ - [AudioOut2 contexts](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) with an SDL device and no `sceAudioOut2ContextAdvance` calls read a grain's port buffers at the next push, at least 100 us after the grain was pushed, and play the mix behind a 40 ms cushion that the queue level leaves out. When the hardware reads the buffers is not known. The timing follows a title whose mixer fills a buffer after pushing it, reuses it two pushes later, and may still be writing it when the next push follows within microseconds. The last grain pushed before a title stops pushing is never read or played. - [sceAudioOut2ContextGetQueueLevel](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp) (libSceAudioOut) with an SDL device counts the grains that `sceAudioOut2ContextAdvance` completed and no push has consumed as queued, together with the pushed grains beyond the 40 ms cushion, up to `queue_depth`. Whether the console counts them is unknown. Pushes that fill the cushion leave the level at 0, so a title that renders as many grains as the level leaves room for and pushes them on another thread rendered a full queue again after each of those pushes: PPSA23226's Wwise sink passes the available count to the sound engine as the number of frames to render, advances once per frame and pushes on a separate thread, and exceeded 16 completed grains. Counted, the completed grains keep such a title at most `queue_depth` grains ahead of its pushes. - [sceAudioOut2LoInit, sceAudioOut2LoContext* and sceAudioOut2LoPort* calls](../../../core/libs/prx/libSceAudioOut/src/AudioOut2Low.cpp) (libSceAudioOut) have no low-latency path: each forwards to its `sceAudioOut2` counterpart, so grain and queue depth are the context's `num_grains` and `queue_depth`, and playback keeps the 40 ms cushion. [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioOut2.cs#L489-L545) only says each call takes the same handles and parameter blocks as its counterpart and drives the low-latency clock with a shallow queue and a small grain ([docs](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/docs/audio.md#L662-L668)). Unverified: the console's grain and depth limits, its error for a handle of the other service (accepted here) and what `sceAudioOut2LoInit` returns when repeated (0 here). -- [sceAudioOutSysGetHdmiMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) reports an HDMI sink without a monitor name or audio descriptors: it zeroes the 0x180 bytes and returns 0, since audio reaches the host through SDL as PCM and there is no EDID to describe. The arguments `(type, info, size)`, the required size 0x180 and the fields titles read (monitor name at +0x0B, 13 bytes; descriptor count as a 32-bit value at +0x94; 8-byte descriptors `[coding type, channels, rate mask, 0, extra, 0, 0, 0]` from +0x98) come from [PS5-Audio-Passthrough-Research](https://github.com/sainsaji/PS5-Audio-Passthrough-Research/blob/c3be9c2afcb8f7838784ead0fe1c12a2ceea965a/docs/FINDINGS.md) (firmware 12.70, one monitor). What a console without an HDMI audio sink returns, an error or no descriptors, is unknown. The other bytes, among them the header at +0x90 (`01 09 4f 00` on that monitor), have no known meaning and are written as 0. No LPCM descriptor is reported for the host device, as the rate mask and extra byte of one are not known. A null buffer returns `0x80260004` like the other AudioOut calls; the console's code is not measured. Monitor type 0x10, which the console also accepts, other types and sizes other than 0x180 throw: their results and error codes are unknown. +- [sceAudioOutSysGetHdmiMonitorInfo](../../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) - native validation requires a non-null buffer and exactly 384 bytes, then accepts types 1 and 16. Mapped pointer/size/type errors are 0x80260004/06/0C. The host still zeroes type 1 output and throws for type 16: native monitor descriptors and system audio settings are unavailable. Driver initialization failures and service errors are not represented. From 9b584b01ad76dd2820c988e28e0d4eadc4321870 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:19:52 +0300 Subject: [PATCH 381/523] docs(hmd2): separate recovered contracts from remaining gaps --- docs/dev/debt/libSceHmd2.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index 36737886e2..20fdeb5810 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -2,15 +2,13 @@ ### Silent stubs -- [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) has no PS VR2 runtime: `sceHmd2Initialize` returns libSceHmd's `UNSUPPORTED_FEATURE` (0x81110016) for any param, so the other calls answer as before initialization and `sceHmd2Open` returns `NotInitialized` (0x8a720002). These results are restored from the behaviour before #7323 and are not measured on hardware: firmware 13.20's libSceHmd2, with the `/dev/hmd2_cmd` state measured on a retail PS5 with no headset connected (connected flag 0, status 2, user -1), initializes, returns a handle from `sceHmd2Open` and reports `NotDetected`, which the code past the initialization check models but no title reaches. PPSA01317 passes a pointer to 16 zeroed bytes, only tests the result for non-zero and then skips its VR set-up; with initialization succeeding it stopped on `sceHmd2ReprojectionQueryBufferSizeAlign` (#6350). That export, `sceHmd2ReprojectionQueryDisplayBufferSizeAlign` and `sceHmd2ReprojectionQuerySeeThroughBufferSizeAlign` stay unimplemented and throw pending console measurements of their sizes and alignments. The other reprojection and gaze exports throw, and [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) never initializes +- [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) has no PS VR2 runtime: Initialize returns unsupported-feature, so normal calls never reach initialized device behavior. Reprojection and gaze operations retain their throwing bodies. Four size/alignment queries return measured constants; QueryBufferSizeAlign still throws. Device setup, headset tracking and rendering are not implemented. ### Unknown function info - [sceHmd2Open](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the handle is the value the console's driver returned to the first open; titles built with an SDK before 7.00 may open for any user, and the firmware's `sceMbusAddHandleByUserId` result for a user other than the initial one is unknown, so that case throws. 0x8a720006 for a second open is named `AlreadyOpened` from its use - [sceHmd2SetVibration](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the second parameter is checked against 25 only while a headset is connected; its name and unit are unknown. The field of view member names are assumed: of the firmware's default tangents the first is the widest and the last two are equal -- PPSA01317 imports declared without parameters, signatures unknown: the [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) exports that throw and the remaining [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) exports. Names from the aerolib NID list -- PPSA15878 imports declared without parameters, signatures unknown: the [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) reprojection and gaze exports, `sceVrTracker2Finalize`, `sceVrTracker2GetPlayAreaOrientedBoundingBox` and `sceVrTracker2LocateCoordinateSystem` in [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp), and the `scePadVrController` trigger effect exports in [libScePad](../../../core/libs/prx/libScePad/Export.cpp). Names from the aerolib NID list -- [libSceHmd2 gaze and reprojection exports](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - `sceHmd2ReprojectionSeeThroughDraw`, `sceHmd2ReprojectionSeeThroughIC`, `sceHmd2ReprojectionVideoOutEq`, `sceHmd2ReprojectionWaitShader`, `sceHmd2SeeThroughCallbackGetPoseDataFunc`, `sceHmd2ReprojectionClear`, `sceHmd2ReprojectionSet`, `sceHmd2RpeorjectionSetParam` are declared without parameters from the firmware export database (aerolib.csv); their guest signatures are unverified on hardware and every call throws +- [libSceHmd2 legacy exports](../../../core/libs/prx/libSceHmd2/Export.cpp) - SeeThroughDraw, SeeThroughIC, VideoOutEq, WaitShader, SeeThroughCallbackGetPoseDataFunc, ReprojectionClear, ReprojectionSet and RpeorjectionSetParam have no matching symbols or callers in the supplied system modules. Their signatures remain unresolved. ClearUserEventEnd/Start, GazeStart/StopUserCalibration and GazeObjectSelectionTerminate are confirmed to take no arguments; their behavior after initialization remains unimplemented. - [sceHmd2InternalSetPortStatus](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - unknown signature - [sceHmd2InternalLedDetectorStartBySlot](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - unknown signature - [sceHmd2InternalImageStartBySlot](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - unknown signature @@ -34,7 +32,7 @@ ### Functional - [sceHmd2Initialize](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the firmware also fails with 0x8a72001f when `sceKernelGetAppInfo` fails and with 0x8a72000a for some applications, depending on app info fields at 0x2c, 0x4b and 0x4d whose meaning is unknown, and then calls `sceMbusInit`; none of this is modelled -- [sceHmd2ReprojectionQueryDisplayBufferSizeAlign, sceHmd2ReprojectionQuerySeeThroughBufferSizeAlign, sceHmd2ReprojectionGetMirroringDisplayBufferSizeAlign and sceHmd2ReprojectionGetMirroringWorkMemorySizeAlign](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - each returns a `{uint64 size, uint64 alignment}` pair in RAX:RDX; the values were measured on a retail PS5 (firmware 13.60) with no PS VR2 headset from a homebrew payload that loaded `libSceHmd2.sprx` without initializing it, and stayed the same for every argument tried; `sceHmd2ReprojectionQueryBufferSizeAlign` measured there returns 0x100828 and 0x100 for every argument, including the 2, 0xAF and 1 that PPSA05666 passes. They are assumed not to depend on initialization or on a connected headset, and their parameters are unknown +- [Reprojection size/alignment queries](../../../core/libs/prx/libSceHmd2/Export.cpp) - all five exports consume no arguments and return two uint64 values in RAX:RDX. Four host queries return values measured without a headset; native display sizes are calculated through the graphics layout service and see-through size follows internal configuration. QueryBufferSizeAlign changes calculation with runtime state; its protected body still throws and its declared scalar return type does not represent the native pair. Correcting that return type also requires changing its return statement, outside the permitted body edits. - [Hmd2BeforeInitializeZero](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers 0 to these 45 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `vDgpdS2N9Rw` (sceHmd2InternalRegisterVibrationCallback), `Ri35gKTZpvE` (sceHmd2InternalUnregisterVibrationCallback), `9oyW8puiKeI`, `1HZNE6jhBnQ` (sceHmd2InternalPowerEventInitialize), `cdwAKBZLXjs`, `Yub1MF58jfY`, `JYXAuQFnPyw` (sceHmd2InternalDfuOpen), `VuPKqCu3wzw` (sceHmd2InternalDfuClose), `GSThagVyW5U` (sceHmd2InternalCrashReportOpen), `J4qE5N937MY` (sceHmd2InternalCrashReportClose), `9KnNj-dyASg`, `Qta2bkstbFk`, `5ZcObnDRBXE`, `AvPNn0qx4WQ`, `jp0apqNGgi8` (sceHmd2SetIpdOffset), `-kZT7X7OMg0` (sceHmd2InternalRelocalizerOpen), `1ewLtng-SVk` (sceHmd2InternalRelocalizerClose), `B0b8DeIUGes` (sceHmd2InternalTrackingOpen), `HclLg4Oj5yw` (sceHmd2InternalTrackingClose), `VbEvrQXsZyY` (sceHmd2InternalLedDetectorOpen), `bB6MKZi5UCQ` (sceHmd2InternalLedDetectorClose), `OXQ1EDf02XU` (sceHmd2InternalImageOpen), `IAxfMTvFm0M` (sceHmd2InternalImageClose), `qTJtAtaLeno` (sceHmd2InternalGenDataOpen), `j7Q16UX6l34` (sceHmd2InternalGenDataClose), `pmmNM06rLqU`, `zdQfyDK9Ods` (sceHmd2InternalSlamTraceOpen), `aPRwp9GnljM` (sceHmd2InternalSlamTraceClose), `MjY1gBSaed0` (sceHmd2InternalGazeOpen), `evAMjJYHqW4` (sceHmd2InternalGazeClose), `r93FLEZ3BBk` (sceHmd2InternalGazeStart), `IdYURdEdNFo` (sceHmd2InternalGazeStop), `CVcL2Cu2AEI` (sceHmd2GazeActivateGazeTracking), `NmKIfVxr2s0`, `KeDLkkp62Gg`, `+aadu6-ra+A`, `FgnTiwwGSzk`, `PWAUwOTHbbo` (sceHmd2InternalCreateSharedMemory), `WzaEApHnsP8` (sceHmd2InternalIsGameVr2d), `dXzQI8QVY7Y` (sceHmd2InternalIsMiniAppVr2d), `cCvFKGqRh1k` (sceHmd2InternalIsCommonDlgVr2d), `uj4-2VEAFPU` (sceHmd2InternalIsCommonDlgMiniAppVr2d), `sIaGStI-iLM` (sceHmd2InternalMapSharedMemory), `JzwpZOemDO8`, `IsC4R4bZFVU`. Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2BeforeInitializeNotInitialized](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720002` to these 9 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `sXaPEPY-fLA` (sceHmd2InternalOpen), `3F5inHhKzXg` (sceHmd2InternalGetDeviceInformation), `sJAiB-6p55U`, `E8hDydLjsSs`, `xEQkSb2WJ7Y`, `aaP4dnR4q-0`, `lEU0TeUoGwI`, `cFVyjHBpUUU`, `zROtb46jTj4`. Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2BeforeInitializeError0004](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720004` to these 15 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `kAMsjYHBbNQ` (sceHmd2InternalSetVolume), `5m73zmLpsQ0` (sceHmd2InternalResetViewPosition), `EPeH4xuQ2XQ` (sceHmd2InternalGetStatus), `TJ8lyRsjFY4` (sceHmd2InternalClearTrackerFts), `-XQVcLnyX68` (sceHmd2InternalSetSlamCommand), `giQwQoHhYYA` (sceHmd2InternalDfuStart), `zNc9IkgawMI`, `-tnG5UJPvAc` (sceHmd2GazeStartUserCalibration), `FPhFmG+jXuQ` (sceHmd2GazeStopUserCalibration), `CKlkvNngofQ`, `yL1CZaciFWE` (sceHmd2GazeSetUserCalibrationPoint), `qlzgyG3s0RY`, `kTIDrDLzMv0`, `9Gee0ef8a+U`, `ecZVjQdwmBI` (sceHmd2GazeSetEnabledEye). Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded @@ -47,7 +45,6 @@ - [Hmd2BeforeInitializeMinusOne](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers -1 to these 12 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `EAkv8Mqt33E` (sceHmd2InternalRelocalizerStart), `Rv7bkmKvmz0` (sceHmd2InternalRelocalizerStop), `1oPZizn0SNA` (sceHmd2InternalTrackingStart), `TwCpE-nNrlo` (sceHmd2InternalTrackingStop), `jkqAeQfdtxw` (sceHmd2InternalLedDetectorStart), `+l0RQph81Z8` (sceHmd2InternalLedDetectorStop), `qsmAtGw1Ncc` (sceHmd2InternalImageStart), `uKhwYoMsIsA` (sceHmd2InternalImageStop), `WNdaigr94dI` (sceHmd2InternalGenDataStart), `P6S5ZVWf-H0` (sceHmd2InternalGenDataStop), `sun6NWE1sdg` (sceHmd2InternalSlamTraceStart), `--TFsJYXo04` (sceHmd2InternalSlamTraceStop). Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2BeforeInitializeValue0400](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x400` to these 1 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `BWqcwjCozzI`. Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2BeforeInitializeValue20000](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x20000` to these 1 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `PRz7TdBGUI0`. Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded -- [libSceHmd2 pre-initialization sweep](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the names in the entries above are the aerolib names of the NIDs where one exists; the error names `Error0004`, `Error002C`, `Error1002` and `Error1003` are placeholders for codes whose meaning is unknown, and `ReprojectionNotInitialized` is ours because every export answering `0x8a72000b` is a reprojection one. Exports that answered differently to null and to zeroed arguments, wrote output, returned a pointer-like value, answered `0x8a72000a` (which `sceHmd2Initialize` also returns for some applications) or crashed the probe are left as they were - [Hmd2CheckFirstThenError0004](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720008` when argument 0 is null and `0x8a720004` when every argument points at a zeroed 16 KiB buffer, writing nothing, for these 20 exports: `9WeZr4kYfzk` (sceHmd2InternalGetFwVersion), `N+5h1hCOFAg` (sceHmd2InternalGetRequiredPUPVersion), `w2vEs2y5ZmE` (sceHmd2InternalGetOpticalParam), `L5gat1pe0y0` (sceHmd2InternalSetCameraSeeThroughParam), `4GnLHR9NJ1w`, `AHk7nGSzef0` (sceHmd2InternalGetTrackerFtsData), `ncjX9d3mc0M`, `l63QYAUuogc` (sceHmd2InternalGetSlamCommand), `mrcBySCdXSo` (sceHmd2InternalDfuGetStatus), `ieBra+td8sU` (sceHmd2InternalDfuGetUpdateStatus), `s+rzBnewgGs` (sceHmd2InternalDfuCheckAfterPvt), `Nfcm0GBdzZI` (sceHmd2InternalRelocalizerGetVersion), `Tc9rasWfrm4` (sceHmd2InternalSetHomeCalibration), `ckTbKTX4KZg`, `D7576PUbUjk` (sceHmd2InternalGazeReadStatus), `tTJ04SrBT8U`, `sDP7hZ-P6OI` (sceHmd2GazeGetDeviceStatus), `J+eVNbN9ndY` (sceHmd2GazeGetUserCalibrationStatus), `kozvHBNyxf0`, `638Tmg5y6U4`. Each argument was nulled on its own with the others present (probe `hmd2n`, payload process, firmware 13.60); what the present arguments mean, and the behaviour after initialization, are not measured, so the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2CheckFirstThenZero](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720008` when argument 0 is null and 0 when every argument points at a zeroed 16 KiB buffer, writing nothing, for these 8 exports: `8ETtFtp5zqY` (sceHmd2InternalGetVrMode), `CWPkBm3EXQA`, `R62liQQIAaA` (sceHmd2InternalGetFakeLensSeparationDistance), `-bcWLHsdzo0`, `JngJzgNbu8U` (sceHmd2SetFieldOfView), `xNBSz8xrGdk`, `yUVPfN-jRIU`, `BKPR3RZ5aO4`. Each argument was nulled on its own with the others present (probe `hmd2n`, payload process, firmware 13.60); what the present arguments mean, and the behaviour after initialization, are not measured, so the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2CheckFirstThenError1002](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720008` when argument 0 is null and `0x8a721002` when every argument points at a zeroed 16 KiB buffer, writing nothing, for these 8 exports: `IG4y+drWAWw` (sceHmd2InternalRelocalizerReadData), `rhYUml9vppE` (sceHmd2InternalRelocalizerReadDataAsync), `6574xHS+jZk` (sceHmd2InternalRelocalizerReadData2), `mXzaY6xVoxg` (sceHmd2InternalTrackingReadData), `6af-HIZQQn4` (sceHmd2InternalTrackingReadData2), `6qWtdTHQsTk`, `pUm8t8xWhFg`, `A0XKI-WjxK8` (sceHmd2InternalImageReadData). Each argument was nulled on its own with the others present (probe `hmd2n`, payload process, firmware 13.60); what the present arguments mean, and the behaviour after initialization, are not measured, so the handler throws once `sceHmd2Initialize` has succeeded @@ -66,7 +63,6 @@ - [Hmd2CheckFirstAndThirdThenError1003](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720008` when argument 0 and argument 2 is null and `0x8a721003` when every argument points at a zeroed 16 KiB buffer, writing nothing, for these 1 exports: `zn7PHGzzEyI`. Each argument was nulled on its own with the others present (probe `hmd2n`, payload process, firmware 13.60); what the present arguments mean, and the behaviour after initialization, are not measured, so the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2CheckSecondThenInvalidHandle](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720008` when argument 1 is null and `0x8a720003` when every argument points at a zeroed 16 KiB buffer, writing nothing, for these 1 exports: `wEHILc7TP04` (sceHmd2GazeInsertRawDataWithHandle). Each argument was nulled on its own with the others present (probe `hmd2n`, payload process, firmware 13.60); what the present arguments mean, and the behaviour after initialization, are not measured, so the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2CheckSecondThenError1002](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720008` when argument 1 is null and `0x8a721002` when every argument points at a zeroed 16 KiB buffer, writing nothing, for these 1 exports: `W-ZIlUXcrNA`. Each argument was nulled on its own with the others present (probe `hmd2n`, payload process, firmware 13.60); what the present arguments mean, and the behaviour after initialization, are not measured, so the handler throws once `sceHmd2Initialize` has succeeded -- [libSceHmd2 null-argument sweep](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - `Error1010` and `Error800d0203` are placeholder names. Left as they were: the exports that write output (`omimgG5itSs`, `FBUEhH9tQ1A`, `6PeHcAaKYg8`, `yur+xPdWPAg`, `RnieDDp5ge4`, `jM3NEJA11yw`, `b0H-n-tUk0U`) and those whose answer with a single null argument is neither `0x8a720008` nor their all-present answer, which take a handle or slot number rather than a pointer first (`MXhloJ6Iw3w`, `19wrIIiqFgM`, `Z389vAm47Ys`, `cDcrvEG8j8k`, `tuqJO6O6cb4`, `fd4fu1cziaI`, `UxMg7iL4nRs`, `K1GELwlaaGc`, `RFn39VCHjGY`, `FRoSjceDv10`, `wiv8+7ogrq0`, `wQ44gBwBw44`) - [Hmd2WriteStateTwo](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console writes the five bytes `02 00 00 00 00` at the start of its first argument and returns 0; a null first argument returns `0x8a720008`: `omimgG5itSs`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2WriteSize0x2000Align0x100](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console writes the 64-bit values `0x2000` through its first argument and `0x100` through its second and returns 0; a null first or second argument returns `0x8a720008`: `6PeHcAaKYg8`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2WriteFourConstants](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console writes the 16 bytes `e1 f0 74 3c d8 f0 74 3c 1f f1 74 3c d8 f0 74 3c` (four floats near 0.01495) at the start of its first argument and returns 0; a null first argument returns `0x8a720008`: `yur+xPdWPAg`. Measured from a payload process on firmware 13.60 (probes `hmd2n` and `hmd2x`); the meaning of the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded From 772273eb693b271c82d099b16c05ea51f071ae21 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:20:44 +0300 Subject: [PATCH 382/523] fix(shareplay): restore initialization validation and state --- core/libs/prx/libSceSharePlay/Export.cpp | 20 +++++++++++++++++--- core/libs/tests/GuestSharePlay.cpp | 21 +++++++++++++++++---- docs/dev/debt/libSceSharePlay.md | 6 +++--- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceSharePlay/Export.cpp b/core/libs/prx/libSceSharePlay/Export.cpp index 216ab34643..a1dcf5eadf 100644 --- a/core/libs/prx/libSceSharePlay/Export.cpp +++ b/core/libs/prx/libSceSharePlay/Export.cpp @@ -1,24 +1,36 @@ #include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" static constexpr int SCE_SHARE_PLAY_ERROR_INVALID_ARGS = static_cast(0x810E0001); +static constexpr int SCE_SHARE_PLAY_ERROR_ALREADY_INITIALIZED = static_cast(0x810E0003); +static constexpr int SCE_SHARE_PLAY_ERROR_NOT_INITIALIZED = static_cast(0x810E0004); +static std::mutex g_mutex; +static bool g_initialized = false; static constexpr std::int32_t SCE_USER_SERVICE_USER_ID_INVALID = -1; extern "C" { int APS5_VABI sceSharePlayInitialize(void* heap, size_t heapSize) { - (void)heap; - if (heapSize == 0) APS5_INVALID_ARG_EX; + std::lock_guard lock(g_mutex); + if (g_initialized) return SCE_SHARE_PLAY_ERROR_ALREADY_INITIALIZED; + if (heap && heapSize < 0x1800) return SCE_SHARE_PLAY_ERROR_INVALID_ARGS; + g_initialized = true; return 0; } int APS5_VABI sceSharePlayTerminate(void) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_SHARE_PLAY_ERROR_NOT_INITIALIZED; + g_initialized = false; return 0; } int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(SharePlayConnectionInfoA* info) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_SHARE_PLAY_ERROR_NOT_INITIALIZED; if (info == nullptr) return SCE_SHARE_PLAY_ERROR_INVALID_ARGS; *info = {}; info->hostUserId = SCE_USER_SERVICE_USER_ID_INVALID; @@ -26,7 +38,9 @@ int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(SharePlayConnectionInfoA* in return 0; } -int APS5_VABI sceSharePlayProhibitController(int prohibited) { +int APS5_VABI sceSharePlayProhibitController(std::uint8_t prohibited) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return SCE_SHARE_PLAY_ERROR_NOT_INITIALIZED; if (prohibited != 0 && prohibited != 1) APS5_INVALID_ARG_EX; return 0; } diff --git a/core/libs/tests/GuestSharePlay.cpp b/core/libs/tests/GuestSharePlay.cpp index fe2819c276..269d05fb4d 100644 --- a/core/libs/tests/GuestSharePlay.cpp +++ b/core/libs/tests/GuestSharePlay.cpp @@ -4,12 +4,13 @@ #include #include #include +#include extern "C" { int APS5_VABI sceSharePlayInitialize(void*, std::size_t); int APS5_VABI sceSharePlayTerminate(void); int APS5_VABI sceSharePlayGetCurrentConnectionInfoA(SharePlayConnectionInfoA*); -int APS5_VABI sceSharePlayProhibitController(int); +int APS5_VABI sceSharePlayProhibitController(std::uint8_t); } static void Require(bool value) { if (!value) std::abort(); } @@ -26,12 +27,19 @@ static bool Throws(TCall call) { int main() { alignas(16) static unsigned char heap[0x1800]; + constexpr int notInitialized = static_cast(0x810E0004); + constexpr int alreadyInitialized = static_cast(0x810E0003); + constexpr int invalidArgument = static_cast(0x810E0001); + Require(sceSharePlayTerminate() == notInitialized); + Require(sceSharePlayGetCurrentConnectionInfoA(nullptr) == notInitialized); + Require(sceSharePlayProhibitController(2) == notInitialized); Require(sceSharePlayInitialize(nullptr, sizeof(heap)) == 0); + Require(sceSharePlayInitialize(heap, 0) == alreadyInitialized); Require(sceSharePlayTerminate() == 0); Require(sceSharePlayInitialize(heap, sizeof(heap)) == 0); Require(sceSharePlayTerminate() == 0); - Require(Throws([&] { sceSharePlayInitialize(heap, 0); })); - Require(Throws([&] { sceSharePlayInitialize(nullptr, 0); })); + for (const std::size_t size : {std::size_t{0}, std::size_t{1}, sizeof(heap) - 1}) Require(sceSharePlayInitialize(heap, size) == invalidArgument); + Require(sceSharePlayInitialize(nullptr, 0) == 0); SharePlayConnectionInfoA info; std::memset(&info, 0x5a, sizeof(info)); Require(sceSharePlayGetCurrentConnectionInfoA(&info) == 0); @@ -43,5 +51,10 @@ int main() { Require(sceSharePlayProhibitController(1) == 0); Require(sceSharePlayProhibitController(0) == 0); Require(Throws([] { sceSharePlayProhibitController(2); })); - Require(Throws([] { sceSharePlayProhibitController(-1); })); + Require(Throws([] { sceSharePlayProhibitController(255); })); + Require(sceSharePlayTerminate() == 0); + std::memset(&info, 0x5a, sizeof(info)); + Require(sceSharePlayGetCurrentConnectionInfoA(&info) == notInitialized); + for (const auto value : std::as_bytes(std::span(&info, 1))) Require(value == std::byte{0x5a}); + Require(sceSharePlayTerminate() == notInitialized); } diff --git a/docs/dev/debt/libSceSharePlay.md b/docs/dev/debt/libSceSharePlay.md index 167653b1ff..cd999b7fab 100644 --- a/docs/dev/debt/libSceSharePlay.md +++ b/docs/dev/debt/libSceSharePlay.md @@ -4,9 +4,9 @@ - [sceSharePlayCrashDaemon, sceSharePlayGetCurrentConnectionInfo, sceSharePlayGetCurrentInfo, sceSharePlayGetEvent, sceSharePlayNotifyDialogOpen, sceSharePlayNotifyForceCloseForCdlg, sceSharePlayResumeScreenForCdlg, sceSharePlayServerLock](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - the list is the intersection of the export table of the console's [libSceSharePlay](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSharePlay.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. - [sceSharePlayServerUnLock, sceSharePlaySetMode, sceSharePlaySetProhibition, sceSharePlaySetProhibitionModeWithAppId, sceSharePlayStartStandby, sceSharePlayStartStreaming, sceSharePlayStopStandby, sceSharePlayStopStreaming](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - the list is the intersection of the export table of the console's [libSceSharePlay](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSharePlay.c) (function symbols only), the pinned [aerolib names](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv) and the functions not yet defined anywhere in the repo. No credible prototypes were found; they are declared without parameters solely to resolve known imports, not as an ABI claim, and callers must not call them until signatures are verified. -- [sceSharePlayInitialize](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - the library keeps no state, so the heap is not used. A null heap is accepted: PPSA11386 passes a null heap and 0x1800 bytes and stops on any non-zero result. The error for a zero size is unknown, so it throws ### Functional -- [sceSharePlayGetCurrentConnectionInfoA](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - reports no Share Play session, as the library keeps none: status 0 (DORMANT), mode 0, empty online IDs, account IDs 0 and both user IDs -1 (`SCE_USER_SERVICE_USER_ID_INVALID`). A null pointer returns `SCE_SHARE_PLAY_ERROR_INVALID_ARGS` (0x810E0001). The 72-byte `SceSharePlayConnectionInfoA` layout and the error code follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libNet.cpp) and [fpPS4](https://github.com/red-prig/fpPS4/blob/trunk/src/ps4_libsceshareplay.pas), which agree on both; KytyPS5 sets the user IDs to -1 and fpPS4 leaves them 0, and which one the console returns is not verified. KytyPS5 also returns `SCE_SHARE_PLAY_ERROR_NOT_INITIALIZED` before `sceSharePlayInitialize`; this library keeps no initialization state and does not. -- [sceSharePlayProhibitController](../../../core/libs/prx/libSceSharePlay/Export.cpp) (libSceSharePlay) - accepts 0 (allow) and 1 (prohibit) and returns 0, following [sharpemu](https://github.com/sharpemu/sharpemu/blob/a2983692930564815a77f81e74c151268f5b935f/src/SharpEmu.Libs/Share/SharePlayExports.cs#L9-L31). The setting is not stored, as no Share Play session or visitor controller exists and no export reads it back. sharpemu rejects any other value with its synthetic `0x80020003`, not a Share Play code, so other values throw. Like the other exports here it does not require `sceSharePlayInitialize`. The `int` argument type and the console's error for other values are unverified +- [sceSharePlayInitialize and sceSharePlayTerminate](../../../core/libs/prx/libSceSharePlay/Export.cpp) - native heap validation and initialization state are recovered: a null heap ignores the supplied size, a supplied heap requires at least 0x1800 bytes, repeated initialization returns 0x810E0003 and termination before initialization returns 0x810E0004. The host does not allocate the native internal heap, connect to the Share Play service or reproduce its failures. +- [sceSharePlayGetCurrentConnectionInfoA](../../../core/libs/prx/libSceSharePlay/Export.cpp) - the native module confirms a 72-byte result, initial zero fields and both user IDs -1. Initialization is checked before the output pointer. The host retains this initial disconnected state; native event draining, cached updates and service reconnection are unimplemented. +- [sceSharePlayProhibitController](../../../core/libs/prx/libSceSharePlay/Export.cpp) - the argument is one byte, forwarded unchanged to the service. The host checks initialization and accepts 0/1 without a visitor controller; other byte values still throw because their handling belongs to the unimplemented service. From 7b55120dca14c13c418ef2733fc77d2fa4f3dce1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:24:50 +0300 Subject: [PATCH 383/523] fix(livestreaming): restore initialization and program info contracts --- .../prx/libSceGameLiveStreaming/Export.cpp | 25 +++++++++++-- core/libs/tests/GuestGameLiveStreaming.cpp | 37 +++++++++---------- docs/dev/debt/libSceGameLiveStreaming.md | 9 ++++- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/core/libs/prx/libSceGameLiveStreaming/Export.cpp b/core/libs/prx/libSceGameLiveStreaming/Export.cpp index 71b57a3129..d4a4a9bdbd 100644 --- a/core/libs/prx/libSceGameLiveStreaming/Export.cpp +++ b/core/libs/prx/libSceGameLiveStreaming/Export.cpp @@ -4,6 +4,15 @@ #include "prx/libc/include/General.hpp" #include "prx/libSceUserService/UserService.hpp" #include +#include + +static std::mutex g_mutex; +static bool g_initialized = false; +static constexpr int ErrorInvalidArgument = static_cast(0x80A00002); +static constexpr int ErrorAlreadyInitialized = static_cast(0x80A00003); +static constexpr int ErrorNotInitialized = static_cast(0x80A00004); + +struct GameLiveStreamingProgramInfo; struct GameLiveStreamingStatus2 { std::int32_t userId; @@ -19,23 +28,31 @@ static_assert(sizeof(GameLiveStreamingStatus2) == 72); extern "C" { -int APS5_VABI sceGameLiveStreamingInitialize(size_t heap_size) { - if (heap_size == 0) APS5_INVALID_ARG_EX; +int APS5_VABI sceGameLiveStreamingInitialize(size_t heapSize) { + if (heapSize != 0x4000) return ErrorInvalidArgument; + std::lock_guard lock(g_mutex); + if (g_initialized) return ErrorAlreadyInitialized; + g_initialized = true; return 0; } int APS5_VABI sceGameLiveStreamingTerminate(void) { + std::lock_guard lock(g_mutex); + if (!g_initialized) return ErrorNotInitialized; + g_initialized = false; return 0; } int APS5_VABI sceGameLiveStreamingGetCurrentStatus2(GameLiveStreamingStatus2* status) { - if (status == nullptr) APS5_INVALID_ARG_EX; + std::lock_guard lock(g_mutex); + if (!g_initialized) return ErrorNotInitialized; + if (status == nullptr) return ErrorInvalidArgument; std::memset(status, 0, sizeof(*status)); status->userId = USER_SERVICE_USER_ID_INVALID; return 0; } -int APS5_VABI sceGameLiveStreamingGetProgramInfo() { +int APS5_VABI sceGameLiveStreamingGetProgramInfo(GameLiveStreamingProgramInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestGameLiveStreaming.cpp b/core/libs/tests/GuestGameLiveStreaming.cpp index a60c2538cd..8f6f879c8a 100644 --- a/core/libs/tests/GuestGameLiveStreaming.cpp +++ b/core/libs/tests/GuestGameLiveStreaming.cpp @@ -4,6 +4,7 @@ #include #include #include +#include struct GameLiveStreamingStatus2 { std::int32_t userId; @@ -21,7 +22,8 @@ extern "C" { int APS5_VABI sceGameLiveStreamingInitialize(std::size_t heap_size); int APS5_VABI sceGameLiveStreamingTerminate(void); int APS5_VABI sceGameLiveStreamingGetCurrentStatus2(GameLiveStreamingStatus2* status); -int APS5_VABI sceGameLiveStreamingGetProgramInfo(void); +struct GameLiveStreamingProgramInfo; +int APS5_VABI sceGameLiveStreamingGetProgramInfo(GameLiveStreamingProgramInfo*); } namespace { @@ -33,23 +35,16 @@ constexpr std::int32_t kInvalidUserId = -1; } int main() { - bool threwZeroHeap = false; - try { - sceGameLiveStreamingInitialize(0); - } catch (const std::invalid_argument&) { - threwZeroHeap = true; - } - Require(threwZeroHeap); - - Require(sceGameLiveStreamingInitialize(1024 * 1024) == 0); - - bool threwNullStatus = false; - try { - sceGameLiveStreamingGetCurrentStatus2(nullptr); - } catch (const std::invalid_argument&) { - threwNullStatus = true; - } - Require(threwNullStatus); + constexpr int invalidArgument = static_cast(0x80A00002); + constexpr int alreadyInitialized = static_cast(0x80A00003); + constexpr int notInitialized = static_cast(0x80A00004); + Require(sceGameLiveStreamingTerminate() == notInitialized); + Require(sceGameLiveStreamingGetCurrentStatus2(nullptr) == notInitialized); + for (const std::size_t size : {0u, 1u, 0x3fffu, 0x4001u, 0x100000u}) Require(sceGameLiveStreamingInitialize(size) == invalidArgument); + Require(sceGameLiveStreamingInitialize(0x4000) == 0); + Require(sceGameLiveStreamingInitialize(0) == invalidArgument); + Require(sceGameLiveStreamingInitialize(0x4000) == alreadyInitialized); + Require(sceGameLiveStreamingGetCurrentStatus2(nullptr) == invalidArgument); GameLiveStreamingStatus2 status{}; std::memset(&status, 0x7f, sizeof(status)); @@ -69,11 +64,15 @@ int main() { bool threwProgramInfo = false; try { - sceGameLiveStreamingGetProgramInfo(); + sceGameLiveStreamingGetProgramInfo(nullptr); } catch (const std::runtime_error&) { threwProgramInfo = true; } Require(threwProgramInfo); Require(sceGameLiveStreamingTerminate() == 0); + std::memset(&status, 0x7f, sizeof(status)); + Require(sceGameLiveStreamingGetCurrentStatus2(&status) == notInitialized); + for (const auto value : std::as_bytes(std::span(&status, 1))) Require(value == std::byte{0x7f}); + Require(sceGameLiveStreamingTerminate() == notInitialized); } diff --git a/docs/dev/debt/libSceGameLiveStreaming.md b/docs/dev/debt/libSceGameLiveStreaming.md index ae477a6346..e85a882199 100644 --- a/docs/dev/debt/libSceGameLiveStreaming.md +++ b/docs/dev/debt/libSceGameLiveStreaming.md @@ -1,9 +1,14 @@ # libSceGameLiveStreaming technical debt +### Functional + +- [sceGameLiveStreamingEnableLiveStreaming](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - imported by PS4 titles, which do not load without it, and added without an implementation: declared without parameters, every call throws +- [libSceGameLiveStreaming](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) - native initialization requires exactly 0x4000 heap bytes before checking repeated initialization. Host lifecycle and pointer errors now match the wrapper, but service registration, allocation failures and asynchronous service readiness are not modelled. The host becomes ready immediately after initialization. +- [sceGameLiveStreamingGetCurrentStatus2](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) - the supplied module confirms the 72-byte layout and inactive status (user -1, remaining bytes zero). Live service counters, active-user changes and broadcast-resolution conversion are unimplemented. +- [sceGameLiveStreamingGetProgramInfo](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) - consumes one output pointer and uses a 4940-byte record. Field semantics remain unresolved; the protected body remains unimplemented. + ### Unknown function info -- [sceGameLiveStreamingEnableLiveStreaming](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - imported by PS4 titles, which do not load without it, and added without an implementation: declared without parameters, every call throws -- [sceGameLiveStreamingGetCurrentStatus2](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - the status layout (user id, on-air flag, spectator, text and command message counts, broadcast resolution, 48 reserved bytes) is taken from shadPS4's `OrbisGameLiveStreamingStatus2`; the call reports that no stream is on air, with an invalid user id - [sceGameLiveStreamingScreenInitialize](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature - [sceGameLiveStreamingScreenInitializeSeparateModeParameter](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature - [sceGameLiveStreamingScreenTerminate](../../../core/libs/prx/libSceGameLiveStreaming/Export.cpp) (libSceGameLiveStreaming) - unknown signature From 7b1bf85ffd38f388a30674ca48dc5d94f91c1a29 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:26:00 +0300 Subject: [PATCH 384/523] fix(appcontent): enforce required output and mount pointers --- core/libs/prx/libSceAppContent/Export.cpp | 18 +++++++++--------- core/libs/tests/GuestAppContent.cpp | 5 ++++- docs/dev/debt/libSceAppContent.md | 2 ++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index bb0eba21ea..30a0b1bc1c 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -73,10 +73,10 @@ int APS5_VABI sceAppContentGetAddcontInfo(uint32_t service_label, const NpUnifie return SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT; } -int APS5_VABI sceAppContentGetAddcontInfoList(uint32_t service_label, AppContentAddcontInfo* list, uint32_t list_num, uint32_t* hit_num) { - (void)service_label; - if ((!list || list_num == 0) && !hit_num) return SCE_APP_CONTENT_ERROR_PARAMETER; - if (hit_num) *hit_num = 0; +int APS5_VABI sceAppContentGetAddcontInfoList(uint32_t serviceLabel, AppContentAddcontInfo* list, uint32_t listNum, uint32_t* hitNum) { + (void)serviceLabel; + if (!hitNum) return SCE_APP_CONTENT_ERROR_PARAMETER; + *hitNum = 0; return 0; } @@ -103,12 +103,12 @@ int APS5_VABI sceAppContentDownloadDataFormat(const AppContentMountPoint* mount_ return 0; } -int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMountPoint* mount_point, size_t* available_space_kb) { - if (!mount_point || !available_space_kb) return SCE_APP_CONTENT_ERROR_PARAMETER; - if (std::strncmp(mount_point->data, DOWNLOAD_MOUNT_POINT, sizeof(mount_point->data)) != 0) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; +int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMountPoint* mountPoint, size_t* availableSpaceKb) { + if (!mountPoint || !availableSpaceKb) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (std::strncmp(mountPoint->data, DOWNLOAD_MOUNT_POINT, sizeof(mountPoint->data)) != 0) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; const std::uint64_t quotaKb = GetAppDownloadDataSizeMiB_nid_postfix() * 1024u; if (quotaKb == 0) { - *available_space_kb = 0; + *availableSpaceKb = 0; return 0; } const auto directory = ResolvePath_nid_no_patch(DOWNLOAD_MOUNT_POINT); @@ -118,7 +118,7 @@ int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMount if (entry.is_regular_file()) usedKb += (entry.file_size() + 1023u) / 1024u; } const std::uint64_t hostKb = std::filesystem::space(directory).available / 1024u; - *available_space_kb = static_cast(std::min(quotaKb - std::min(quotaKb, usedKb), hostKb)); + *availableSpaceKb = static_cast(std::min(quotaKb - std::min(quotaKb, usedKb), hostKb)); return 0; } diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp index 6d3745cd70..4cf0721f55 100644 --- a/core/libs/tests/GuestAppContent.cpp +++ b/core/libs/tests/GuestAppContent.cpp @@ -51,7 +51,10 @@ int main() { Require(sceAppContentDownloadDataGetAvailableSpaceKb(&download, &availableKb) == 0); Require(availableKb == 0); Require(sceAppContentDownloadDataGetAvailableSpaceKb(&download, nullptr) == ErrorParameter); + availableKb = 12345; Require(sceAppContentDownloadDataGetAvailableSpaceKb(nullptr, &availableKb) == ErrorParameter); + Require(availableKb == 12345); + Require(sceAppContentDownloadDataGetAvailableSpaceKb(nullptr, nullptr) == ErrorParameter); AppContentMountPoint invalidMount{}; Require(sceAppContentDownloadDataGetAvailableSpaceKb(&invalidMount, &availableKb) == ErrorNotMounted); const auto downloadData = ResolvePath_nid_no_patch("/download0"); @@ -170,7 +173,7 @@ int main() { Require(sceAppContentGetAddcontInfoList(0, info, 1, &hitNum) == 0); Require(hitNum == 0); Require(std::memcmp(info, untouchedInfo, sizeof(info)) == 0); - Require(sceAppContentGetAddcontInfoList(0, info, 1, nullptr) == 0); + Require(sceAppContentGetAddcontInfoList(0, info, 1, nullptr) == ErrorParameter); Require(sceAppContentGetAddcontInfoList(0, nullptr, 0, nullptr) == ErrorParameter); Require(sceAppContentGetAddcontInfoList(0, info, 0, nullptr) == ErrorParameter); Require(sceAppContentGetAddcontInfoList(0, nullptr, 1, nullptr) == ErrorParameter); diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index 62bdd6066f..373aa6fdba 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -9,5 +9,7 @@ - [libSceAppContent pending exports](../../../core/libs/prx/libSceAppContent/Export.cpp) - 17 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 +- [Initialization](../../../core/libs/prx/libSceAppContent/Export.cpp) does not track native service readiness or initialization. Native operations reject uninitialized use with `0x80D90001`; SDK 0x01500000 and later also reject repeated initialization and require zeroed initialization fields. Native entitlement operations additionally validate label bytes 17 through 19 from SDK 0x03500000. These checks and SDK-dependent service-error translation are not implemented. + - [Additional content](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) is not installed or mounted: `sceAppContentAddcontMount` answers `NOT_FOUND` for every entitlement label and `sceAppContentAddcontUnmount` for every mount point, as no add-content mount is ever handed out; `sceAppContentGetAddcontInfo` answers `DRM_NO_ENTITLEMENT` for every label and `sceAppContentGetAddcontInfoList` reports 0 entries; `sceAppContentAddcontEnqueueDownload` answers `PARAMETER` for a null label and `NOT_FOUND` otherwise, since no download is ever queued; `sceAppContentAddcontDelete` answers `PARAMETER` for a null label and `NOT_FOUND` otherwise, since no additional content is ever installed. `sceAppContentGetAddcontDownloadProgress` answers `PARAMETER` for a null label or progress and `NOT_FOUND` otherwise, as [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.cpp#L2328-L2341) does for a label that is not installed additional content, and never writes the progress. Its 16-byte layout (`DataSize`, then `DownloadedSize`, both 64-bit) follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppContent/AppContent.cs#L45-L54) and is unused here. The signatures of `sceAppContentAddcontEnqueueDownload` and `sceAppContentGetAddcontDownloadProgress` follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppContent/AppContent.cs#L140-L148), and the signature of `sceAppContentAddcontDelete` follows [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.h#L91-L92); dlc_emu is not a console measurement and no console source gives the error codes, so the signatures and the error codes are unverified - [Temporary app-content storage](../../../core/libs/prx/libSceAppContent/Export.cpp) tracks one `/temp0` mount, preserves its files on unmount, and rejects new path resolutions until remount. Mount, format, space-query and unmount errors follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/sys/sys_path.pas); the pointer ABI is also declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/AppContent/AppContent.cs). Existing open host descriptors remain usable after unmount; console behaviour with open files is unverified. Host filesystem failures still throw. From fc8970626125992e374cb2fd0c489c8277fa5b8a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:28:19 +0300 Subject: [PATCH 385/523] fix(vrtracker2): split anonymous tracking query signatures --- core/libs/prx/libSceVrTracker2/Export.cpp | 82 ++++++++++++++++++++--- docs/dev/debt/libSceVrTracker2.md | 2 + 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 61ccdcc8fd..3d67c0619a 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -125,6 +125,40 @@ std::int32_t ClearThenReportNotInitialized(void* output, std::size_t size) { } +struct VrTracker2RelativePoseResult { + VrTracker2Pose pose; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(VrTracker2RelativePoseResult) == 64); + +struct VrTracker2ScalarResult { + std::uint32_t size; + float value; +}; + +struct VrTracker2TrackingOptions { + std::int32_t options[3]; + std::uint8_t reset; + std::uint8_t reserved[3]; + float vector[3]; +}; + +struct VrTracker2TrackingOptionsResult { + VrTracker2TrackingOptions options; + float position[3]; +}; + +struct VrTracker2DiagnosticResult; +struct VrTracker2ExtendedResult; +struct VrTracker2TrackingConfiguration; + +static_assert(sizeof(VrTracker2ScalarResult) == 8); +static_assert(sizeof(VrTracker2TrackingOptions) == 28); +static_assert(offsetof(VrTracker2TrackingOptions, vector) == 16); +static_assert(sizeof(VrTracker2TrackingOptionsResult) == 40); +static_assert(offsetof(VrTracker2TrackingOptionsResult, position) == 28); + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -191,14 +225,42 @@ std::int32_t APS5_VABI sceVrTracker2Initialize(const VrTracker2InitializeParamet return 0; } -APS5_EXPORT("1m6DD9BBmSE", VrTracker2NotInitialized); -APS5_EXPORT("SgunslIO-C0", VrTracker2NotInitialized); -APS5_EXPORT("KMIToVnHrhc", VrTracker2NotInitialized); -APS5_EXPORT("XfEBVemeJyE", VrTracker2NotInitialized); -APS5_EXPORT("y79S3-zrJKg", VrTracker2NotInitialized); +APS5_EXPORT("1m6DD9BBmSE", VrTracker2GetRelativePose); +std::int32_t APS5_VABI VrTracker2GetRelativePose(std::int32_t firstDeviceHandle, std::int32_t secondDeviceHandle, VrTracker2RelativePoseResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("SgunslIO-C0", VrTracker2GetDiagnosticResult); +std::int32_t APS5_VABI VrTracker2GetDiagnosticResult(VrTracker2DiagnosticResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("KMIToVnHrhc", VrTracker2SetTrackingOptions); +std::int32_t APS5_VABI VrTracker2SetTrackingOptions(const VrTracker2TrackingOptions* options) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("XfEBVemeJyE", VrTracker2GetTrackingOptions); +std::int32_t APS5_VABI VrTracker2GetTrackingOptions(VrTracker2TrackingOptionsResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("y79S3-zrJKg", VrTracker2GetExtendedResult); +std::int32_t APS5_VABI VrTracker2GetExtendedResult(VrTracker2ExtendedResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("b777Kpfe2J8", VrTracker2NotInitialized); -APS5_EXPORT("UfUYgiT7VK0", VrTracker2NotInitialized); -APS5_EXPORT("MB7B6396K6U", VrTracker2NotInitialized); +APS5_EXPORT("UfUYgiT7VK0", VrTracker2GetCounter); +std::int32_t APS5_VABI VrTracker2GetCounter(std::uint64_t* counter, std::uint32_t selector) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("MB7B6396K6U", VrTracker2SetTrackingConfiguration); +std::int32_t APS5_VABI VrTracker2SetTrackingConfiguration(const VrTracker2TrackingConfiguration* configuration) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("H2Mnx7gz4ac", VrTracker2NotInitialized); APS5_EXPORT("SgNHzN6XaEc", VrTracker2NotInitialized); APS5_EXPORT("sYBNLrMxjAM", VrTracker2NotInitialized); @@ -211,7 +273,11 @@ APS5_EXPORT("914+9oJxNts", VrTracker2NotInitialized); APS5_EXPORT("c6SZtdEjH0E", VrTracker2NotInitialized); APS5_EXPORT("74IXIMe+Lto", VrTracker2NotInitialized); APS5_EXPORT("hmahOYMiYNY", VrTracker2NotInitialized); -APS5_EXPORT("pNXc6ZlLq-8", VrTracker2NotInitialized); +APS5_EXPORT("pNXc6ZlLq-8", VrTracker2GetScalarResult); +std::int32_t APS5_VABI VrTracker2GetScalarResult(VrTracker2ScalarResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("omJ7b8FqAN0", VrTracker2NotInitialized); APS5_EXPORT("G2PWj1HLqn8", VrTracker2NotInitialized); APS5_EXPORT("73zcxAFawYk", VrTracker2NotInitialized); diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index ce9f91ccc8..fc0416c069 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -8,3 +8,5 @@ - [sceVrTracker2Initialize](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the 72-byte parameter layout is recovered, including thread priorities, CPU affinity masks, work memory, size/alignment and coordinate system. Meanings of the 32-bit option at 0x30 and byte options at 0x38/0x39 remain unresolved; initialization remains unimplemented. - [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the seven anonymous throwing exports now have their consumed argument slots restored. Calibration output (`jRmuXjsknsI`) uses a 32-bit capacity with 608/1216-byte variants; its fields remain unresolved. Mesh requirements (`NBhWy+Gl0tY`) contain four 32-bit counts/sizes. The mode/buffer/capacity/size export (`9R6W33Au4B4`) uses 64-bit sizes. The 32/48-byte result parameters are recovered; result output and pose parameters still need full layouts; the telemetry input (`oZDm1bynCaA`) spans 7024 bytes. Their bodies remain unimplemented. + +- [Anonymous tracking queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) still lack initialized behavior. The paired option setter/getter consume 28/40-byte records with three integer options, a reset byte and one/two float vectors; option meanings remain unresolved. The scalar query uses an 8-byte size/float record. Diagnostic output spans 4096 bytes, extended output 0xD02E8 bytes, and tracking configuration input 1024 bytes; their fields remain unresolved. The relative-pose query uses two device handles and a 64-byte output containing a pose and 32 cleared bytes; the counter query uses a 64-bit output and a 32-bit selector whose meaning remains unresolved. Remaining aliases still need individual signatures. From 12215c498dc8023344d8023b3a40368691606f1f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:29:33 +0300 Subject: [PATCH 386/523] fix(vrtracker2): restore diagnostic and configuration arguments --- core/libs/prx/libSceVrTracker2/Export.cpp | 79 ++++++++++++++++++++--- docs/dev/debt/libSceVrTracker2.md | 2 + 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 3d67c0619a..b5499df15f 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -159,6 +159,25 @@ static_assert(offsetof(VrTracker2TrackingOptions, vector) == 16); static_assert(sizeof(VrTracker2TrackingOptionsResult) == 40); static_assert(offsetof(VrTracker2TrackingOptionsResult, position) == 28); +struct VrTracker2ModeParameter { + std::uint32_t size; + std::uint32_t mode; + float value; +}; + +struct VrTracker2DiagnosticText { + char sections[6][512]; +}; + +struct VrTracker2CompositePoseResult; +struct VrTracker2CameraImages; +struct VrTracker2SensorFrame; +struct VrTracker2TrackingStatus; +struct VrTracker2BoundaryParameter; + +static_assert(sizeof(VrTracker2ModeParameter) == 12); +static_assert(sizeof(VrTracker2DiagnosticText) == 3072); + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -250,7 +269,11 @@ std::int32_t APS5_VABI VrTracker2GetExtendedResult(VrTracker2ExtendedResult* res return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -APS5_EXPORT("b777Kpfe2J8", VrTracker2NotInitialized); +APS5_EXPORT("b777Kpfe2J8", VrTracker2GetCompositePose); +std::int32_t APS5_VABI VrTracker2GetCompositePose(const VrTracker2ResultParameter* parameter, VrTracker2CompositePoseResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("UfUYgiT7VK0", VrTracker2GetCounter); std::int32_t APS5_VABI VrTracker2GetCounter(std::uint64_t* counter, std::uint32_t selector) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; @@ -262,17 +285,53 @@ std::int32_t APS5_VABI VrTracker2SetTrackingConfiguration(const VrTracker2Tracki } APS5_EXPORT("H2Mnx7gz4ac", VrTracker2NotInitialized); -APS5_EXPORT("SgNHzN6XaEc", VrTracker2NotInitialized); -APS5_EXPORT("sYBNLrMxjAM", VrTracker2NotInitialized); -APS5_EXPORT("3gQncidodmw", VrTracker2NotInitialized); -APS5_EXPORT("qnCmEbUGqWY", VrTracker2NotInitialized); -APS5_EXPORT("oGNn7JIdMHw", VrTracker2NotInitialized); +APS5_EXPORT("SgNHzN6XaEc", VrTracker2GetTrackingConfiguration); +std::int32_t APS5_VABI VrTracker2GetTrackingConfiguration(VrTracker2TrackingConfiguration* configuration) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("sYBNLrMxjAM", VrTracker2GetCameraImages); +std::int32_t APS5_VABI VrTracker2GetCameraImages(VrTracker2CameraImages* images) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("3gQncidodmw", VrTracker2GetCameraImage); +std::int32_t APS5_VABI VrTracker2GetCameraImage(void* image, std::uint32_t size, std::uint32_t* frameId) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("qnCmEbUGqWY", VrTracker2GetDiagnosticText); +std::int32_t APS5_VABI VrTracker2GetDiagnosticText(VrTracker2DiagnosticText* text) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("oGNn7JIdMHw", VrTracker2GetSensorFrame); +std::int32_t APS5_VABI VrTracker2GetSensorFrame(VrTracker2SensorFrame* frame) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("80IxOn8swIA", VrTracker2NotInitialized); APS5_EXPORT("NTbJNAZA4ro", VrTracker2NotInitialized); -APS5_EXPORT("914+9oJxNts", VrTracker2NotInitialized); -APS5_EXPORT("c6SZtdEjH0E", VrTracker2NotInitialized); -APS5_EXPORT("74IXIMe+Lto", VrTracker2NotInitialized); -APS5_EXPORT("hmahOYMiYNY", VrTracker2NotInitialized); +APS5_EXPORT("914+9oJxNts", VrTracker2GetTrackingStatus); +std::int32_t APS5_VABI VrTracker2GetTrackingStatus(VrTracker2TrackingStatus* status) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("c6SZtdEjH0E", VrTracker2GetBoundaryParameter); +std::int32_t APS5_VABI VrTracker2GetBoundaryParameter(VrTracker2BoundaryParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("74IXIMe+Lto", VrTracker2SetBoundaryParameter); +std::int32_t APS5_VABI VrTracker2SetBoundaryParameter(const VrTracker2BoundaryParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("hmahOYMiYNY", VrTracker2SetMode); +std::int32_t APS5_VABI VrTracker2SetMode(const VrTracker2ModeParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("pNXc6ZlLq-8", VrTracker2GetScalarResult); std::int32_t APS5_VABI VrTracker2GetScalarResult(VrTracker2ScalarResult* result) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index fc0416c069..e3b07e4421 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -10,3 +10,5 @@ - [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the seven anonymous throwing exports now have their consumed argument slots restored. Calibration output (`jRmuXjsknsI`) uses a 32-bit capacity with 608/1216-byte variants; its fields remain unresolved. Mesh requirements (`NBhWy+Gl0tY`) contain four 32-bit counts/sizes. The mode/buffer/capacity/size export (`9R6W33Au4B4`) uses 64-bit sizes. The 32/48-byte result parameters are recovered; result output and pose parameters still need full layouts; the telemetry input (`oZDm1bynCaA`) spans 7024 bytes. Their bodies remain unimplemented. - [Anonymous tracking queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) still lack initialized behavior. The paired option setter/getter consume 28/40-byte records with three integer options, a reset byte and one/two float vectors; option meanings remain unresolved. The scalar query uses an 8-byte size/float record. Diagnostic output spans 4096 bytes, extended output 0xD02E8 bytes, and tracking configuration input 1024 bytes; their fields remain unresolved. The relative-pose query uses two device handles and a 64-byte output containing a pose and 32 cleared bytes; the counter query uses a 64-bit output and a 32-bit selector whose meaning remains unresolved. Remaining aliases still need individual signatures. + +- [Anonymous diagnostic exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have separate pointer contracts for composite poses, configuration, camera images, sensor frames, tracking status and boundary parameters. The camera-image query takes a 32-bit byte count and an in/out frame identifier; diagnostics use six 512-byte text sections, and the mode setter uses a 12-byte size/mode/float record. Composite pose, sensor/status, configuration and boundary fields still require reconstruction; none of these exports supports initialized operation. From a180963101be9329b3d1a2edd18ad2a11bbb580b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:30:42 +0300 Subject: [PATCH 387/523] fix(vrtracker2): define camera frame and scalar option contracts --- core/libs/prx/libSceVrTracker2/Export.cpp | 58 ++++++++++++++++++++--- docs/dev/debt/libSceVrTracker2.md | 2 + 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index b5499df15f..d74218d1f8 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -178,6 +178,22 @@ struct VrTracker2BoundaryParameter; static_assert(sizeof(VrTracker2ModeParameter) == 12); static_assert(sizeof(VrTracker2DiagnosticText) == 3072); +struct VrTracker2CameraFrame { + std::uint32_t selector; + std::uint32_t identifiers[2]; + std::uint32_t width; + std::uint32_t height; + std::uint32_t pitch; + const void* pixels; + std::int32_t bufferIndex; +}; + +struct VrTracker2CameraFrameMetadata; + +static_assert(offsetof(VrTracker2CameraFrame, pixels) == 24); +static_assert(offsetof(VrTracker2CameraFrame, bufferIndex) == 32); +static_assert(sizeof(VrTracker2CameraFrame) == 40); + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -353,15 +369,43 @@ APS5_EXPORT("4+B+TcHEHks", VrTracker2NotInitialized); APS5_EXPORT("cjDz4kYJKTw", VrTracker2NotInitialized); APS5_EXPORT("BjVg0fKV9VA", VrTracker2NotInitialized); APS5_EXPORT("oDV5j-2Luic", VrTracker2NotInitialized); -APS5_EXPORT("t+86o9sCZ-Y", VrTracker2NotInitialized); -APS5_EXPORT("TIpYP35JzUs", VrTracker2NotInitialized); -APS5_EXPORT("LrSc+KuxIkg", VrTracker2NotInitialized); -APS5_EXPORT("Ur+M5wy+QBY", VrTracker2NotInitialized); -APS5_EXPORT("OvKC351Udqo", VrTracker2NotInitialized); +APS5_EXPORT("t+86o9sCZ-Y", VrTracker2AcquireCameraFrame); +std::int32_t APS5_VABI VrTracker2AcquireCameraFrame(std::uint32_t selector, VrTracker2CameraFrame* frame) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; +APS5_EXPORT("TIpYP35JzUs", VrTracker2AcquireCameraFrameWithMetadata); +std::int32_t APS5_VABI VrTracker2AcquireCameraFrameWithMetadata(std::uint32_t selector, VrTracker2CameraFrame* frame, VrTracker2CameraFrameMetadata* metadata) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; +APS5_EXPORT("LrSc+KuxIkg", VrTracker2ReleaseCameraFrame); +std::int32_t APS5_VABI VrTracker2ReleaseCameraFrame(const VrTracker2CameraFrame* frame) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; +APS5_EXPORT("Ur+M5wy+QBY", VrTracker2SetScalarOption); +std::int32_t APS5_VABI VrTracker2SetScalarOption(float value) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; +APS5_EXPORT("OvKC351Udqo", VrTracker2GetScalarOption); +std::int32_t APS5_VABI VrTracker2GetScalarOption(float* value) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; APS5_EXPORT("LQV4nz6wteY", VrTracker2NotInitialized); APS5_EXPORT("x0pGthEwf88", VrTracker2NotInitialized); -APS5_EXPORT("d5Fjs0rHkBg", VrTracker2NotInitialized); -APS5_EXPORT("Yed3ZONcuMk", VrTracker2NotInitialized); +APS5_EXPORT("d5Fjs0rHkBg", VrTracker2SelectDevice); +std::int32_t APS5_VABI VrTracker2SelectDevice(std::int32_t deviceHandle) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; +APS5_EXPORT("Yed3ZONcuMk", VrTracker2GetSelectedDevice); +std::int32_t APS5_VABI VrTracker2GetSelectedDevice(std::int32_t* deviceHandle) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} +; APS5_EXPORT("fkptW6uRdWc", VrTracker2NotInitialized); APS5_EXPORT("HDlD-xE1Xuk", VrTracker2NotInitialized); APS5_EXPORT("IGdE6nsMTAY", VrTracker2NotInitialized); diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index e3b07e4421..3217ba4d5a 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -12,3 +12,5 @@ - [Anonymous tracking queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) still lack initialized behavior. The paired option setter/getter consume 28/40-byte records with three integer options, a reset byte and one/two float vectors; option meanings remain unresolved. The scalar query uses an 8-byte size/float record. Diagnostic output spans 4096 bytes, extended output 0xD02E8 bytes, and tracking configuration input 1024 bytes; their fields remain unresolved. The relative-pose query uses two device handles and a 64-byte output containing a pose and 32 cleared bytes; the counter query uses a 64-bit output and a 32-bit selector whose meaning remains unresolved. Remaining aliases still need individual signatures. - [Anonymous diagnostic exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have separate pointer contracts for composite poses, configuration, camera images, sensor frames, tracking status and boundary parameters. The camera-image query takes a 32-bit byte count and an in/out frame identifier; diagnostics use six 512-byte text sections, and the mode setter uses a 12-byte size/mode/float record. Composite pose, sensor/status, configuration and boundary fields still require reconstruction; none of these exports supports initialized operation. + +- [Camera-frame exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) do not acquire or release native buffers. Their descriptor has a selector, two identifiers, dimensions, pitch, pixel pointer and buffer index; native acquisition writes the first 36 bytes, leaving natural tail padding untouched. The optional 48-byte metadata record and identifier meanings remain unresolved. The scalar-option setter/getter use a float value/output, but its units and meaning remain unresolved. From dab466203693867546556cce7d04b6b13725dd7f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:32:05 +0300 Subject: [PATCH 388/523] fix(vrtracker2): restore device and spatial query signatures --- core/libs/prx/libSceVrTracker2/Export.cpp | 75 ++++++++++++++++++++--- docs/dev/debt/libSceVrTracker2.md | 2 + 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index d74218d1f8..07d244a879 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -194,6 +194,21 @@ static_assert(offsetof(VrTracker2CameraFrame, pixels) == 24); static_assert(offsetof(VrTracker2CameraFrame, bufferIndex) == 32); static_assert(sizeof(VrTracker2CameraFrame) == 40); +struct VrTracker2SpatialQueryResult { + std::uint32_t size; + std::int32_t status; + float vectors[2][3]; + float value; +}; + +struct VrTracker2DeviceCalibrationStatus; +struct VrTracker2DeviceBoundaryParameter; +struct VrTracker2ResetPoseParameter; + +static_assert(sizeof(VrTracker2SpatialQueryResult) == 36); +static_assert(offsetof(VrTracker2SpatialQueryResult, vectors) == 8); +static_assert(offsetof(VrTracker2SpatialQueryResult, value) == 32); + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -406,16 +421,56 @@ std::int32_t APS5_VABI VrTracker2GetSelectedDevice(std::int32_t* deviceHandle) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } ; -APS5_EXPORT("fkptW6uRdWc", VrTracker2NotInitialized); -APS5_EXPORT("HDlD-xE1Xuk", VrTracker2NotInitialized); -APS5_EXPORT("IGdE6nsMTAY", VrTracker2NotInitialized); -APS5_EXPORT("DSTergmOvvE", VrTracker2NotInitialized); -APS5_EXPORT("B7hPwvvg79E", VrTracker2NotInitialized); -APS5_EXPORT("ZwnIsDe1eNE", VrTracker2NotInitialized); -APS5_EXPORT("B2vw+s-0Dfo", VrTracker2NotInitialized); -APS5_EXPORT("4LNTLivfRuA", VrTracker2NotInitialized); -APS5_EXPORT("edajhZhz9Uo", VrTracker2NotInitialized); -APS5_EXPORT("QS+7oqiszuc", VrTracker2NotInitialized); +APS5_EXPORT("fkptW6uRdWc", VrTracker2ResetDevicePose); +std::int32_t APS5_VABI VrTracker2ResetDevicePose(std::int32_t deviceHandle) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("HDlD-xE1Xuk", VrTracker2ResetPose); +std::int32_t APS5_VABI VrTracker2ResetPose(const VrTracker2ResetPoseParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("IGdE6nsMTAY", VrTracker2QueryPosition); +std::int32_t APS5_VABI VrTracker2QueryPosition(const float* position, VrTracker2SpatialQueryResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("DSTergmOvvE", VrTracker2QueryDevicePosition); +std::int32_t APS5_VABI VrTracker2QueryDevicePosition(std::int32_t deviceHandle, VrTracker2SpatialQueryResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("B7hPwvvg79E", VrTracker2GetTrackingBounds); +std::int32_t APS5_VABI VrTracker2GetTrackingBounds(VrTracker2OrientedBoundingBox* bounds) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("ZwnIsDe1eNE", VrTracker2GetDeviceState); +std::int32_t APS5_VABI VrTracker2GetDeviceState(std::int32_t deviceHandle, std::int32_t* state) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("B2vw+s-0Dfo", VrTracker2GetDeviceCalibrationStatus); +std::int32_t APS5_VABI VrTracker2GetDeviceCalibrationStatus(std::int32_t deviceHandle, VrTracker2DeviceCalibrationStatus* status) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("4LNTLivfRuA", VrTracker2SetDeviceBoundary); +std::int32_t APS5_VABI VrTracker2SetDeviceBoundary(std::int32_t deviceHandle, std::int32_t selector, const VrTracker2DeviceBoundaryParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("edajhZhz9Uo", VrTracker2GetDeviceBoundary); +std::int32_t APS5_VABI VrTracker2GetDeviceBoundary(std::int32_t deviceHandle, std::int32_t selector, VrTracker2DeviceBoundaryParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("QS+7oqiszuc", VrTracker2ApplyDeviceBoundary); +std::int32_t APS5_VABI VrTracker2ApplyDeviceBoundary(std::int32_t deviceHandle, std::int32_t selector) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("-dDyvzd2jf0", VrTracker2NotInitialized); APS5_EXPORT("k8JDLkCrOgs", VrTracker2NotInitialized); APS5_EXPORT("-HFx+dnDSOI", VrTracker2NotInitialized); diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 3217ba4d5a..58e11d0671 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -14,3 +14,5 @@ - [Anonymous diagnostic exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have separate pointer contracts for composite poses, configuration, camera images, sensor frames, tracking status and boundary parameters. The camera-image query takes a 32-bit byte count and an in/out frame identifier; diagnostics use six 512-byte text sections, and the mode setter uses a 12-byte size/mode/float record. Composite pose, sensor/status, configuration and boundary fields still require reconstruction; none of these exports supports initialized operation. - [Camera-frame exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) do not acquire or release native buffers. Their descriptor has a selector, two identifiers, dimensions, pitch, pixel pointer and buffer index; native acquisition writes the first 36 bytes, leaving natural tail padding untouched. The optional 48-byte metadata record and identifier meanings remain unresolved. The scalar-option setter/getter use a float value/output, but its units and meaning remain unresolved. + +- [Device and spatial queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Position queries use a three-float input or a device handle and a 36-byte size/status/two-vector/scalar result; the vector and scalar meanings remain unresolved. The bounds query uses the recovered 80-byte oriented box. Device-boundary operations consume a handle and selector; their internal parameter layout differs from the public boundary record and remains unresolved, as do the 28-byte device-calibration status and reset-pose input. From ea06e78756cd86aaa1774830a7918de8df468420 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:33:30 +0300 Subject: [PATCH 389/523] fix(vrtracker2): restore map and tracking flag arguments --- core/libs/prx/libSceVrTracker2/Export.cpp | 33 +++++++++++++++++++---- docs/dev/debt/libSceVrTracker2.md | 2 ++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 07d244a879..c029429275 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -209,6 +209,9 @@ static_assert(sizeof(VrTracker2SpatialQueryResult) == 36); static_assert(offsetof(VrTracker2SpatialQueryResult, vectors) == 8); static_assert(offsetof(VrTracker2SpatialQueryResult, value) == 32); +struct VrTracker2MapData; +struct VrTracker2MapStatus; + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -471,15 +474,27 @@ std::int32_t APS5_VABI VrTracker2ApplyDeviceBoundary(std::int32_t deviceHandle, return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -APS5_EXPORT("-dDyvzd2jf0", VrTracker2NotInitialized); -APS5_EXPORT("k8JDLkCrOgs", VrTracker2NotInitialized); +APS5_EXPORT("-dDyvzd2jf0", VrTracker2GetDeviceMapState); +std::int32_t APS5_VABI VrTracker2GetDeviceMapState(std::int32_t deviceHandle, std::int32_t* state) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("k8JDLkCrOgs", VrTracker2InitializeMap); +std::int32_t APS5_VABI VrTracker2InitializeMap(std::int32_t deviceHandle, const float* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("-HFx+dnDSOI", VrTracker2NotInitialized); APS5_EXPORT("wgDeDXirl0g", VrTracker2NotInitialized); APS5_EXPORT("a1kWSTYGBxg", VrTracker2NotInitialized); APS5_EXPORT("zUwxBEKr3fc", VrTracker2NotInitialized); APS5_EXPORT("TC7VTVzj0xc", VrTracker2NotInitialized); APS5_EXPORT("1DwROxtuANs", VrTracker2NotInitialized); -APS5_EXPORT("wwbt4s8cbMQ", VrTracker2NotInitialized); +APS5_EXPORT("wwbt4s8cbMQ", VrTracker2GetTrackingFlags); +std::int32_t APS5_VABI VrTracker2GetTrackingFlags(bool* active, std::uint8_t* flags) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("eGxBJ0IolOA", VrTracker2NotInitialized); APS5_EXPORT("9BaFhspHF+E", VrTracker2NotInitialized); APS5_EXPORT("lb9q5nJ4vzk", VrTracker2NotInitialized); @@ -487,7 +502,11 @@ APS5_EXPORT("ZsD1-thjXS4", VrTracker2NotInitialized); APS5_EXPORT("QdHQnM0XICI", VrTracker2NotInitialized); APS5_EXPORT("Ek4WMOvwv6g", VrTracker2NotInitialized); APS5_EXPORT("W1YAO6G+hNc", VrTracker2NotInitialized); -APS5_EXPORT("HAJB7irjl+0", VrTracker2NotInitialized); +APS5_EXPORT("HAJB7irjl+0", VrTracker2GetMapStatus); +std::int32_t APS5_VABI VrTracker2GetMapStatus(VrTracker2MapStatus* status) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("x2mN50nKScs", VrTracker2NotInitialized); APS5_EXPORT("cCKExge5d3A", VrTracker2NotInitialized); APS5_EXPORT("gn6e5I4g9bw", VrTracker2NotInitialized); @@ -499,7 +518,11 @@ APS5_EXPORT("DBa3wov7dKQ", VrTracker2NotInitialized); APS5_EXPORT("ojTGClQ52r4", VrTracker2NotInitialized); APS5_EXPORT("oOLE62H66z0", VrTracker2NotInitialized); APS5_EXPORT("MHekhnRBKAw", VrTracker2NotInitialized); -APS5_EXPORT("JnSmj+5E8WY", VrTracker2NotInitialized); +APS5_EXPORT("JnSmj+5E8WY", VrTracker2GetMapData); +std::int32_t APS5_VABI VrTracker2GetMapData(VrTracker2MapData* data, std::uint32_t size) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("N1P+melAgb8", VrTracker2NotInitialized); APS5_EXPORT("-e7A3rAM3Yk", VrTracker2NotInitialized); APS5_EXPORT("RefT1spBleg", VrTracker2NotInitialized); diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 58e11d0671..de8e5e0a03 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -16,3 +16,5 @@ - [Camera-frame exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) do not acquire or release native buffers. Their descriptor has a selector, two identifiers, dimensions, pitch, pixel pointer and buffer index; native acquisition writes the first 36 bytes, leaving natural tail padding untouched. The optional 48-byte metadata record and identifier meanings remain unresolved. The scalar-option setter/getter use a float value/output, but its units and meaning remain unresolved. - [Device and spatial queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Position queries use a three-float input or a device handle and a 36-byte size/status/two-vector/scalar result; the vector and scalar meanings remain unresolved. The bounds query uses the recovered 80-byte oriented box. Device-boundary operations consume a handle and selector; their internal parameter layout differs from the public boundary record and remains unresolved, as do the 28-byte device-calibration status and reset-pose input. + +- [Map and flag queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Map initialization takes a device handle and a pointer to one float; map data retrieval takes an output and a 32-bit size constrained to 2048. Map data and status field meanings remain unresolved. Tracking flags use a required boolean output and an optional byte output, rather than an argument-free alias. From eab1127190ab8b69095ed0f776e03bc9653b841f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:34:17 +0300 Subject: [PATCH 390/523] fix(vrtracker2): separate diagnostic record pointer contracts --- core/libs/prx/libSceVrTracker2/Export.cpp | 63 +++++++++++++++++++---- docs/dev/debt/libSceVrTracker2.md | 2 + 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index c029429275..931fa8a418 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -212,6 +212,15 @@ static_assert(offsetof(VrTracker2SpatialQueryResult, value) == 32); struct VrTracker2MapData; struct VrTracker2MapStatus; +struct VrTracker2RuntimeStatus; +struct VrTracker2DiagnosticState; +struct VrTracker2ExtendedDiagnosticState; +struct VrTracker2SensorDiagnostics; +struct VrTracker2DeviceDiagnostics; +struct VrTracker2TimingDiagnostics; +struct VrTracker2MapConfiguration; +struct VrTracker2SensorSummary; + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -489,19 +498,51 @@ APS5_EXPORT("wgDeDXirl0g", VrTracker2NotInitialized); APS5_EXPORT("a1kWSTYGBxg", VrTracker2NotInitialized); APS5_EXPORT("zUwxBEKr3fc", VrTracker2NotInitialized); APS5_EXPORT("TC7VTVzj0xc", VrTracker2NotInitialized); -APS5_EXPORT("1DwROxtuANs", VrTracker2NotInitialized); +APS5_EXPORT("1DwROxtuANs", VrTracker2GetRuntimeStatus); +std::int32_t APS5_VABI VrTracker2GetRuntimeStatus(VrTracker2RuntimeStatus* status) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("wwbt4s8cbMQ", VrTracker2GetTrackingFlags); std::int32_t APS5_VABI VrTracker2GetTrackingFlags(bool* active, std::uint8_t* flags) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -APS5_EXPORT("eGxBJ0IolOA", VrTracker2NotInitialized); -APS5_EXPORT("9BaFhspHF+E", VrTracker2NotInitialized); -APS5_EXPORT("lb9q5nJ4vzk", VrTracker2NotInitialized); -APS5_EXPORT("ZsD1-thjXS4", VrTracker2NotInitialized); -APS5_EXPORT("QdHQnM0XICI", VrTracker2NotInitialized); -APS5_EXPORT("Ek4WMOvwv6g", VrTracker2NotInitialized); -APS5_EXPORT("W1YAO6G+hNc", VrTracker2NotInitialized); +APS5_EXPORT("eGxBJ0IolOA", VrTracker2GetDiagnosticState); +std::int32_t APS5_VABI VrTracker2GetDiagnosticState(VrTracker2DiagnosticState* state) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("9BaFhspHF+E", VrTracker2GetExtendedDiagnosticState); +std::int32_t APS5_VABI VrTracker2GetExtendedDiagnosticState(VrTracker2ExtendedDiagnosticState* state) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("lb9q5nJ4vzk", VrTracker2GetSensorDiagnostics); +std::int32_t APS5_VABI VrTracker2GetSensorDiagnostics(VrTracker2SensorDiagnostics* diagnostics) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("ZsD1-thjXS4", VrTracker2GetDeviceDiagnostics); +std::int32_t APS5_VABI VrTracker2GetDeviceDiagnostics(VrTracker2DeviceDiagnostics* diagnostics) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("QdHQnM0XICI", VrTracker2GetTimingDiagnostics); +std::int32_t APS5_VABI VrTracker2GetTimingDiagnostics(VrTracker2TimingDiagnostics* diagnostics) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("Ek4WMOvwv6g", VrTracker2GetMapConfiguration); +std::int32_t APS5_VABI VrTracker2GetMapConfiguration(VrTracker2MapConfiguration* configuration) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("W1YAO6G+hNc", VrTracker2SetMapConfiguration); +std::int32_t APS5_VABI VrTracker2SetMapConfiguration(const VrTracker2MapConfiguration* configuration) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("HAJB7irjl+0", VrTracker2GetMapStatus); std::int32_t APS5_VABI VrTracker2GetMapStatus(VrTracker2MapStatus* status) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; @@ -526,7 +567,11 @@ std::int32_t APS5_VABI VrTracker2GetMapData(VrTracker2MapData* data, std::uint32 APS5_EXPORT("N1P+melAgb8", VrTracker2NotInitialized); APS5_EXPORT("-e7A3rAM3Yk", VrTracker2NotInitialized); APS5_EXPORT("RefT1spBleg", VrTracker2NotInitialized); -APS5_EXPORT("DsOfryhbm4U", VrTracker2NotInitialized); +APS5_EXPORT("DsOfryhbm4U", VrTracker2GetSensorSummary); +std::int32_t APS5_VABI VrTracker2GetSensorSummary(VrTracker2SensorSummary* summary) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + std::int32_t APS5_VABI VrTracker2NotInitialized() { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index de8e5e0a03..f1a9ea843c 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -18,3 +18,5 @@ - [Device and spatial queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Position queries use a three-float input or a device handle and a 36-byte size/status/two-vector/scalar result; the vector and scalar meanings remain unresolved. The bounds query uses the recovered 80-byte oriented box. Device-boundary operations consume a handle and selector; their internal parameter layout differs from the public boundary record and remains unresolved, as do the 28-byte device-calibration status and reset-pose input. - [Map and flag queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Map initialization takes a device handle and a pointer to one float; map data retrieval takes an output and a 32-bit size constrained to 2048. Map data and status field meanings remain unresolved. Tracking flags use a required boolean output and an optional byte output, rather than an argument-free alias. + +- [Diagnostic records](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have individual output-pointer signatures, but field interpretation remains incomplete. Native runtime status clears 88 bytes; diagnostic state writes 120 bytes, sensor diagnostics reach byte 339, device diagnostics reach byte 6983, timing diagnostics write 80 bytes, and sensor summary writes 32 bytes. The map-configuration getter/setter exchange the same 72-byte record. These exports still return only the uninitialized error. From e1cfd8adfba8f39fb94b96572154593019a00b2c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:35:47 +0300 Subject: [PATCH 391/523] fix(vrtracker2): restore coordinate and boundary fitting contracts --- core/libs/prx/libSceVrTracker2/Export.cpp | 63 +++++++++++++++++++---- docs/dev/debt/libSceVrTracker2.md | 4 +- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 931fa8a418..0f5bf941c7 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -221,6 +221,29 @@ struct VrTracker2TimingDiagnostics; struct VrTracker2MapConfiguration; struct VrTracker2SensorSummary; +struct VrTracker2CoordinateResultParameter { + std::uint32_t coordinateSystem; + std::uint32_t reserved; + VrTracker2ResultParameter parameter; +}; + +struct VrTracker2BoundaryFitResult { + std::uint8_t flags[4]; + float dimensions[4]; +}; + +struct VrTracker2VectorResult { + std::uint32_t size; + float value[3]; +}; + +struct VrTracker2ExtendedTrackingResult; + +static_assert(sizeof(VrTracker2CoordinateResultParameter) == 56); +static_assert(offsetof(VrTracker2CoordinateResultParameter, parameter) == 8); +static_assert(sizeof(VrTracker2BoundaryFitResult) == 20); +static_assert(sizeof(VrTracker2VectorResult) == 16); + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -288,7 +311,7 @@ std::int32_t APS5_VABI sceVrTracker2Initialize(const VrTracker2InitializeParamet } APS5_EXPORT("1m6DD9BBmSE", VrTracker2GetRelativePose); -std::int32_t APS5_VABI VrTracker2GetRelativePose(std::int32_t firstDeviceHandle, std::int32_t secondDeviceHandle, VrTracker2RelativePoseResult* result) { +std::int32_t APS5_VABI VrTracker2GetRelativePose(std::uint32_t firstCoordinateSystem, std::uint32_t secondCoordinateSystem, VrTracker2RelativePoseResult* result) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } @@ -423,13 +446,13 @@ std::int32_t APS5_VABI VrTracker2GetScalarOption(float* value) { ; APS5_EXPORT("LQV4nz6wteY", VrTracker2NotInitialized); APS5_EXPORT("x0pGthEwf88", VrTracker2NotInitialized); -APS5_EXPORT("d5Fjs0rHkBg", VrTracker2SelectDevice); -std::int32_t APS5_VABI VrTracker2SelectDevice(std::int32_t deviceHandle) { +APS5_EXPORT("d5Fjs0rHkBg", VrTracker2SelectCoordinateSystem); +std::int32_t APS5_VABI VrTracker2SelectCoordinateSystem(std::uint32_t coordinateSystem) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } ; -APS5_EXPORT("Yed3ZONcuMk", VrTracker2GetSelectedDevice); -std::int32_t APS5_VABI VrTracker2GetSelectedDevice(std::int32_t* deviceHandle) { +APS5_EXPORT("Yed3ZONcuMk", VrTracker2GetSelectedCoordinateSystem); +std::int32_t APS5_VABI VrTracker2GetSelectedCoordinateSystem(std::uint32_t* coordinateSystem) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } ; @@ -493,11 +516,31 @@ std::int32_t APS5_VABI VrTracker2InitializeMap(std::int32_t deviceHandle, const return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -APS5_EXPORT("-HFx+dnDSOI", VrTracker2NotInitialized); -APS5_EXPORT("wgDeDXirl0g", VrTracker2NotInitialized); -APS5_EXPORT("a1kWSTYGBxg", VrTracker2NotInitialized); -APS5_EXPORT("zUwxBEKr3fc", VrTracker2NotInitialized); -APS5_EXPORT("TC7VTVzj0xc", VrTracker2NotInitialized); +APS5_EXPORT("-HFx+dnDSOI", VrTracker2GetCoordinateTrackingResult); +std::int32_t APS5_VABI VrTracker2GetCoordinateTrackingResult(const VrTracker2CoordinateResultParameter* parameter, VrTracker2ExtendedTrackingResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("wgDeDXirl0g", VrTracker2GetExtendedTrackingResult); +std::int32_t APS5_VABI VrTracker2GetExtendedTrackingResult(const VrTracker2ResultParameter* parameter, VrTracker2ExtendedTrackingResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("a1kWSTYGBxg", VrTracker2FitBoundary); +std::int32_t APS5_VABI VrTracker2FitBoundary(const VrTracker2BoundaryGeometry* boundary, VrTracker2BoundaryFitResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("zUwxBEKr3fc", VrTracker2FitBoundaryWithBounds); +std::int32_t APS5_VABI VrTracker2FitBoundaryWithBounds(const VrTracker2BoundaryGeometry* boundary, VrTracker2OrientedBoundingBox* firstBounds, VrTracker2OrientedBoundingBox* secondBounds, VrTracker2BoundaryFitResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("TC7VTVzj0xc", VrTracker2GetDeviceCenter); +std::int32_t APS5_VABI VrTracker2GetDeviceCenter(std::int32_t deviceHandle, VrTracker2VectorResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("1DwROxtuANs", VrTracker2GetRuntimeStatus); std::int32_t APS5_VABI VrTracker2GetRuntimeStatus(VrTracker2RuntimeStatus* status) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index f1a9ea843c..55cb5b67a7 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -9,7 +9,7 @@ - [sceVrTracker2Initialize](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the 72-byte parameter layout is recovered, including thread priorities, CPU affinity masks, work memory, size/alignment and coordinate system. Meanings of the 32-bit option at 0x30 and byte options at 0x38/0x39 remain unresolved; initialization remains unimplemented. - [libSceVrTracker2](../../../core/libs/prx/libSceVrTracker2/Export.cpp) - the seven anonymous throwing exports now have their consumed argument slots restored. Calibration output (`jRmuXjsknsI`) uses a 32-bit capacity with 608/1216-byte variants; its fields remain unresolved. Mesh requirements (`NBhWy+Gl0tY`) contain four 32-bit counts/sizes. The mode/buffer/capacity/size export (`9R6W33Au4B4`) uses 64-bit sizes. The 32/48-byte result parameters are recovered; result output and pose parameters still need full layouts; the telemetry input (`oZDm1bynCaA`) spans 7024 bytes. Their bodies remain unimplemented. -- [Anonymous tracking queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) still lack initialized behavior. The paired option setter/getter consume 28/40-byte records with three integer options, a reset byte and one/two float vectors; option meanings remain unresolved. The scalar query uses an 8-byte size/float record. Diagnostic output spans 4096 bytes, extended output 0xD02E8 bytes, and tracking configuration input 1024 bytes; their fields remain unresolved. The relative-pose query uses two device handles and a 64-byte output containing a pose and 32 cleared bytes; the counter query uses a 64-bit output and a 32-bit selector whose meaning remains unresolved. Remaining aliases still need individual signatures. +- [Anonymous tracking queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) still lack initialized behavior. The paired option setter/getter consume 28/40-byte records with three integer options, a reset byte and one/two float vectors; option meanings remain unresolved. The scalar query uses an 8-byte size/float record. Diagnostic output spans 4096 bytes, extended output 0xD02E8 bytes, and tracking configuration input 1024 bytes; their fields remain unresolved. The relative-pose query uses two coordinate-system selectors and a 64-byte output containing a pose and 32 cleared bytes; the counter query uses a 64-bit output and a 32-bit selector whose meaning remains unresolved. Remaining aliases still need individual signatures. - [Anonymous diagnostic exports](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have separate pointer contracts for composite poses, configuration, camera images, sensor frames, tracking status and boundary parameters. The camera-image query takes a 32-bit byte count and an in/out frame identifier; diagnostics use six 512-byte text sections, and the mode setter uses a 12-byte size/mode/float record. Composite pose, sensor/status, configuration and boundary fields still require reconstruction; none of these exports supports initialized operation. @@ -20,3 +20,5 @@ - [Map and flag queries](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Map initialization takes a device handle and a pointer to one float; map data retrieval takes an output and a 32-bit size constrained to 2048. Map data and status field meanings remain unresolved. Tracking flags use a required boolean output and an optional byte output, rather than an argument-free alias. - [Diagnostic records](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have individual output-pointer signatures, but field interpretation remains incomplete. Native runtime status clears 88 bytes; diagnostic state writes 120 bytes, sensor diagnostics reach byte 339, device diagnostics reach byte 6983, timing diagnostics write 80 bytes, and sensor summary writes 32 bytes. The map-configuration getter/setter exchange the same 72-byte record. These exports still return only the uninitialized error. + +- [Extended result and boundary fitting](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. The extended result spans 816 bytes; its coordinate-specific parameter prefixes the 48-byte query with a coordinate selector and four-byte padding. Boundary fitting takes the recovered point list, optionally returns two 80-byte bounds, and writes four byte flags plus four floats; flag and dimension semantics remain unresolved. The device-center output is a 16-byte size/three-float record. From 6efa62b7fa717cf6aa8bf589d16c96bfcde5de47 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:37:27 +0300 Subject: [PATCH 392/523] fix(vrtracker2): restore mesh and image pose signatures --- core/libs/prx/libSceVrTracker2/Export.cpp | 41 +++++++++++++++++++---- docs/dev/debt/libSceVrTracker2.md | 2 ++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index 0f5bf941c7..db5458a308 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -244,6 +244,11 @@ static_assert(offsetof(VrTracker2CoordinateResultParameter, parameter) == 8); static_assert(sizeof(VrTracker2BoundaryFitResult) == 20); static_assert(sizeof(VrTracker2VectorResult) == 16); +struct VrTracker2MeshVertex; +struct VrTracker2TimedPoseResult; +struct VrTracker2ImagePoseResult; +struct VrTracker2ImageFrame; + extern "C" { std::int32_t APS5_VABI sceVrTracker2Finalize() { @@ -599,17 +604,41 @@ APS5_EXPORT("loU5lT92Zrw", VrTracker2NotInitialized); APS5_EXPORT("o6fsgA3R2Yc", VrTracker2NotInitialized); APS5_EXPORT("AeBtEIE0idk", VrTracker2NotInitialized); APS5_EXPORT("DBa3wov7dKQ", VrTracker2NotInitialized); -APS5_EXPORT("ojTGClQ52r4", VrTracker2NotInitialized); -APS5_EXPORT("oOLE62H66z0", VrTracker2NotInitialized); -APS5_EXPORT("MHekhnRBKAw", VrTracker2NotInitialized); +APS5_EXPORT("ojTGClQ52r4", VrTracker2BuildMesh); +std::int32_t APS5_VABI VrTracker2BuildMesh(VrTracker2MeshVertex* vertices, std::uint16_t* indices, std::uint32_t format) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("oOLE62H66z0", VrTracker2GetMapScalar); +std::int32_t APS5_VABI VrTracker2GetMapScalar(float* value, const VrTracker2MapData* data) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("MHekhnRBKAw", VrTracker2GetTimedPose); +std::int32_t APS5_VABI VrTracker2GetTimedPose(VrTracker2TimedPoseResult* result, std::uint64_t timestamp, std::uint64_t userTag) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("JnSmj+5E8WY", VrTracker2GetMapData); std::int32_t APS5_VABI VrTracker2GetMapData(VrTracker2MapData* data, std::uint32_t size) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -APS5_EXPORT("N1P+melAgb8", VrTracker2NotInitialized); -APS5_EXPORT("-e7A3rAM3Yk", VrTracker2NotInitialized); -APS5_EXPORT("RefT1spBleg", VrTracker2NotInitialized); +APS5_EXPORT("N1P+melAgb8", VrTracker2GetPoseScalar); +std::int32_t APS5_VABI VrTracker2GetPoseScalar(float* value) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("-e7A3rAM3Yk", VrTracker2SubmitImageFrame); +std::int32_t APS5_VABI VrTracker2SubmitImageFrame(const VrTracker2ImageFrame* frame) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("RefT1spBleg", VrTracker2GetImagePose); +std::int32_t APS5_VABI VrTracker2GetImagePose(VrTracker2ImagePoseResult* result) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("DsOfryhbm4U", VrTracker2GetSensorSummary); std::int32_t APS5_VABI VrTracker2GetSensorSummary(VrTracker2SensorSummary* summary) { return VR_TRACKER2_ERROR_NOT_INITIALIZED; diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 55cb5b67a7..8e138fc9db 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -22,3 +22,5 @@ - [Diagnostic records](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have individual output-pointer signatures, but field interpretation remains incomplete. Native runtime status clears 88 bytes; diagnostic state writes 120 bytes, sensor diagnostics reach byte 339, device diagnostics reach byte 6983, timing diagnostics write 80 bytes, and sensor summary writes 32 bytes. The map-configuration getter/setter exchange the same 72-byte record. These exports still return only the uninitialized error. - [Extended result and boundary fitting](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. The extended result spans 816 bytes; its coordinate-specific parameter prefixes the 48-byte query with a coordinate selector and four-byte padding. Boundary fitting takes the recovered point list, optionally returns two 80-byte bounds, and writes four byte flags plus four floats; flag and dimension semantics remain unresolved. The device-center output is a 16-byte size/three-float record. + +- [Mesh and image-pose utilities](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Mesh generation takes vertex and 16-bit index outputs plus a 32-bit format. Timed-pose queries take an in/out result, timestamp and user tag. Image submission consumes a record with format at byte 16 and a data pointer at byte 80; image-pose retrieval uses an in/out record with a buffer pointer and capacity. Record fields and initialized algorithms remain incomplete. Scalar queries use float outputs; the map-scalar query also consumes map data. From 23478ef090f024b8b74efac0dd69fb798ddd179f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:43:01 +0300 Subject: [PATCH 393/523] fix(jpegenc): validate restart interval bounds --- core/libs/prx/libSceJpegEnc/Export.cpp | 14 ++++++-------- core/libs/tests/GuestJpegEnc.cpp | 5 +++-- docs/dev/debt/libSceJpegEnc.md | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceJpegEnc/Export.cpp b/core/libs/prx/libSceJpegEnc/Export.cpp index 6de513f53d..4ebcc09712 100644 --- a/core/libs/prx/libSceJpegEnc/Export.cpp +++ b/core/libs/prx/libSceJpegEnc/Export.cpp @@ -82,7 +82,7 @@ std::int32_t validateEncodeParam(const JpegEncEncodeParam* param) { if (param->sampling_type != SAMPLING_TYPE_FULL && param->sampling_type != SAMPLING_TYPE_422 && param->sampling_type != SAMPLING_TYPE_420) { return SCE_JPEG_ENC_ERROR_INVALID_PARAM; } - if (param->restart_interval > static_cast(MAX_IMAGE_DIMENSION)) return SCE_JPEG_ENC_ERROR_INVALID_PARAM; + if (param->restart_interval < RESTART_INTERVAL_PER_ROW || param->restart_interval > static_cast(MAX_IMAGE_DIMENSION)) return SCE_JPEG_ENC_ERROR_INVALID_PARAM; switch (param->pixel_format) { case PIXEL_FORMAT_R8G8B8A8: @@ -186,25 +186,23 @@ int32_t APS5_VABI sceJpegEncDelete(void* handle) { return 0; } -int32_t APS5_VABI sceJpegEncEncode(void* handle, const JpegEncEncodeParam* param, JpegEncOutputInfo* output_info) { +int32_t APS5_VABI sceJpegEncEncode(void* handle, const JpegEncEncodeParam* param, JpegEncOutputInfo* outputInfo) { if (!toEncoder(handle)) return SCE_JPEG_ENC_ERROR_INVALID_HANDLE; const std::int32_t result = validateEncodeParam(param); if (result != 0) return result; if (param->encode_mode == ENCODE_MODE_MJPEG) throw std::runtime_error("sceJpegEncEncode: MJPEG encode mode is not implemented"); - if (param->restart_interval < RESTART_INTERVAL_PER_ROW) throw std::runtime_error("sceJpegEncEncode: unknown negative restart interval"); const std::uint32_t channels = param->pixel_format == PIXEL_FORMAT_Y8 ? 1 : 3; const std::vector pixels = toPackedPixels(*param, channels); const std::uint32_t restartBlocks = param->restart_interval > 0 ? static_cast(param->restart_interval) : 0; const std::uint32_t restartRows = param->restart_interval == RESTART_INTERVAL_PER_ROW ? 1 : 0; - const std::vector jpeg = Decoder::Jpeg::Encode(pixels, param->image_width, param->image_height, channels, toQuality(*param), toSampling(*param), - restartBlocks, restartRows); + const std::vector jpeg = Decoder::Jpeg::Encode(pixels, param->image_width, param->image_height, channels, toQuality(*param), toSampling(*param), restartBlocks, restartRows); if (jpeg.size() > param->jpeg_size) return SCE_JPEG_ENC_ERROR_INVALID_SIZE; std::memcpy(param->jpeg, jpeg.data(), jpeg.size()); - if (output_info) { - output_info->size = static_cast(jpeg.size()); - output_info->height = param->image_height; + if (outputInfo) { + outputInfo->size = static_cast(jpeg.size()); + outputInfo->height = param->image_height; } return 0; } diff --git a/core/libs/tests/GuestJpegEnc.cpp b/core/libs/tests/GuestJpegEnc.cpp index 52437df7a5..8206423e5f 100644 --- a/core/libs/tests/GuestJpegEnc.cpp +++ b/core/libs/tests/GuestJpegEnc.cpp @@ -278,8 +278,9 @@ int main() { Require(encodeWith([](JpegEncEncodeParam& p) { p.image_width = 0; }) == invalidParam); Require(ThrowsRuntimeError([&] { encodeWith([](JpegEncEncodeParam& p) { p.encode_mode = 1; }); })); - Require(ThrowsRuntimeError([&] { encodeWith([](JpegEncEncodeParam& p) { p.restart_interval = -2; }); })); - Require(ThrowsRuntimeError([&] { encodeWith([](JpegEncEncodeParam& p) { p.restart_interval = INT32_MIN; }); })); + Require(encodeWith([](JpegEncEncodeParam& p) { p.restart_interval = -2; }) == invalidParam); + Require(encodeWith([](JpegEncEncodeParam& p) { p.restart_interval = INT32_MIN; }) == invalidParam); + Require(encodeWith([](JpegEncEncodeParam& p) { p.encode_mode = 1; p.restart_interval = -2; }) == invalidParam); constexpr std::uint32_t wideWidth = 48; constexpr std::uint32_t wideHeight = 32; diff --git a/docs/dev/debt/libSceJpegEnc.md b/docs/dev/debt/libSceJpegEnc.md index 2bff53d167..d64816c332 100644 --- a/docs/dev/debt/libSceJpegEnc.md +++ b/docs/dev/debt/libSceJpegEnc.md @@ -1,5 +1,5 @@ # libSceJpegEnc technical debt -### Unknown function info +### Unimplemented functionality -- [sceJpegEncEncode](../../../core/libs/prx/libSceJpegEnc/Export.cpp) (libSceJpegEnc) - `restart_interval` follows SharpProspero's field doc (0 none, -1 every row of blocks, positive a block count); a positive value is taken as the JPEG DRI interval in MCUs and -1 as one restart per MCU row, which no reference confirms. Other negative values throw +- [sceJpegEncEncode](../../../core/libs/prx/libSceJpegEnc/Export.cpp) (libSceJpegEnc) - MJPEG mode throws; its encoding path is not implemented. Restart intervals are confirmed by the native validator, MCU counter and DRI writer: 0 disables restart markers, -1 selects one MCU row, and 1 through 65535 specify an MCU count. Other values return the invalid-parameter error. From 65f686c33672c51d7e307b6f9e47f9d781f96c9d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:44:50 +0300 Subject: [PATCH 394/523] fix(jpegdec): restore error codes and work memory contract --- core/libs/prx/libSceJpegDec/Export.cpp | 55 +++++++++++++++----------- core/libs/tests/GuestJpegDec.cpp | 37 ++++++++++------- docs/dev/debt/libSceJpegDec.md | 2 +- 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/core/libs/prx/libSceJpegDec/Export.cpp b/core/libs/prx/libSceJpegDec/Export.cpp index 19ec61c18b..5232a524e1 100644 --- a/core/libs/prx/libSceJpegDec/Export.cpp +++ b/core/libs/prx/libSceJpegDec/Export.cpp @@ -18,18 +18,18 @@ static_assert(sizeof(JpegDecImageInfo) == 0x24); namespace { -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_ADDR = static_cast(0x80690001); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_SIZE = static_cast(0x80690002); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_PARAM = static_cast(0x80690003); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_HANDLE = static_cast(0x80690004); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_WORK_MEMORY = static_cast(0x80690005); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_DATA = static_cast(0x80690010); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_DECODE_ERROR = static_cast(0x80690012); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_ADDR = static_cast(0x80650001); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_SIZE = static_cast(0x80650002); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_PARAM = static_cast(0x80650003); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_HANDLE = static_cast(0x80650004); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_WORK_MEMORY = static_cast(0x80650005); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_DATA = static_cast(0x80650010); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_DECODE_ERROR = static_cast(0x80650012); constexpr std::uint32_t ATTRIBUTE_CSC_ORDINARY = 1; constexpr std::uint32_t ATTRIBUTE_CSC_FULL = 2; -constexpr std::uint32_t MEMORY_SIZE = 0x20; -constexpr std::uintptr_t HANDLE_ALIGNMENT = 8; +constexpr std::uint32_t MEMORY_OVERHEAD = 29536; +constexpr std::uintptr_t HANDLE_ALIGNMENT = 32; constexpr std::uint16_t DECODE_MODE_NORMAL = 0; constexpr std::uint16_t DOWN_SCALE_NONE = 1; @@ -48,17 +48,24 @@ constexpr std::uint32_t MAX_PACKED_DIMENSION = 32767; struct Context { Context* self; std::uint32_t attribute; + std::uint32_t maxImageWidth; }; -static_assert(sizeof(Context) + HANDLE_ALIGNMENT - 1 <= MEMORY_SIZE); +static_assert(sizeof(Context) + HANDLE_ALIGNMENT - 1 <= MEMORY_OVERHEAD); std::int32_t validateCreateParam(const JpegDecCreateParam* param) { - if (!param) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + if (!param) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; if (param->this_size != sizeof(JpegDecCreateParam)) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; if (param->attribute != ATTRIBUTE_CSC_ORDINARY && param->attribute != ATTRIBUTE_CSC_FULL) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + if (param->max_image_width == 0 || param->max_image_width > 65535) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; return 0; } +std::uint32_t workMemorySize(const JpegDecCreateParam& param) { + const std::uint32_t alignedWidth = (param.max_image_width + 31) & ~std::uint32_t{31}; + return MEMORY_OVERHEAD + (alignedWidth + 16) * (param.attribute == ATTRIBUTE_CSC_ORDINARY ? 54 : 166); +} + std::uint32_t requireDownScale(const char* function, std::uint16_t decodeMode, std::uint16_t downScale) { if (decodeMode != DECODE_MODE_NORMAL) throw std::runtime_error(std::string(function) + ": decode mode " + std::to_string(decodeMode) + " is not implemented"); if (downScale != DOWN_SCALE_NONE && downScale != DOWN_SCALE_HALF && downScale != DOWN_SCALE_QUARTER && downScale != DOWN_SCALE_EIGHTH) @@ -92,25 +99,27 @@ std::span toBytes(const void* data, std::uint32_t size) { extern "C" { -int32_t APS5_VABI sceJpegDecCreate(const JpegDecCreateParam* param, void* memory_address, uint32_t memory_size, void** handle) { +int32_t APS5_VABI sceJpegDecCreate(const JpegDecCreateParam* param, void* memoryAddress, uint32_t memorySize, void** handle) { const std::int32_t result = validateCreateParam(param); if (result != 0) return result; - if (!memory_address || !handle) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; - if (memory_size < MEMORY_SIZE) return SCE_JPEG_DEC_ERROR_INVALID_WORK_MEMORY; - const auto address = reinterpret_cast(memory_address); + if (!memoryAddress) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + if (memorySize < workMemorySize(*param)) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; + if (!handle) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + const auto address = reinterpret_cast(memoryAddress); const std::uintptr_t aligned = (address + HANDLE_ALIGNMENT - 1) & ~(HANDLE_ALIGNMENT - 1); auto* context = new (reinterpret_cast(aligned)) Context{}; context->self = context; context->attribute = param->attribute; + context->maxImageWidth = (param->max_image_width + 31) & ~std::uint32_t{31}; *handle = context; return 0; } -int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param, JpegDecImageInfo* image_info) { +int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param, JpegDecImageInfo* imageInfo) { const Context* context = toContext(handle); if (!context) return SCE_JPEG_DEC_ERROR_INVALID_HANDLE; (void)context; - if (!param) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + if (!param) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; if (!param->jpeg_mem_addr || !param->image_mem_addr || (reinterpret_cast(param->image_mem_addr) & 3u) != 0) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; if (param->jpeg_mem_size == 0 || param->image_mem_size == 0) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; if (param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; @@ -153,7 +162,7 @@ int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param } } - if (image_info) fillImageInfo(*header, downScale, image_info); + if (imageInfo) fillImageInfo(*header, downScale, imageInfo); if (outputWidth > MAX_PACKED_DIMENSION || outputHeight > MAX_PACKED_DIMENSION) return 0; return static_cast(outputWidth << 16 | outputHeight); } @@ -165,21 +174,21 @@ int32_t APS5_VABI sceJpegDecDelete(void* handle) { return 0; } -int32_t APS5_VABI sceJpegDecParseHeader(const JpegDecParseParam* param, JpegDecImageInfo* image_info) { - if (!param) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; - if (!param->jpeg_mem_addr || !image_info) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; +int32_t APS5_VABI sceJpegDecParseHeader(const JpegDecParseParam* param, JpegDecImageInfo* imageInfo) { + if (!param) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + if (!param->jpeg_mem_addr || !imageInfo) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; if (param->jpeg_mem_size == 0) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; const std::uint32_t downScale = requireDownScale(__func__, param->decode_mode, param->down_scale); const std::optional header = Decoder::Jpeg::ParseHeader(toBytes(param->jpeg_mem_addr, param->jpeg_mem_size)); if (!header) return SCE_JPEG_DEC_ERROR_INVALID_DATA; - fillImageInfo(*header, downScale, image_info); + fillImageInfo(*header, downScale, imageInfo); return 0; } int32_t APS5_VABI sceJpegDecQueryMemorySize(const JpegDecCreateParam* param) { const std::int32_t result = validateCreateParam(param); if (result != 0) return result; - return static_cast(MEMORY_SIZE); + return static_cast(workMemorySize(*param)); } int APS5_VABI sceJpegDecDecodeWithInputControl() { diff --git a/core/libs/tests/GuestJpegDec.cpp b/core/libs/tests/GuestJpegDec.cpp index 018dba662d..93c5242f8c 100644 --- a/core/libs/tests/GuestJpegDec.cpp +++ b/core/libs/tests/GuestJpegDec.cpp @@ -68,17 +68,16 @@ static JpegDecDecodeParam DecodeParam(const std::vector& jpeg, std } int main() { - constexpr std::int32_t invalidAddr = static_cast(0x80690001); - constexpr std::int32_t invalidSize = static_cast(0x80690002); - constexpr std::int32_t invalidParam = static_cast(0x80690003); - constexpr std::int32_t invalidHandle = static_cast(0x80690004); - constexpr std::int32_t invalidWorkMemory = static_cast(0x80690005); - constexpr std::int32_t invalidData = static_cast(0x80690010); + constexpr std::int32_t invalidAddr = static_cast(0x80650001); + constexpr std::int32_t invalidSize = static_cast(0x80650002); + constexpr std::int32_t invalidParam = static_cast(0x80650003); + constexpr std::int32_t invalidHandle = static_cast(0x80650004); + constexpr std::int32_t invalidData = static_cast(0x80650010); const std::vector jpeg = TestJpeg(7, 5, 3); JpegDecImageInfo info{}; const JpegDecParseParam parseParam{jpeg.data(), static_cast(jpeg.size()), 0, 1}; - Require(sceJpegDecParseHeader(nullptr, &info) == invalidParam); + Require(sceJpegDecParseHeader(nullptr, &info) == invalidAddr); Require(sceJpegDecParseHeader(&parseParam, nullptr) == invalidAddr); const JpegDecParseParam emptyParam{nullptr, 0, 0, 1}; Require(sceJpegDecParseHeader(&emptyParam, &info) == invalidAddr); @@ -106,10 +105,17 @@ int main() { const JpegDecCreateParam param{sizeof(JpegDecCreateParam), info.suitable_csc_attribute, info.image_width}; const std::int32_t memorySize = sceJpegDecQueryMemorySize(¶m); - Require(memorySize > 0); + Require(memorySize == 32128); const JpegDecCreateParam fullParam{sizeof(JpegDecCreateParam), 2, info.image_width}; - Require(sceJpegDecQueryMemorySize(&fullParam) == memorySize); - Require(sceJpegDecQueryMemorySize(nullptr) == invalidParam); + const std::int32_t fullMemorySize = sceJpegDecQueryMemorySize(&fullParam); + Require(fullMemorySize == 37504); + for (const std::uint32_t width : {0u, 65536u, UINT32_MAX}) { + const JpegDecCreateParam badWidth{sizeof(JpegDecCreateParam), 1, width}; + Require(sceJpegDecQueryMemorySize(&badWidth) == invalidParam); + } + const JpegDecCreateParam largestWidth{sizeof(JpegDecCreateParam), 1, 65535}; + Require(sceJpegDecQueryMemorySize(&largestWidth) == 3569344); + Require(sceJpegDecQueryMemorySize(nullptr) == invalidAddr); const JpegDecCreateParam badSize{sizeof(JpegDecCreateParam) - 4, 1, 7}; Require(sceJpegDecQueryMemorySize(&badSize) == invalidSize); for (const std::uint32_t attribute : {0u, 3u}) { @@ -117,12 +123,13 @@ int main() { Require(sceJpegDecQueryMemorySize(&badAttribute) == invalidParam); } - std::vector memory(static_cast(memorySize) + 8); + std::vector memory(static_cast(fullMemorySize) + 32); void* handle = nullptr; - Require(sceJpegDecCreate(¶m, memory.data(), 0, &handle) == invalidWorkMemory); + Require(sceJpegDecCreate(¶m, memory.data(), 0, &handle) == invalidSize); Require(sceJpegDecCreate(¶m, memory.data(), static_cast(memorySize), nullptr) == invalidAddr); - Require(sceJpegDecCreate(&fullParam, memory.data(), static_cast(memorySize), &handle) == 0); - Require(handle != nullptr); + Require(sceJpegDecCreate(&fullParam, memory.data(), static_cast(fullMemorySize), &handle) == 0); + Require(handle != nullptr && reinterpret_cast(handle) % 32 == 0); + Require(sceJpegDecCreate(¶m, memory.data(), 0, nullptr) == invalidSize); Require(sceJpegDecDelete(nullptr) == invalidHandle); Require(sceJpegDecDelete(handle) == 0); @@ -134,7 +141,7 @@ int main() { std::vector image(7 * 5 * 4, 0xCC); JpegDecDecodeParam decodeParam = DecodeParam(jpeg, image, 7 * 4); Require(sceJpegDecDecode(nullptr, &decodeParam, &info) == invalidHandle); - Require(sceJpegDecDecode(handle, nullptr, &info) == invalidParam); + Require(sceJpegDecDecode(handle, nullptr, &info) == invalidAddr); JpegDecDecodeParam badFormat = decodeParam; badFormat.pixel_format = 2; Require(sceJpegDecDecode(handle, &badFormat, &info) == invalidParam); diff --git a/docs/dev/debt/libSceJpegDec.md b/docs/dev/debt/libSceJpegDec.md index e5b7252fa0..92ff01d77a 100644 --- a/docs/dev/debt/libSceJpegDec.md +++ b/docs/dev/debt/libSceJpegDec.md @@ -3,7 +3,7 @@ ### Unknown function info - [sceJpegDecDecodeWithInputControl](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceJpegDec.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. The error codes are libScePngDec's: no reference lists the JpegDec ones, and SharpProspero raises 0x80650020 on its JpegDec path where it raises libScePngDec's 0x80690020 on its PngDec path. Image info always reports color space 3, suitable CSC attribute 1 and zero component ids and sampling factors; the console's values, including which images it reports attribute 2 for, are unknown. The coefficient memory size is 0 because the host decoder needs no coefficient buffer; the size the console reports for progressive images is unknown. `max_image_width` is not checked, and the packed `width << 16 | height` decode result is libScePngDec's +- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Image info always reports color space 3, suitable CSC attribute 1 and zero component ids and sampling factors; the console's values, including which images it reports attribute 2 for, are unknown. The coefficient memory size is 0 because the host decoder needs no coefficient buffer; the size the console reports for progressive images is unknown. `max_image_width` is not checked, and the packed `width << 16 | height` decode result is confirmed by the native export ### Functional From a058d8d8faadb4e4cd2c6307afa7fcbc3dc8090a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:45:58 +0300 Subject: [PATCH 395/523] fix(jpegdec): enforce native decode parameter limits --- core/libs/prx/libSceJpegDec/Export.cpp | 39 +++++++++++++++++--------- core/libs/tests/GuestJpegDec.cpp | 20 ++++++++++--- docs/dev/debt/libSceJpegDec.md | 4 +-- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libSceJpegDec/Export.cpp b/core/libs/prx/libSceJpegDec/Export.cpp index 5232a524e1..43883fc455 100644 --- a/core/libs/prx/libSceJpegDec/Export.cpp +++ b/core/libs/prx/libSceJpegDec/Export.cpp @@ -22,7 +22,7 @@ constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_ADDR = static_cast(0x80650002); constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_PARAM = static_cast(0x80650003); constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_HANDLE = static_cast(0x80650004); -constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_WORK_MEMORY = static_cast(0x80650005); +constexpr std::int32_t SCE_JPEG_DEC_ERROR_IMAGE_TOO_WIDE = static_cast(0x80650006); constexpr std::int32_t SCE_JPEG_DEC_ERROR_INVALID_DATA = static_cast(0x80650010); constexpr std::int32_t SCE_JPEG_DEC_ERROR_DECODE_ERROR = static_cast(0x80650012); @@ -66,11 +66,12 @@ std::uint32_t workMemorySize(const JpegDecCreateParam& param) { return MEMORY_OVERHEAD + (alignedWidth + 16) * (param.attribute == ATTRIBUTE_CSC_ORDINARY ? 54 : 166); } -std::uint32_t requireDownScale(const char* function, std::uint16_t decodeMode, std::uint16_t downScale) { +bool validDecodeOptions(std::uint16_t decodeMode, std::uint16_t downScale) { + return decodeMode <= 1 && (downScale == DOWN_SCALE_NONE || downScale == DOWN_SCALE_HALF || downScale == DOWN_SCALE_QUARTER || downScale == DOWN_SCALE_EIGHTH); +} + +void requireDecodeMode(const char* function, std::uint16_t decodeMode) { if (decodeMode != DECODE_MODE_NORMAL) throw std::runtime_error(std::string(function) + ": decode mode " + std::to_string(decodeMode) + " is not implemented"); - if (downScale != DOWN_SCALE_NONE && downScale != DOWN_SCALE_HALF && downScale != DOWN_SCALE_QUARTER && downScale != DOWN_SCALE_EIGHTH) - throw std::runtime_error(std::string(function) + ": down scale " + std::to_string(downScale) + " is not implemented"); - return downScale; } Context* toContext(void* handle) { @@ -118,13 +119,16 @@ int32_t APS5_VABI sceJpegDecCreate(const JpegDecCreateParam* param, void* memory int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param, JpegDecImageInfo* imageInfo) { const Context* context = toContext(handle); if (!context) return SCE_JPEG_DEC_ERROR_INVALID_HANDLE; - (void)context; if (!param) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; - if (!param->jpeg_mem_addr || !param->image_mem_addr || (reinterpret_cast(param->image_mem_addr) & 3u) != 0) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + if (!param->jpeg_mem_addr || !param->image_mem_addr) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + if ((reinterpret_cast(param->image_mem_addr) & 3) != 0 && (param->pixel_format == 0 || param->pixel_format == 1 || param->pixel_format == 9 || param->pixel_format == 10)) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; if (param->jpeg_mem_size == 0 || param->image_mem_size == 0) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; - if (param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; - if (param->alpha_value > 0xFF) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; - const std::uint32_t downScale = requireDownScale(__func__, param->decode_mode, param->down_scale); + if (!validDecodeOptions(param->decode_mode, param->down_scale)) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + if (param->pixel_format != 0 && param->pixel_format != 1 && param->pixel_format != 9 && param->pixel_format != 10 && param->pixel_format != 11) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + if (param->alpha_value > 255) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + requireDecodeMode(__func__, param->decode_mode); + if (param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) throw std::runtime_error("sceJpegDecDecode: pixel format is not implemented"); + const std::uint32_t downScale = param->down_scale; const std::span jpeg = toBytes(param->jpeg_mem_addr, param->jpeg_mem_size); const std::optional header = Decoder::Jpeg::ParseHeader(jpeg); @@ -134,8 +138,12 @@ int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param const std::uint64_t rowSize = static_cast(outputWidth) * BYTES_PER_PIXEL; const std::uint64_t pitch = param->image_pitch == 0 ? rowSize : param->image_pitch; - if (pitch < rowSize) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; - if ((outputHeight - 1) * pitch + rowSize > param->image_mem_size) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; + if (param->image_pitch != 0) { + if (pitch < rowSize || pitch % 4 != 0 || pitch * outputHeight >= 0x80000000 || pitch * outputHeight > param->image_mem_size) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + } else if (pitch * outputHeight > param->image_mem_size) { + return SCE_JPEG_DEC_ERROR_INVALID_SIZE; + } + if (((outputWidth + 31) & ~std::uint32_t{31}) > context->maxImageWidth) return SCE_JPEG_DEC_ERROR_IMAGE_TOO_WIDE; const std::optional image = downScale == DOWN_SCALE_NONE ? Decoder::Jpeg::Decode(jpeg) : Decoder::Jpeg::DecodeScaled(jpeg, downScale); if (!image || image->width != outputWidth || image->height != outputHeight) return SCE_JPEG_DEC_ERROR_DECODE_ERROR; @@ -176,9 +184,12 @@ int32_t APS5_VABI sceJpegDecDelete(void* handle) { int32_t APS5_VABI sceJpegDecParseHeader(const JpegDecParseParam* param, JpegDecImageInfo* imageInfo) { if (!param) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; - if (!param->jpeg_mem_addr || !imageInfo) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + if (!param->jpeg_mem_addr) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; if (param->jpeg_mem_size == 0) return SCE_JPEG_DEC_ERROR_INVALID_SIZE; - const std::uint32_t downScale = requireDownScale(__func__, param->decode_mode, param->down_scale); + if (!validDecodeOptions(param->decode_mode, param->down_scale)) return SCE_JPEG_DEC_ERROR_INVALID_PARAM; + if (!imageInfo) return SCE_JPEG_DEC_ERROR_INVALID_ADDR; + requireDecodeMode(__func__, param->decode_mode); + const std::uint32_t downScale = param->down_scale; const std::optional header = Decoder::Jpeg::ParseHeader(toBytes(param->jpeg_mem_addr, param->jpeg_mem_size)); if (!header) return SCE_JPEG_DEC_ERROR_INVALID_DATA; fillImageInfo(*header, downScale, imageInfo); diff --git a/core/libs/tests/GuestJpegDec.cpp b/core/libs/tests/GuestJpegDec.cpp index 93c5242f8c..1e41580fd3 100644 --- a/core/libs/tests/GuestJpegDec.cpp +++ b/core/libs/tests/GuestJpegDec.cpp @@ -83,7 +83,7 @@ int main() { Require(sceJpegDecParseHeader(&emptyParam, &info) == invalidAddr); for (const std::uint16_t downScale : {std::uint16_t{0}, std::uint16_t{3}, std::uint16_t{16}}) { const JpegDecParseParam unknownScaleParse{jpeg.data(), static_cast(jpeg.size()), 0, downScale}; - Require(ThrowsRuntimeError([&] { sceJpegDecParseHeader(&unknownScaleParse, &info); })); + Require(sceJpegDecParseHeader(&unknownScaleParse, &info) == invalidParam); } const JpegDecParseParam otherModeParse{jpeg.data(), static_cast(jpeg.size()), 1, 1}; Require(ThrowsRuntimeError([&] { sceJpegDecParseHeader(&otherModeParse, &info); })); @@ -147,7 +147,16 @@ int main() { Require(sceJpegDecDecode(handle, &badFormat, &info) == invalidParam); JpegDecDecodeParam unknownScale = decodeParam; unknownScale.down_scale = 3; - Require(ThrowsRuntimeError([&] { sceJpegDecDecode(handle, &unknownScale, &info); })); + Require(sceJpegDecDecode(handle, &unknownScale, &info) == invalidParam); + JpegDecDecodeParam invalidOptions = decodeParam; + invalidOptions.alpha_value = 256; + Require(sceJpegDecDecode(handle, &invalidOptions, &info) == invalidParam); + invalidOptions = decodeParam; + invalidOptions.decode_mode = 2; + Require(sceJpegDecDecode(handle, &invalidOptions, &info) == invalidParam); + invalidOptions = decodeParam; + invalidOptions.image_mem_addr = image.data() + 1; + Require(sceJpegDecDecode(handle, &invalidOptions, &info) == invalidAddr); std::vector invalidAlphaImage(7 * 5 * 4, 0xCC); JpegDecDecodeParam invalidAlpha = DecodeParam(jpeg, invalidAlphaImage, 7 * 4); invalidAlpha.alpha_value = 0x100; @@ -175,7 +184,7 @@ int main() { } constexpr std::uint32_t pitch = 7 * 4 + 8; - std::vector bgra(4 * pitch + 7 * 4, 0xEE); + std::vector bgra(5 * pitch, 0xEE); JpegDecDecodeParam bgraParam = DecodeParam(jpeg, bgra, pitch); bgraParam.pixel_format = 1; bgraParam.alpha_value = 0x77; @@ -192,6 +201,9 @@ int main() { } const std::vector large = TestJpeg(37, 21, 3); + std::vector wideOutput(37 * 21 * 4); + JpegDecDecodeParam wideParam = DecodeParam(large, wideOutput, 0); + Require(sceJpegDecDecode(handle, &wideParam, nullptr) == static_cast(0x80650006)); const std::vector largeGray = TestJpeg(19, 11, 1); for (const auto* source : {&large, &largeGray}) { const std::optional sourceHeader = Decoder::Jpeg::ParseHeader(*source); @@ -215,7 +227,7 @@ int main() { } const std::uint32_t paddedPitch = width * 4 + 4; - std::vector padded((height - 1) * paddedPitch + width * 4, 0xEE); + std::vector padded(height * paddedPitch, 0xEE); JpegDecDecodeParam paddedParam = DecodeParam(*source, padded, paddedPitch); paddedParam.down_scale = downScale; paddedParam.pixel_format = 1; diff --git a/docs/dev/debt/libSceJpegDec.md b/docs/dev/debt/libSceJpegDec.md index 92ff01d77a..fc660f7950 100644 --- a/docs/dev/debt/libSceJpegDec.md +++ b/docs/dev/debt/libSceJpegDec.md @@ -3,8 +3,8 @@ ### Unknown function info - [sceJpegDecDecodeWithInputControl](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceJpegDec.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Image info always reports color space 3, suitable CSC attribute 1 and zero component ids and sampling factors; the console's values, including which images it reports attribute 2 for, are unknown. The coefficient memory size is 0 because the host decoder needs no coefficient buffer; the size the console reports for progressive images is unknown. `max_image_width` is not checked, and the packed `width << 16 | height` decode result is confirmed by the native export +- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Image info always reports color space 3, suitable CSC attribute 1 and zero component ids and sampling factors; the console's values, including which images it reports attribute 2 for, are unknown. The coefficient memory size is 0 because the host decoder needs no coefficient buffer; the size the console reports for progressive images is unknown. the output-width limit, aligned output address, pitch, alpha and option validation are confirmed by native checks, and the packed `width << 16 | height` decode result is confirmed by the native export ### Functional -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - decode modes other than 0 and down scales other than 1, 2, 4 and 8 throw, as does a down scale of 2, 4 or 8 on a CMYK or YCCK image, which libjpeg-turbo cannot decode to RGB. The down scale factors are the ones [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L57-L58) documents; the output size is the source size divided by the factor and rounded up, and the pixels come from libjpeg-turbo's scaled inverse DCT. The console's rounding, pixel values and the packed decode result for a down-scaled image (the output size here) are unverified. In `sceJpegDecDecode`, rejecting an output `image_mem_addr` not aligned to 4 bytes and rejecting an `alpha_value` above 0xFF follow libScePngDec validation and are not verified from a libSceJpegDec binary disassembly +- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - decode mode 1 and pixel formats 9, 10 and 11 throw; invalid modes and down scales return the native invalid-parameter error, as does a down scale of 2, 4 or 8 on a CMYK or YCCK image, which libjpeg-turbo cannot decode to RGB. The down scale factors are the ones [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L57-L58) documents; the output size is the source size divided by the factor and rounded up, and the pixels come from libjpeg-turbo's scaled inverse DCT. The native ceiling division and packed scaled output dimensions are confirmed; exact decoded pixel values remain unverified From c3eba45600e097c154cf60ff51426a9f12388d24 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:47:59 +0300 Subject: [PATCH 396/523] fix(jpegdec): recover image metadata and coefficient requirements --- core/Decoder/Jpeg/include/Decoder/Jpeg.hpp | 4 +++ core/Decoder/Jpeg/src/Jpeg.cpp | 28 ++++++++++++++++++- core/libs/prx/libSceJpegDec/Export.cpp | 32 ++++++++++++++++++++-- core/libs/tests/GuestJpegDec.cpp | 27 ++++++++++++++++++ docs/dev/debt/libSceJpegDec.md | 2 +- 5 files changed, 88 insertions(+), 5 deletions(-) diff --git a/core/Decoder/Jpeg/include/Decoder/Jpeg.hpp b/core/Decoder/Jpeg/include/Decoder/Jpeg.hpp index ed59467ac3..62d09b0698 100644 --- a/core/Decoder/Jpeg/include/Decoder/Jpeg.hpp +++ b/core/Decoder/Jpeg/include/Decoder/Jpeg.hpp @@ -19,6 +19,10 @@ struct Header { std::uint32_t width; std::uint32_t height; std::uint32_t channels; + std::uint8_t componentIds[4]{}; + std::uint8_t samplingFactors[4]{}; + bool progressive = false; + bool app0BeforeFrame = false; }; enum class Sampling { diff --git a/core/Decoder/Jpeg/src/Jpeg.cpp b/core/Decoder/Jpeg/src/Jpeg.cpp index e8e6aa19c3..96ace625a4 100644 --- a/core/Decoder/Jpeg/src/Jpeg.cpp +++ b/core/Decoder/Jpeg/src/Jpeg.cpp @@ -126,7 +126,33 @@ std::optional
ParseHeader(std::span jpeg) { int channels = 0; if (!stbi_info_from_memory(jpeg.data(), static_cast(jpeg.size()), &width, &height, &channels)) return std::nullopt; if (width <= 0 || height <= 0 || channels <= 0) return std::nullopt; - return Header{static_cast(width), static_cast(height), static_cast(channels)}; + Header header{static_cast(width), static_cast(height), static_cast(channels)}; + std::size_t offset = 2; + while (offset < jpeg.size()) { + if (jpeg[offset++] != 0xFF) return std::nullopt; + while (offset < jpeg.size() && jpeg[offset] == 0xFF) ++offset; + if (offset == jpeg.size()) return std::nullopt; + const std::uint8_t marker = jpeg[offset++]; + if (marker == 0xD9 || marker == 0xDA) return std::nullopt; + if (marker == 0x01 || (marker >= 0xD0 && marker <= 0xD8)) continue; + if (jpeg.size() - offset < 2) return std::nullopt; + const std::size_t length = (static_cast(jpeg[offset]) << 8) | jpeg[offset + 1]; + if (length < 2 || length > jpeg.size() - offset) return std::nullopt; + if (marker == 0xE0) header.app0BeforeFrame = true; + if (marker == 0xC0 || marker == 0xC1 || marker == 0xC2) { + if (length < 8) return std::nullopt; + header.channels = jpeg[offset + 7]; + if (header.channels == 0 || header.channels > 4 || length != 8 + 3 * header.channels) return std::nullopt; + header.progressive = marker == 0xC2; + for (std::uint32_t i = 0; i < header.channels; ++i) { + header.componentIds[i] = jpeg[offset + 8 + 3 * i]; + header.samplingFactors[i] = jpeg[offset + 9 + 3 * i]; + } + return header; + } + offset += length; + } + return std::nullopt; } std::optional Decode(std::span jpeg) { diff --git a/core/libs/prx/libSceJpegDec/Export.cpp b/core/libs/prx/libSceJpegDec/Export.cpp index 43883fc455..efec1932dc 100644 --- a/core/libs/prx/libSceJpegDec/Export.cpp +++ b/core/libs/prx/libSceJpegDec/Export.cpp @@ -40,7 +40,9 @@ constexpr std::uint16_t DOWN_SCALE_EIGHTH = 8; constexpr std::uint16_t PIXEL_FORMAT_R8G8B8A8 = 0; constexpr std::uint16_t PIXEL_FORMAT_B8G8R8A8 = 1; -constexpr std::uint16_t COLOR_SPACE_RGB = 3; +constexpr std::uint16_t COLOR_SPACE_UNKNOWN = 0; +constexpr std::uint16_t COLOR_SPACE_YCBCR = 1; +constexpr std::uint16_t COLOR_SPACE_GRAY = 2; constexpr std::uint32_t BYTES_PER_PIXEL = 4; constexpr std::uint32_t MAX_PACKED_DIMENSION = 32767; @@ -85,9 +87,29 @@ void fillImageInfo(const Decoder::Jpeg::Header& header, std::uint32_t downScale, *info = {}; info->image_width = header.width; info->image_height = header.height; - info->color_space = COLOR_SPACE_RGB; + info->color_space = COLOR_SPACE_UNKNOWN; + if (header.channels == 1) info->color_space = COLOR_SPACE_GRAY; + if (header.channels == 3 && (header.app0BeforeFrame || (header.componentIds[0] == 1 && header.componentIds[1] == 2 && header.componentIds[2] == 3))) info->color_space = COLOR_SPACE_YCBCR; info->component_number = static_cast(header.channels); - info->suitable_csc_attribute = ATTRIBUTE_CSC_ORDINARY; + std::uint32_t maxHorizontal = 1; + std::uint32_t maxVertical = 1; + std::uint32_t blocksPerMcu = 0; + for (std::uint32_t i = 0; i < header.channels; ++i) { + info->component_id[i] = header.componentIds[i]; + info->sampling_factor[i] = header.samplingFactors[i]; + const std::uint32_t horizontal = header.channels == 1 ? 1 : header.samplingFactors[i] >> 4; + const std::uint32_t vertical = header.channels == 1 ? 1 : header.samplingFactors[i] & 15; + if (horizontal > maxHorizontal) maxHorizontal = horizontal; + if (vertical > maxVertical) maxVertical = vertical; + blocksPerMcu += horizontal * vertical; + } + if (info->color_space == COLOR_SPACE_GRAY) info->suitable_csc_attribute = ATTRIBUTE_CSC_ORDINARY; + if (info->color_space == COLOR_SPACE_YCBCR) info->suitable_csc_attribute = (12 / maxHorizontal) * blocksPerMcu < 37 ? ATTRIBUTE_CSC_ORDINARY : ATTRIBUTE_CSC_FULL; + if (header.progressive) { + const std::uint32_t mcuColumns = (header.width + 8 * maxHorizontal - 1) / (8 * maxHorizontal); + const std::uint32_t mcuRows = (header.height + 8 * maxVertical - 1) / (8 * maxVertical); + info->coefficient_mem_size = mcuColumns * mcuRows * blocksPerMcu * 128 + 64; + } info->output_image_width = Decoder::Jpeg::ScaledDimension(header.width, downScale); info->output_image_height = Decoder::Jpeg::ScaledDimension(header.height, downScale); } @@ -133,6 +155,10 @@ int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param const std::span jpeg = toBytes(param->jpeg_mem_addr, param->jpeg_mem_size); const std::optional header = Decoder::Jpeg::ParseHeader(jpeg); if (!header) return SCE_JPEG_DEC_ERROR_INVALID_DATA; + JpegDecImageInfo parsedInfo{}; + fillImageInfo(*header, downScale, &parsedInfo); + if (parsedInfo.coefficient_mem_size != 0 && (!param->coefficient_mem_addr || param->coefficient_mem_size < parsedInfo.coefficient_mem_size)) return static_cast(0x80650005); + if (parsedInfo.color_space == COLOR_SPACE_UNKNOWN) return static_cast(0x80650011); const std::uint32_t outputWidth = Decoder::Jpeg::ScaledDimension(header->width, downScale); const std::uint32_t outputHeight = Decoder::Jpeg::ScaledDimension(header->height, downScale); diff --git a/core/libs/tests/GuestJpegDec.cpp b/core/libs/tests/GuestJpegDec.cpp index 1e41580fd3..ff526ac2b8 100644 --- a/core/libs/tests/GuestJpegDec.cpp +++ b/core/libs/tests/GuestJpegDec.cpp @@ -98,10 +98,31 @@ int main() { const JpegDecParseParam grayParse{grayJpeg.data(), static_cast(grayJpeg.size()), 0, 1}; Require(sceJpegDecParseHeader(&grayParse, &info) == 0); Require(info.image_width == 3 && info.image_height == 2 && info.component_number == 1); + Require(info.color_space == 2 && info.component_id[0] == 1 && info.sampling_factor[0] == 0x11); + Require(info.component_id[1] == 0 && info.sampling_factor[1] == 0); Require(sceJpegDecParseHeader(&parseParam, &info) == 0); Require(info.image_width == 7 && info.image_height == 5 && info.component_number == 3); Require(info.output_image_width == 7 && info.output_image_height == 5); Require(info.coefficient_mem_size == 0 && info.suitable_csc_attribute == 1); + Require(info.color_space == 1); + for (std::uint32_t i = 0; i < 3; ++i) Require(info.component_id[i] == i + 1 && info.sampling_factor[i] == 0x11); + std::vector progressiveHeader = jpeg; + bool changedFrame = false; + for (std::size_t offset = 2; offset + 3 < progressiveHeader.size();) { + const std::uint8_t marker = progressiveHeader[offset + 1]; + const std::size_t length = (static_cast(progressiveHeader[offset + 2]) << 8) | progressiveHeader[offset + 3]; + if (marker == 0xC0) { + progressiveHeader[offset + 1] = 0xC2; + changedFrame = true; + break; + } + offset += 2 + length; + } + Require(changedFrame); + const JpegDecParseParam progressiveParse{progressiveHeader.data(), static_cast(progressiveHeader.size()), 0, 1}; + Require(sceJpegDecParseHeader(&progressiveParse, &info) == 0); + Require(info.coefficient_mem_size == 448); + Require(sceJpegDecParseHeader(&parseParam, &info) == 0); const JpegDecCreateParam param{sizeof(JpegDecCreateParam), info.suitable_csc_attribute, info.image_width}; const std::int32_t memorySize = sceJpegDecQueryMemorySize(¶m); @@ -140,6 +161,12 @@ int main() { std::vector image(7 * 5 * 4, 0xCC); JpegDecDecodeParam decodeParam = DecodeParam(jpeg, image, 7 * 4); + JpegDecDecodeParam progressiveDecode = DecodeParam(progressiveHeader, image, 7 * 4); + Require(sceJpegDecDecode(handle, &progressiveDecode, &info) == static_cast(0x80650005)); + std::array insufficientCoefficients{}; + progressiveDecode.coefficient_mem_addr = insufficientCoefficients.data(); + progressiveDecode.coefficient_mem_size = static_cast(insufficientCoefficients.size()); + Require(sceJpegDecDecode(handle, &progressiveDecode, &info) == static_cast(0x80650005)); Require(sceJpegDecDecode(nullptr, &decodeParam, &info) == invalidHandle); Require(sceJpegDecDecode(handle, nullptr, &info) == invalidAddr); JpegDecDecodeParam badFormat = decodeParam; diff --git a/docs/dev/debt/libSceJpegDec.md b/docs/dev/debt/libSceJpegDec.md index fc660f7950..dad95387c4 100644 --- a/docs/dev/debt/libSceJpegDec.md +++ b/docs/dev/debt/libSceJpegDec.md @@ -3,7 +3,7 @@ ### Unknown function info - [sceJpegDecDecodeWithInputControl](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceJpegDec.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Image info always reports color space 3, suitable CSC attribute 1 and zero component ids and sampling factors; the console's values, including which images it reports attribute 2 for, are unknown. The coefficient memory size is 0 because the host decoder needs no coefficient buffer; the size the console reports for progressive images is unknown. the output-width limit, aligned output address, pitch, alpha and option validation are confirmed by native checks, and the packed `width << 16 | height` decode result is confirmed by the native export +- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Native field copies confirm the image-info layout. Color space, component identifiers, sampling factors, CSC selection, progressive coefficient-buffer size and scaled dimensions are recovered from the native header helpers. The host now checks the aligned output-width limit and progressive coefficient-buffer capacity. The native decoder also limits internal row-buffer consumption for unusual sampling factors; that additional capacity check is not yet implemented. ### Functional From d0417623a77785fddc01528d98e149e1e844a976 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:49:23 +0300 Subject: [PATCH 397/523] fix(jpegdec): enforce component row buffer capacity --- core/libs/prx/libSceJpegDec/Export.cpp | 24 +++++++++++++++++++++++- core/libs/tests/GuestJpegDec.cpp | 13 +++++++++++++ docs/dev/debt/libSceJpegDec.md | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceJpegDec/Export.cpp b/core/libs/prx/libSceJpegDec/Export.cpp index efec1932dc..aeddcbeb6c 100644 --- a/core/libs/prx/libSceJpegDec/Export.cpp +++ b/core/libs/prx/libSceJpegDec/Export.cpp @@ -51,6 +51,7 @@ struct Context { Context* self; std::uint32_t attribute; std::uint32_t maxImageWidth; + std::uint32_t rowBufferCapacity; }; static_assert(sizeof(Context) + HANDLE_ALIGNMENT - 1 <= MEMORY_OVERHEAD); @@ -114,6 +115,26 @@ void fillImageInfo(const Decoder::Jpeg::Header& header, std::uint32_t downScale, info->output_image_height = Decoder::Jpeg::ScaledDimension(header.height, downScale); } +std::uint32_t rowBufferSize(const Decoder::Jpeg::Header& header, std::uint32_t downScale) { + std::uint32_t maxHorizontal = 1; + if (header.channels != 1) { + for (std::uint32_t i = 0; i < header.channels; ++i) { + const std::uint32_t horizontal = header.samplingFactors[i] >> 4; + if (horizontal > maxHorizontal) maxHorizontal = horizontal; + } + } + const std::uint32_t mcuColumns = (header.width + 8 * maxHorizontal - 1) / (8 * maxHorizontal); + const std::uint32_t blockSize = 8 / downScale; + std::uint32_t size = 0; + for (std::uint32_t i = 0; i < header.channels; ++i) { + const std::uint32_t horizontal = header.channels == 1 ? 1 : header.samplingFactors[i] >> 4; + const std::uint32_t vertical = header.channels == 1 ? 1 : header.samplingFactors[i] & 15; + const std::uint32_t stride = (mcuColumns * blockSize * horizontal + 7) & ~std::uint32_t{7}; + size += stride * vertical * blockSize; + } + return (size + 31) & ~std::uint32_t{31}; +} + std::span toBytes(const void* data, std::uint32_t size) { return {static_cast(data), size}; } @@ -134,6 +155,7 @@ int32_t APS5_VABI sceJpegDecCreate(const JpegDecCreateParam* param, void* memory context->self = context; context->attribute = param->attribute; context->maxImageWidth = (param->max_image_width + 31) & ~std::uint32_t{31}; + context->rowBufferCapacity = (context->maxImageWidth + 16) * (param->attribute == ATTRIBUTE_CSC_ORDINARY ? 24 : 80); *handle = context; return 0; } @@ -169,7 +191,7 @@ int32_t APS5_VABI sceJpegDecDecode(void* handle, const JpegDecDecodeParam* param } else if (pitch * outputHeight > param->image_mem_size) { return SCE_JPEG_DEC_ERROR_INVALID_SIZE; } - if (((outputWidth + 31) & ~std::uint32_t{31}) > context->maxImageWidth) return SCE_JPEG_DEC_ERROR_IMAGE_TOO_WIDE; + if (((outputWidth + 31) & ~std::uint32_t{31}) > context->maxImageWidth || rowBufferSize(*header, downScale) > context->rowBufferCapacity) return SCE_JPEG_DEC_ERROR_IMAGE_TOO_WIDE; const std::optional image = downScale == DOWN_SCALE_NONE ? Decoder::Jpeg::Decode(jpeg) : Decoder::Jpeg::DecodeScaled(jpeg, downScale); if (!image || image->width != outputWidth || image->height != outputHeight) return SCE_JPEG_DEC_ERROR_DECODE_ERROR; diff --git a/core/libs/tests/GuestJpegDec.cpp b/core/libs/tests/GuestJpegDec.cpp index ff526ac2b8..6a8273a54e 100644 --- a/core/libs/tests/GuestJpegDec.cpp +++ b/core/libs/tests/GuestJpegDec.cpp @@ -161,6 +161,19 @@ int main() { std::vector image(7 * 5 * 4, 0xCC); JpegDecDecodeParam decodeParam = DecodeParam(jpeg, image, 7 * 4); + std::vector tallSampling = TestJpeg(31, 5, 3); + for (std::size_t offset = 2; offset + 3 < tallSampling.size();) { + const std::uint8_t marker = tallSampling[offset + 1]; + const std::size_t length = (static_cast(tallSampling[offset + 2]) << 8) | tallSampling[offset + 3]; + if (marker == 0xC0) { + tallSampling[offset + 11] = 0x14; + break; + } + offset += 2 + length; + } + std::vector tallSamplingOutput(31 * 5 * 4); + JpegDecDecodeParam tallSamplingDecode = DecodeParam(tallSampling, tallSamplingOutput, 0); + Require(sceJpegDecDecode(handle, &tallSamplingDecode, &info) == static_cast(0x80650006)); JpegDecDecodeParam progressiveDecode = DecodeParam(progressiveHeader, image, 7 * 4); Require(sceJpegDecDecode(handle, &progressiveDecode, &info) == static_cast(0x80650005)); std::array insufficientCoefficients{}; diff --git a/docs/dev/debt/libSceJpegDec.md b/docs/dev/debt/libSceJpegDec.md index dad95387c4..b00f07b6d9 100644 --- a/docs/dev/debt/libSceJpegDec.md +++ b/docs/dev/debt/libSceJpegDec.md @@ -3,7 +3,7 @@ ### Unknown function info - [sceJpegDecDecodeWithInputControl](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceJpegDec.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Native field copies confirm the image-info layout. Color space, component identifiers, sampling factors, CSC selection, progressive coefficient-buffer size and scaled dimensions are recovered from the native header helpers. The host now checks the aligned output-width limit and progressive coefficient-buffer capacity. The native decoder also limits internal row-buffer consumption for unusual sampling factors; that additional capacity check is not yet implemented. +- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Native field copies confirm the image-info layout. Color space, component identifiers, sampling factors, CSC selection, progressive coefficient-buffer size and scaled dimensions are recovered from the native header helpers. The host now checks the aligned output-width limit and progressive coefficient-buffer capacity. The native per-component row-buffer calculation is also enforced against the capacity selected by the creation attribute and maximum width. ### Functional From bc730915a08ae8642502d8aebc0e9bcff2764469 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:50:29 +0300 Subject: [PATCH 398/523] docs(jpegdec): retain remaining decoder compatibility gaps --- docs/dev/debt/libSceJpegDec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libSceJpegDec.md b/docs/dev/debt/libSceJpegDec.md index b00f07b6d9..174718fdd4 100644 --- a/docs/dev/debt/libSceJpegDec.md +++ b/docs/dev/debt/libSceJpegDec.md @@ -3,8 +3,8 @@ ### Unknown function info - [sceJpegDecDecodeWithInputControl](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceJpegDec.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - structure layouts follow [OpenOrbis `jpeg.h`](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/0a1aaf9dd4a92695538bdeb09fb056d06dd11725/include/orbis/_types/jpeg.h#L13-L72) and [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L20-L117); attributes 1 and 2 are the values [SharpProspero's `JpegImage.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Graphics/JpegImage.cs#L64-L74) passes. Error codes, creation validation, 32-byte handle alignment and the attribute/width-dependent work-memory size are confirmed by the native exports. Native field copies confirm the image-info layout. Color space, component identifiers, sampling factors, CSC selection, progressive coefficient-buffer size and scaled dimensions are recovered from the native header helpers. The host now checks the aligned output-width limit and progressive coefficient-buffer capacity. The native per-component row-buffer calculation is also enforced against the capacity selected by the creation attribute and maximum width. ### Functional -- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp) (libSceJpegDec) - decode mode 1 and pixel formats 9, 10 and 11 throw; invalid modes and down scales return the native invalid-parameter error, as does a down scale of 2, 4 or 8 on a CMYK or YCCK image, which libjpeg-turbo cannot decode to RGB. The down scale factors are the ones [SharpProspero `JpegDec.cs`](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Image/JpegDec.cs#L57-L58) documents; the output size is the source size divided by the factor and rounded up, and the pixels come from libjpeg-turbo's scaled inverse DCT. The native ceiling division and packed scaled output dimensions are confirmed; exact decoded pixel values remain unverified +- [libSceJpegDec](../../../core/libs/prx/libSceJpegDec/Export.cpp): work-memory size, alignment, creation checks, image-info fields, progressive coefficient-buffer capacity, row-buffer capacity and scaled dimensions are recovered from the native module. The host still allocates its decoder storage separately rather than using the guest work and coefficient regions. Malformed-header diagnostics and output mutation on failure are not fully matched. +- [sceJpegDecDecode](../../../core/libs/prx/libSceJpegDec/Export.cpp): decode mode 1 and pixel formats 9, 10 and 11 remain unimplemented and throw. Exact decoded pixel values and color conversion are not verified against the native decoder. From 0a8ee1520d50cc31d036e166bb66d77010078e27 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:50:30 +0300 Subject: [PATCH 399/523] fix(pngenc): allow unfiltered rows with partial filter masks --- core/libs/prx/libScePngEnc/Export.cpp | 2 +- core/libs/tests/GuestPngEnc.cpp | 5 +++-- docs/dev/debt/libScePngEnc.md | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libScePngEnc/Export.cpp b/core/libs/prx/libScePngEnc/Export.cpp index 4ff0af04e3..a4bad699c4 100644 --- a/core/libs/prx/libScePngEnc/Export.cpp +++ b/core/libs/prx/libScePngEnc/Export.cpp @@ -55,7 +55,7 @@ PngEncContext* context(void* handle) { std::uint8_t filterSet(std::uint16_t filterType) { if (filterType == FILTER_ALL) return Decoder::Png::FILTER_ALL; if (filterType == 0) return Decoder::Png::FILTER_NONE; - std::uint8_t filters = 0; + std::uint8_t filters = Decoder::Png::FILTER_NONE; if (filterType & FILTER_SUB) filters |= Decoder::Png::FILTER_SUB; if (filterType & FILTER_UP) filters |= Decoder::Png::FILTER_UP; if (filterType & FILTER_AVERAGE) filters |= Decoder::Png::FILTER_AVERAGE; diff --git a/core/libs/tests/GuestPngEnc.cpp b/core/libs/tests/GuestPngEnc.cpp index 7984c2fff5..3a84ca0f83 100644 --- a/core/libs/tests/GuestPngEnc.cpp +++ b/core/libs/tests/GuestPngEnc.cpp @@ -126,8 +126,9 @@ int main() { {0, FILTER_NONE}, {1, FILTER_SUB}, {2, FILTER_UP}, {4, FILTER_AVERAGE}, {8, FILTER_PAETH}, {3, FILTER_SUB | FILTER_UP}, {6, FILTER_UP | FILTER_AVERAGE}, {9, FILTER_SUB | FILTER_PAETH}, {14, FILTER_UP | FILTER_AVERAGE | FILTER_PAETH}, {15, FILTER_ALL}}; - for (const auto& [filterType, filters] : filterSets) Require(encodeWith(filterType) == Encode(rows, 16, 6, 4, {6, filters})); - Require(encodeWith(3) != encodeWith(1) && encodeWith(3) != encodeWith(2) && encodeWith(15) != encodeWith(14)); + for (const auto& [filterType, filters] : filterSets) Require(encodeWith(filterType) == Encode(rows, 16, 6, 4, {6, static_cast(filters | FILTER_NONE)})); + const std::vector unfiltered = Encode(rows, 16, 6, 4, {6, FILTER_NONE}); + Require(encodeWith(0) == unfiltered); encode.png_mem_size = 20; PngEncOutputInfo info{1, 1}; diff --git a/docs/dev/debt/libScePngEnc.md b/docs/dev/debt/libScePngEnc.md index 0ba7edc2bf..e5a107d55e 100644 --- a/docs/dev/debt/libScePngEnc.md +++ b/docs/dev/debt/libScePngEnc.md @@ -1,5 +1,5 @@ # libScePngEnc technical debt -### Unknown function info +### Functional -- [scePngEncEncode](../../../core/libs/prx/libScePngEnc/Export.cpp) (libScePngEnc) - whether rows may stay unfiltered when `filter_type` names a subset of the filters is unknown; as in shadPS4, they may only for 0 and for the all-filters mask +- [scePngEncEncode](../../../core/libs/prx/libScePngEnc/Export.cpp): native filter setup always includes the unfiltered candidate, including for partial masks. The host now permits it too; exact filter-selection ties, compressed bytes and partial-output behavior are not matched to the native encoder. Creation memory layout and encode validation still differ from the supplied module. From 13fa4033a4409f859da46efd95d555ab219634d9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:51:26 +0300 Subject: [PATCH 400/523] fix(pngenc): restore creation memory contract --- core/libs/prx/libScePngEnc/Export.cpp | 44 +++++++++++++++++++-------- core/libs/tests/GuestPngEnc.cpp | 16 ++++++---- docs/dev/debt/libScePngEnc.md | 2 +- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libScePngEnc/Export.cpp b/core/libs/prx/libScePngEnc/Export.cpp index a4bad699c4..cb8cddc9ce 100644 --- a/core/libs/prx/libScePngEnc/Export.cpp +++ b/core/libs/prx/libScePngEnc/Export.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -18,7 +20,7 @@ constexpr int PNG_ENC_ERROR_FATAL = static_cast(0x80690120); constexpr std::uint32_t MAX_IMAGE_WIDTH = 1000000; constexpr std::uint32_t MAX_IMAGE_HEIGHT = 1000000; -constexpr std::uint32_t MAX_FILTER_NUMBER = 5; +constexpr std::uint32_t MAX_FILTER_NUMBER = 4; constexpr std::uint16_t COLOR_SPACE_RGB = 3; constexpr std::uint16_t COLOR_SPACE_RGBA = 19; constexpr std::uint16_t PIXEL_FORMAT_R8G8B8A8 = 0; @@ -28,12 +30,15 @@ constexpr std::uint16_t FILTER_UP = 2; constexpr std::uint16_t FILTER_AVERAGE = 4; constexpr std::uint16_t FILTER_PAETH = 8; constexpr std::uint16_t FILTER_ALL = FILTER_SUB | FILTER_UP | FILTER_AVERAGE | FILTER_PAETH; -constexpr std::uint64_t CONTEXT_MAGIC = 0x434E45474E505341ull; +constexpr std::uintptr_t HANDLE_ALIGNMENT = 16; struct PngEncContext { - std::uint64_t magic; - std::uint32_t maxImageWidth; + PngEncContext* self; + std::uint32_t size; std::uint32_t reserved; + std::uint32_t maxImageWidth; + std::uint32_t maxFilterNumber; + std::uint32_t scratchSize; }; static_assert(sizeof(PngEncCreateParam) == 16); @@ -42,14 +47,19 @@ static_assert(sizeof(PngEncOutputInfo) == 8); int validateCreateParam(const PngEncCreateParam* param) { if (!param) return PNG_ENC_ERROR_INVALID_ADDR; - if (param->attribute != 0 || param->max_filter_number > MAX_FILTER_NUMBER) return PNG_ENC_ERROR_INVALID_PARAM; - if (param->max_image_width == 0 || param->max_image_width > MAX_IMAGE_WIDTH) return PNG_ENC_ERROR_INVALID_SIZE; + if (param->this_size != sizeof(PngEncCreateParam)) return PNG_ENC_ERROR_INVALID_SIZE; + if (param->attribute != 0 || param->max_image_width == 0 || param->max_image_width > MAX_IMAGE_WIDTH || param->max_filter_number > MAX_FILTER_NUMBER) return PNG_ENC_ERROR_INVALID_PARAM; return 0; } +std::uint32_t workMemorySize(const PngEncCreateParam& param) { + return 285808 + ((4 * param.max_image_width + 28) & ~std::uint32_t{31}) * (param.max_filter_number + 2); +} + PngEncContext* context(void* handle) { + if (reinterpret_cast(handle) % HANDLE_ALIGNMENT != 0) return nullptr; auto* ctx = static_cast(handle); - return ctx && ctx->magic == CONTEXT_MAGIC ? ctx : nullptr; + return ctx && ctx->self == ctx ? ctx : nullptr; } std::uint8_t filterSet(std::uint16_t filterType) { @@ -69,22 +79,30 @@ extern "C" { int APS5_VABI scePngEncQueryMemorySize(const PngEncCreateParam* param) { if (const int result = validateCreateParam(param); result != 0) return result; - return sizeof(PngEncContext); + return static_cast(workMemorySize(*param)); } int APS5_VABI scePngEncCreate(const PngEncCreateParam* param, void* memoryAddress, uint32_t memorySize, void** handle) { if (const int result = validateCreateParam(param); result != 0) return result; - if (!memoryAddress || !handle) return PNG_ENC_ERROR_INVALID_ADDR; - if (memorySize < sizeof(PngEncContext)) return PNG_ENC_ERROR_INVALID_SIZE; - *static_cast(memoryAddress) = {CONTEXT_MAGIC, param->max_image_width, 0}; - *handle = memoryAddress; + if (!memoryAddress) return PNG_ENC_ERROR_INVALID_ADDR; + const std::uint32_t requiredSize = workMemorySize(*param); + if (memorySize < requiredSize) return PNG_ENC_ERROR_INVALID_SIZE; + if (!handle) return PNG_ENC_ERROR_INVALID_ADDR; + const std::uintptr_t aligned = (reinterpret_cast(memoryAddress) + HANDLE_ALIGNMENT - 1) & ~(HANDLE_ALIGNMENT - 1); + auto* ctx = new (reinterpret_cast(aligned)) PngEncContext{}; + ctx->self = ctx; + ctx->size = sizeof(PngEncCreateParam); + ctx->maxImageWidth = (param->max_image_width + 7) & ~std::uint32_t{7}; + ctx->maxFilterNumber = param->max_filter_number; + ctx->scratchSize = requiredSize - 112; + *handle = ctx; return 0; } int APS5_VABI scePngEncDelete(void* handle) { PngEncContext* ctx = context(handle); if (!ctx) return PNG_ENC_ERROR_INVALID_HANDLE; - ctx->magic = 0; + ctx->self = nullptr; return 0; } diff --git a/core/libs/tests/GuestPngEnc.cpp b/core/libs/tests/GuestPngEnc.cpp index 3a84ca0f83..62287f1c73 100644 --- a/core/libs/tests/GuestPngEnc.cpp +++ b/core/libs/tests/GuestPngEnc.cpp @@ -26,28 +26,32 @@ int main() { constexpr std::uint32_t height = 3; constexpr std::uint32_t pitch = 24; - PngEncCreateParam create{sizeof(PngEncCreateParam), 0, 16, 5}; + PngEncCreateParam create{sizeof(PngEncCreateParam), 0, 16, 4}; const int memorySize = scePngEncQueryMemorySize(&create); - Require(memorySize > 0); + Require(memorySize == 286192); Require(scePngEncQueryMemorySize(nullptr) == invalidAddr); PngEncCreateParam bad = create; bad.attribute = 1; Require(scePngEncQueryMemorySize(&bad) == invalidParam); bad = create; - bad.max_filter_number = 6; + bad.max_filter_number = 5; Require(scePngEncQueryMemorySize(&bad) == invalidParam); bad = create; bad.max_image_width = 0; - Require(scePngEncQueryMemorySize(&bad) == invalidSize); + Require(scePngEncQueryMemorySize(&bad) == invalidParam); bad.max_image_width = 1000001; - Require(scePngEncQueryMemorySize(&bad) == invalidSize); + Require(scePngEncQueryMemorySize(&bad) == invalidParam); + bad = create; + bad.this_size = 0; + Require(scePngEncQueryMemorySize(&bad) == invalidSize); std::vector memory((memorySize + 7) / 8); void* handle = nullptr; Require(scePngEncCreate(&create, nullptr, memorySize, &handle) == invalidAddr); Require(scePngEncCreate(&create, memory.data(), memorySize - 1, &handle) == invalidSize); - Require(scePngEncCreate(&create, memory.data(), memorySize, &handle) == 0 && handle == memory.data()); + Require(scePngEncCreate(&create, memory.data(), memorySize, &handle) == 0 && reinterpret_cast(handle) % 16 == 0); + Require(scePngEncCreate(&create, memory.data(), 0, nullptr) == invalidSize); std::vector image(pitch * height, 0xEE); for (std::uint32_t y = 0; y < height; ++y) { for (std::uint32_t x = 0; x < width; ++x) { diff --git a/docs/dev/debt/libScePngEnc.md b/docs/dev/debt/libScePngEnc.md index e5a107d55e..a73a10572d 100644 --- a/docs/dev/debt/libScePngEnc.md +++ b/docs/dev/debt/libScePngEnc.md @@ -2,4 +2,4 @@ ### Functional -- [scePngEncEncode](../../../core/libs/prx/libScePngEnc/Export.cpp): native filter setup always includes the unfiltered candidate, including for partial masks. The host now permits it too; exact filter-selection ties, compressed bytes and partial-output behavior are not matched to the native encoder. Creation memory layout and encode validation still differ from the supplied module. +- [scePngEncEncode](../../../core/libs/prx/libScePngEnc/Export.cpp): native filter setup always includes the unfiltered candidate, including for partial masks. The host now permits it too; exact filter-selection ties, compressed bytes and partial-output behavior are not matched to the native encoder. Creation validation, work-memory size, 16-byte handle alignment and the context prefix now match the supplied module. Host scratch storage remains separately allocated, and encode validation still differs. From d616c4c3ce3d26669d5a4546f58e09ecc0e66c14 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:52:10 +0300 Subject: [PATCH 401/523] fix(pngenc): validate input buffers and filter capacity --- core/libs/prx/libScePngEnc/Export.cpp | 23 ++++++++++++----------- core/libs/tests/GuestPngEnc.cpp | 26 +++++++++++++++++++++----- docs/dev/debt/libScePngEnc.md | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libScePngEnc/Export.cpp b/core/libs/prx/libScePngEnc/Export.cpp index cb8cddc9ce..c6e3c9532a 100644 --- a/core/libs/prx/libScePngEnc/Export.cpp +++ b/core/libs/prx/libScePngEnc/Export.cpp @@ -109,19 +109,20 @@ int APS5_VABI scePngEncDelete(void* handle) { int APS5_VABI scePngEncEncode(void* handle, const PngEncEncodeParam* param, PngEncOutputInfo* outputInfo) { const PngEncContext* ctx = context(handle); if (!ctx) return PNG_ENC_ERROR_INVALID_HANDLE; - if (!param) return PNG_ENC_ERROR_INVALID_PARAM; - if (!param->image_mem_addr || !param->png_mem_addr) return PNG_ENC_ERROR_INVALID_ADDR; - if ((param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) - || (param->color_space != COLOR_SPACE_RGB && param->color_space != COLOR_SPACE_RGBA) || param->bit_depth != 8 - || param->clut_number != 0 || (param->filter_type & ~FILTER_ALL) != 0 || param->compression_level > 9) { - return PNG_ENC_ERROR_INVALID_PARAM; - } - + if (!param) return PNG_ENC_ERROR_INVALID_ADDR; + if (!param->image_mem_addr || reinterpret_cast(param->image_mem_addr) % 4 != 0 || !param->png_mem_addr) return PNG_ENC_ERROR_INVALID_ADDR; + if (param->image_mem_size == 0 || param->png_mem_size == 0) return PNG_ENC_ERROR_INVALID_SIZE; const std::uint32_t width = param->image_width; const std::uint32_t height = param->image_height; - if (width == 0 || height == 0 || width > ctx->maxImageWidth || height > MAX_IMAGE_HEIGHT || param->png_mem_size == 0 - || param->image_pitch < width * 4 || static_cast(param->image_pitch) * (height - 1) + width * 4 > param->image_mem_size) { - return PNG_ENC_ERROR_INVALID_SIZE; + const std::uint64_t inputSize = static_cast(param->image_pitch) * height; + if (width == 0 || width > ctx->maxImageWidth || height == 0 || height > MAX_IMAGE_HEIGHT + || param->image_pitch == 0 || param->image_pitch % 4 != 0 || param->image_pitch > 0x0FFFFFFF || param->image_pitch / 4 < width + || inputSize > 0x7FFFFFFF || inputSize > param->image_mem_size) return PNG_ENC_ERROR_INVALID_PARAM; + if ((param->pixel_format != PIXEL_FORMAT_R8G8B8A8 && param->pixel_format != PIXEL_FORMAT_B8G8R8A8) + || (param->color_space != COLOR_SPACE_RGB && param->color_space != COLOR_SPACE_RGBA) || param->bit_depth != 8 + || param->clut_number != 0 || (param->filter_type & ~FILTER_ALL) != 0 + || static_cast(std::popcount(param->filter_type)) > ctx->maxFilterNumber || param->compression_level > 9) { + return PNG_ENC_ERROR_INVALID_PARAM; } const std::uint32_t channels = param->color_space == COLOR_SPACE_RGBA ? 4 : 3; diff --git a/core/libs/tests/GuestPngEnc.cpp b/core/libs/tests/GuestPngEnc.cpp index 62287f1c73..d8227b3bab 100644 --- a/core/libs/tests/GuestPngEnc.cpp +++ b/core/libs/tests/GuestPngEnc.cpp @@ -140,7 +140,7 @@ int main() { encode.png_mem_size = static_cast(png.size()); PngEncEncodeParam invalid = encode; - Require(scePngEncEncode(handle, nullptr, nullptr) == invalidParam); + Require(scePngEncEncode(handle, nullptr, nullptr) == invalidAddr); invalid.image_mem_addr = nullptr; Require(scePngEncEncode(handle, &invalid, nullptr) == invalidAddr); for (auto change : {+[](PngEncEncodeParam& p) { p.pixel_format = 2; }, +[](PngEncEncodeParam& p) { p.color_space = 4; }, @@ -151,15 +151,31 @@ int main() { Require(scePngEncEncode(handle, &invalid, nullptr) == invalidParam); } for (auto change : {+[](PngEncEncodeParam& p) { p.image_width = 0; }, +[](PngEncEncodeParam& p) { p.image_width = 17; }, - +[](PngEncEncodeParam& p) { p.image_pitch = 19; }, +[](PngEncEncodeParam& p) { p.image_mem_size = 67; }, - +[](PngEncEncodeParam& p) { p.png_mem_size = 0; }}) { + +[](PngEncEncodeParam& p) { p.image_pitch = 19; }, +[](PngEncEncodeParam& p) { p.image_mem_size = 67; }}) { invalid = encode; change(invalid); - Require(scePngEncEncode(handle, &invalid, nullptr) == invalidSize); + Require(scePngEncEncode(handle, &invalid, nullptr) == invalidParam); } invalid = encode; invalid.image_mem_size = pitch * (height - 1) + width * 4; - Require(scePngEncEncode(handle, &invalid, nullptr) > 0); + Require(scePngEncEncode(handle, &invalid, nullptr) == invalidParam); + invalid = encode; + invalid.png_mem_size = 0; + Require(scePngEncEncode(handle, &invalid, nullptr) == invalidSize); + invalid = encode; + invalid.image_mem_addr = image.data() + 1; + Require(scePngEncEncode(handle, &invalid, nullptr) == invalidAddr); + PngEncCreateParam oneFilter = create; + oneFilter.max_filter_number = 1; + void* oneFilterHandle = nullptr; + std::vector oneFilterMemory((scePngEncQueryMemorySize(&oneFilter) + 7) / 8); + Require(scePngEncCreate(&oneFilter, oneFilterMemory.data(), static_cast(oneFilterMemory.size() * 8), &oneFilterHandle) == 0); + invalid = encode; + invalid.filter_type = 3; + Require(scePngEncEncode(oneFilterHandle, &invalid, nullptr) == invalidParam); + invalid.filter_type = 1; + Require(scePngEncEncode(oneFilterHandle, &invalid, nullptr) > 0); + Require(scePngEncDelete(oneFilterHandle) == 0); Require(scePngEncDelete(handle) == 0); Require(scePngEncDelete(handle) == invalidHandle); diff --git a/docs/dev/debt/libScePngEnc.md b/docs/dev/debt/libScePngEnc.md index a73a10572d..88f5089fbc 100644 --- a/docs/dev/debt/libScePngEnc.md +++ b/docs/dev/debt/libScePngEnc.md @@ -2,4 +2,4 @@ ### Functional -- [scePngEncEncode](../../../core/libs/prx/libScePngEnc/Export.cpp): native filter setup always includes the unfiltered candidate, including for partial masks. The host now permits it too; exact filter-selection ties, compressed bytes and partial-output behavior are not matched to the native encoder. Creation validation, work-memory size, 16-byte handle alignment and the context prefix now match the supplied module. Host scratch storage remains separately allocated, and encode validation still differs. +- [scePngEncEncode](../../../core/libs/prx/libScePngEnc/Export.cpp): native filter setup always includes the unfiltered candidate, including for partial masks. The host now permits it too; exact filter-selection ties, compressed bytes and partial-output behavior are not matched to the native encoder. Creation validation, work-memory size, 16-byte handle alignment and the context prefix now match the supplied module. Input-buffer alignment, pitch, dimensions, buffer capacity and selected-filter count now follow the native checks. Host scratch storage remains separately allocated. From 69a0a691d187d7dce0a7cf4366c1c358d519c36d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:53:39 +0300 Subject: [PATCH 402/523] fix(audio3d): restore attribute count output contract --- core/libs/prx/libSceAudio3d/Export.cpp | 15 +++++---------- core/libs/tests/GuestAudio3dPort.cpp | 9 +++++---- docs/dev/debt/libSceAudio3d.md | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceAudio3d/Export.cpp b/core/libs/prx/libSceAudio3d/Export.cpp index 28854734cc..4420537497 100644 --- a/core/libs/prx/libSceAudio3d/Export.cpp +++ b/core/libs/prx/libSceAudio3d/Export.cpp @@ -199,22 +199,17 @@ int APS5_VABI sceAudio3dPortGetQueueLevel(uint32_t port_id, uint32_t* queue_leve return 0; } -int APS5_VABI sceAudio3dPortGetAttributesSupported(uint32_t port_id, uint32_t* attributes, uint32_t* num_attributes) { +int APS5_VABI sceAudio3dPortGetAttributesSupported(uint32_t portId, uint32_t* attributes, uint32_t* numAttributes) { std::lock_guard lock(g_mutex); - if (num_attributes == nullptr) return AUDIO3D_ERROR_INVALID_PARAMETER; - if (!PortIsOpen(port_id)) return AUDIO3D_ERROR_INVALID_PORT; + if (!PortIsOpen(portId)) return AUDIO3D_ERROR_INVALID_PORT; + if (numAttributes == nullptr) return AUDIO3D_ERROR_INVALID_PARAMETER; constexpr std::uint32_t supported[]{ AUDIO3D_ATTRIBUTE_LATE_REVERB_LEVEL, AUDIO3D_ATTRIBUTE_DOWNMIX_SPREAD_RADIUS, AUDIO3D_ATTRIBUTE_DOWNMIX_SPREAD_HEIGHT_AWARE, }; - if (attributes == nullptr) { - *num_attributes = std::size(supported); - return 0; - } - const std::uint32_t count = std::min(*num_attributes, std::size(supported)); - std::copy_n(std::begin(supported), count, attributes); - *num_attributes = count; + *numAttributes = std::size(supported); + if (attributes != nullptr) std::copy(std::begin(supported), std::end(supported), attributes); return 0; } diff --git a/core/libs/tests/GuestAudio3dPort.cpp b/core/libs/tests/GuestAudio3dPort.cpp index 5b5467437d..7adbb92ede 100644 --- a/core/libs/tests/GuestAudio3dPort.cpp +++ b/core/libs/tests/GuestAudio3dPort.cpp @@ -155,10 +155,11 @@ void CheckAttributes() { Require(sceAudio3dPortGetAttributesSupported(0, nullptr, &num_attributes) == 0 && num_attributes == 3, "null output reports the supported attribute count"); Require(sceAudio3dPortGetAttributesSupported(1, nullptr, &num_attributes) == INVALID_PORT, "supported attributes reject an invalid port"); Require(sceAudio3dPortGetAttributesSupported(0, nullptr, nullptr) == INVALID_PARAMETER, "supported attributes require a count pointer"); - num_attributes = 2; - std::uint32_t partial[3]{0, 0, 99}; - Require(sceAudio3dPortGetAttributesSupported(0, partial, &num_attributes) == 0 && num_attributes == 2, "supported attributes respect the caller capacity"); - Require(partial[0] == supported[0] && partial[1] == supported[1] && partial[2] == 99, "supported attributes copy only the requested entries"); + Require(sceAudio3dPortGetAttributesSupported(1, nullptr, nullptr) == INVALID_PORT, "port validation precedes count validation"); + num_attributes = 0; + std::uint32_t output[4]{0, 0, 0, 99}; + Require(sceAudio3dPortGetAttributesSupported(0, output, &num_attributes) == 0 && num_attributes == 3, "the count is output only"); + Require(output[0] == supported[0] && output[1] == supported[1] && output[2] == supported[2] && output[3] == 99, "the complete list is written without touching the following element"); num_attributes = 3; std::uint32_t all[3]{}; Require(sceAudio3dPortGetAttributesSupported(0, all, &num_attributes) == 0 && num_attributes == 3, "all supported attributes are returned"); diff --git a/docs/dev/debt/libSceAudio3d.md b/docs/dev/debt/libSceAudio3d.md index c7d94441a4..e83c9da9a2 100644 --- a/docs/dev/debt/libSceAudio3d.md +++ b/docs/dev/debt/libSceAudio3d.md @@ -11,7 +11,7 @@ ### Unknown function info - [sceAudio3dPortOpen](../../../core/libs/prx/libSceAudio3d/Export.cpp) (libSceAudio3d) - buffer modes 0 and 1 (also selected by the 0x10 and 0x18 parameter sizes) and 3 beds depend on an SDK version check in the module ([prosper](https://github.com/mattias800/prosper)) and throw. A repeated `sceAudio3dInitialize` (`NOT_READY` in shadPS4, 0 in prosper) and `sceAudio3dPortAdvance` on a full queue (`NOT_READY` in shadPS4, unhandled in KytyPS5) throw too -- [sceAudio3dPortGetAttributesSupported](../../../core/libs/prx/libSceAudio3d/Export.cpp) (libSceAudio3d) - the supported list (late reverb level, downmix spread radius, downmix spread height aware) is inferred from the attributes `sceAudio3dPortSetAttribute` accepts; its order and the count written when the caller capacity is smaller are unverified. [shadPS4](https://github.com/shadps4-emu/shadPS4) returns object attributes (PCM, gain...) instead, prosper and KytyPS5 have no body +- [sceAudio3dPortGetAttributesSupported](../../../core/libs/prx/libSceAudio3d/Export.cpp): the native export validates the port before the count pointer, writes the total count without reading its incoming value, and writes the complete list when an array is supplied. These checks are matched. The list itself remains the host's three port attributes: the native list derives from AudioOut2 device capability masks, SDK version and platform mode, which the host does not yet model. - [sceAudio3dObjectReserve](../../../core/libs/prx/libSceAudio3d/Export.cpp) (libSceAudio3d) - object ids, the `max_objects` limit and the checks of `sceAudio3dObjectReserve` and `sceAudio3dObjectUnreserve` follow [shadPS4](https://github.com/shadps4-emu/shadPS4) (PS4), the only reference with a body: ids count up from 1 per port, skipping 0, 0xFFFFFFFF and ids in use, and a failed reserve writes 0xFFFFFFFF - [sceAudio3dStrError](../../../core/libs/prx/libSceAudio3d/Export.cpp) (libSceAudio3d) - unknown signature - [sceAudio3dPortOpenEx](../../../core/libs/prx/libSceAudio3d/Export.cpp) (libSceAudio3d) - unknown signature From c55342edbb636ded88c78aa64a819494a5f06adb Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:56:09 +0300 Subject: [PATCH 403/523] fix(rtc): use current time for null formatting ticks --- core/libs/prx/libSceRtc/Export.cpp | 44 +++++++++++++++--------------- docs/dev/debt/libSceRtc.md | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceRtc/Export.cpp b/core/libs/prx/libSceRtc/Export.cpp index 7364077dff..3cc3b045de 100644 --- a/core/libs/prx/libSceRtc/Export.cpp +++ b/core/libs/prx/libSceRtc/Export.cpp @@ -579,64 +579,64 @@ int APS5_VABI sceRtcSetMicrosecond(RtcDateTime* time, int microsecond) { return 0; } -int APS5_VABI sceRtcFormatRFC3339(char* date_time, const RtcTick* utc, int time_zone_minutes) { - if (!date_time) return SCE_RTC_ERROR_INVALID_POINTER; +int APS5_VABI sceRtcFormatRFC3339(char* dateTime, const RtcTick* utc, int timeZoneMinutes) { + if (!dateTime) return SCE_RTC_ERROR_INVALID_POINTER; RtcTick now{}; if (!utc) { now.tick = currentTick(); utc = &now; } - if (time_zone_minutes < -1439 || time_zone_minutes > 1439) return SCE_RTC_ERROR_INVALID_VALUE; + if (timeZoneMinutes < -1439 || timeZoneMinutes > 1439) return SCE_RTC_ERROR_INVALID_VALUE; RtcTick local{}; - if (const int result = addTicks(&local, utc, time_zone_minutes, TICKS_PER_MINUTE); result != 0) return result; + if (const int result = addTicks(&local, utc, timeZoneMinutes, TICKS_PER_MINUTE); result != 0) return result; const RtcDateTime time = fromTick(local.tick); - int written = std::snprintf(date_time, 23, "%04u-%02u-%02uT%02u:%02u:%02u.%02u", time.year, time.month, time.day, time.hour, time.minute, time.second, time.microsecond / 10000); - if (time_zone_minutes == 0) { - std::snprintf(date_time + written, 2, "Z"); + int written = std::snprintf(dateTime, 23, "%04u-%02u-%02uT%02u:%02u:%02u.%02u", time.year, time.month, time.day, time.hour, time.minute, time.second, time.microsecond / 10000); + if (timeZoneMinutes == 0) { + std::snprintf(dateTime + written, 2, "Z"); } else { - const int offset = time_zone_minutes < 0 ? -time_zone_minutes : time_zone_minutes; - std::snprintf(date_time + written, 7, "%c%02d:%02d", time_zone_minutes < 0 ? '-' : '+', offset / 60, offset % 60); + const int offset = timeZoneMinutes < 0 ? -timeZoneMinutes : timeZoneMinutes; + std::snprintf(dateTime + written, 7, "%c%02d:%02d", timeZoneMinutes < 0 ? '-' : '+', offset / 60, offset % 60); } return 0; } -int APS5_VABI sceRtcFormatRFC3339LocalTime(char* date_time, const RtcTick* utc) { - if (!date_time) return SCE_RTC_ERROR_INVALID_POINTER; +int APS5_VABI sceRtcFormatRFC3339LocalTime(char* dateTime, const RtcTick* utc) { + if (!dateTime) return SCE_RTC_ERROR_INVALID_POINTER; RtcTick now{}; if (!utc) { now.tick = currentTick(); utc = &now; } if (utc->tick > MAX_TICK) return SCE_RTC_ERROR_INVALID_VALUE; - return sceRtcFormatRFC3339(date_time, utc, localOffsetMinutes(*utc)); + return sceRtcFormatRFC3339(dateTime, utc, localOffsetMinutes(*utc)); } -int APS5_VABI sceRtcFormatRFC2822(char* date_time, const RtcTick* utc, int time_zone_minutes) { - if (!date_time) return SCE_RTC_ERROR_INVALID_POINTER; +int APS5_VABI sceRtcFormatRFC2822(char* dateTime, const RtcTick* utc, int timeZoneMinutes) { + if (!dateTime) return SCE_RTC_ERROR_INVALID_POINTER; RtcTick now{}; if (!utc) { now.tick = currentTick(); utc = &now; } - if (time_zone_minutes < -1439 || time_zone_minutes > 1439) return SCE_RTC_ERROR_INVALID_VALUE; + if (timeZoneMinutes < -1439 || timeZoneMinutes > 1439) return SCE_RTC_ERROR_INVALID_VALUE; RtcTick local{}; - if (const int result = addTicks(&local, utc, time_zone_minutes, TICKS_PER_MINUTE); result != 0) return result; + if (const int result = addTicks(&local, utc, timeZoneMinutes, TICKS_PER_MINUTE); result != 0) return result; const RtcDateTime time = fromTick(local.tick); - const int offset = time_zone_minutes < 0 ? -time_zone_minutes : time_zone_minutes; - std::snprintf(date_time, 32, "%s, %02u %s %04u %02u:%02u:%02u %c%02d%02d", WEEKDAY_NAMES[(local.tick / TICKS_PER_DAY + 1) % 7], time.day, - MONTH_NAMES[time.month - 1], time.year, time.hour, time.minute, time.second, time_zone_minutes < 0 ? '-' : '+', offset / 60, offset % 60); + const int offset = timeZoneMinutes < 0 ? -timeZoneMinutes : timeZoneMinutes; + std::snprintf(dateTime, 32, "%s, %02u %s %04u %02u:%02u:%02u %c%02d%02d", WEEKDAY_NAMES[(local.tick / TICKS_PER_DAY + 1) % 7], time.day, + MONTH_NAMES[time.month - 1], time.year, time.hour, time.minute, time.second, timeZoneMinutes < 0 ? '-' : '+', offset / 60, offset % 60); return 0; } -int APS5_VABI sceRtcFormatRFC2822LocalTime(char* date_time, const RtcTick* utc) { - if (!date_time) return SCE_RTC_ERROR_INVALID_POINTER; +int APS5_VABI sceRtcFormatRFC2822LocalTime(char* dateTime, const RtcTick* utc) { + if (!dateTime) return SCE_RTC_ERROR_INVALID_POINTER; RtcTick now{}; if (!utc) { now.tick = currentTick(); utc = &now; } if (utc->tick > MAX_TICK) return SCE_RTC_ERROR_INVALID_VALUE; - return sceRtcFormatRFC2822(date_time, utc, localOffsetMinutes(*utc)); + return sceRtcFormatRFC2822(dateTime, utc, localOffsetMinutes(*utc)); } int APS5_VABI sceRtcParseRFC3339(RtcTick* utc, const char* date_time) { diff --git a/docs/dev/debt/libSceRtc.md b/docs/dev/debt/libSceRtc.md index 8b683a05c6..56985f25a2 100644 --- a/docs/dev/debt/libSceRtc.md +++ b/docs/dev/debt/libSceRtc.md @@ -7,8 +7,8 @@ ### Unknown function info - [sceRtcParseDateTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - a blank zone after seconds is accepted. With fully allocated input storage on PS5 firmware 13.42 and again on 13.60, the same NUL-terminated text returned `BAD_PARSE` when the byte after its terminator was zero and succeeded when that byte was uppercase `T`. This dependence on storage after the terminator is not modeled; controlled backing-byte measurements in a title process are unavailable. -- [sceRtcFormatRFC2822, sceRtcFormatRFC2822LocalTime, sceRtcFormatRFC3339LocalTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - only shadPS4 (PS4) has bodies. A null tick formats the current time, as shadPS4 does and as these three and `sceRtcFormatRFC3339` did in a payload process on a PS5 (firmware 13.60); offsets outside ±1439 minutes return `INVALID_VALUE` like `sceRtcFormatRFC3339`, also with a null tick there; the local-time variants use the host's offset at the given instant (shadPS4 uses `sceKernelGettimezone`, the current offset) - [sceRtcGetDosTime, sceRtcSetDosTime](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - bit layout from shadPS4 (PS4, the standard FAT date/time). A year outside 1980-2107 makes `GetDosTime` return `INVALID_YEAR` after writing 0 for an earlier one and `0xff9fbf7d` (2107-12-31 23:59:58, the largest encodable time) for a later one, and `SetDosTime` decodes the year as the unsigned 7-bit FAT field (1980-2107) without validating the fields, as the disassembly-derived [fpPS4](https://github.com/red-prig/fpPS4) reimplementation of the PS4 library does; not confirmed on a PS5 title, and shadPS4 instead masks out-of-range years into the packed value and sign-extends the 16-bit date +- [sceRtcFormatRFC2822LocalTime, sceRtcFormatRFC3339LocalTime](../../../core/libs/prx/libSceRtc/Export.cpp): the native wrappers obtain the zone from a kernel timezone query, while the host uses the offset at the supplied instant. The kernel timezone configuration and its failure paths are not modeled. Native formatting confirms that null tick pointers select current time, RFC3339 uses two fractional digits, and explicit offsets are limited to -1439 through 1439 minutes. - [sceRtcGetCurrentRawNetworkTick](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - the console reads clock 0x13 and returns its time plus the Unix epoch in ticks, or `0x80B50001` when the clock is unsupported (errno 5). The raw network clock is not modelled; this returns the host clock. Read from the disassembly of libSceRtc.sprx on firmware 13.60, not measured on a console - [sceRtcGetCurrentAdNetworkTick, sceRtcGetCurrentDebugNetworkTick](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - the console reads clocks 0x12 and 0x11 and falls back to network clock 0x10 when either clock is unsupported. These getters use their process-local clock when it was set, otherwise they follow the process-local network clock. The firmware's unsupported-clock result is not modelled. Read from the disassembly of libSceRtc.sprx on firmware 13.60, not measured on a console - [sceRtcSetCurrentTick, sceRtcSetCurrentNetworkTick, sceRtcSetCurrentAdNetworkTick, sceRtcSetCurrentDebugNetworkTick](../../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - a valid tick sets a process-local clock that advances from a monotonic host clock; the host system clock is not changed. The console sets clock 0, 0x10, 0x12 or 0x11. Null pointers return `0x80B50002` and ticks before the Unix epoch return `0x80B50003`, from disassembly of libSceRtc.sprx on firmware 13.60, not measured on a console. sceRtcSetConf passes its third and fourth arguments to the same clock call with clock 0 and throws; what the pair selects is unknown From 2956059cc8b550fe0d3b6cf024e2e0cf54760083 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:57:57 +0300 Subject: [PATCH 404/523] fix(audiopropagation): type audio path update descriptors --- .../AudioPropagation.hpp | 27 ++++++++++++++++++- .../prx/libSceAudioPropagation/Export.cpp | 2 +- docs/dev/debt/libSceAudioPropagation.md | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index 538b6a867f..ad7f009806 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -76,6 +76,31 @@ struct Attribute { std::uint32_t reserved2; }; +struct PathPoint { + AudioPropagationStructDescriptor desc; + AudioPropagationHandle room; + float gain; + float position[3]; + std::byte opaque[8]; +}; + +struct AudioPath { + AudioPropagationStructDescriptor desc; + PathPoint points[4]; + std::uint32_t pointCount; + std::uint32_t flags; + std::byte opaque[8]; +}; + +static_assert(sizeof(PathPoint) == 48); +static_assert(offsetof(PathPoint, room) == 16); +static_assert(offsetof(PathPoint, gain) == 24); +static_assert(offsetof(PathPoint, position) == 28); +static_assert(sizeof(AudioPath) == 224); +static_assert(offsetof(AudioPath, points) == 16); +static_assert(offsetof(AudioPath, pointCount) == 208); +static_assert(offsetof(AudioPath, flags) == 212); + struct RenderInfo { AudioPropagationStructDescriptor desc; AudioPropagationHandle source; @@ -125,7 +150,7 @@ std::int32_t APS5_VABI sceAudioPropagationSourceGetAudioPath(AudioPropagationHan std::int32_t APS5_VABI sceAudioPropagationSourceGetRays(AudioPropagationHandle source, void* rays, std::uint32_t* count); std::int32_t APS5_VABI sceAudioPropagationSourceCalculateAudioPaths(AudioPropagationHandle source, const void* rays, std::uint32_t rayCount, std::uint32_t reflectionOrder, void* paths, std::uint32_t pathCount); std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPaths(AudioPropagationHandle source, const void* entries, std::uint32_t count); -std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHandle path, const void* data, float gain); +std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHandle path, const AudioPropagation::AudioPath* data, float gain); std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle system, const AudioPropagation::RenderInfo* infos, std::uint32_t count); } diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index d1af51030e..f32ef42485 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -293,7 +293,7 @@ std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPaths(AudioPropagationHa return 0; } -std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHandle path, const void* data, float gain) { +std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHandle path, const AudioPath* data, float gain) { static_cast(path); static_cast(data); static_cast(gain); diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 1800ba874d..98545297ca 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -6,6 +6,6 @@ ### Unknown function info -- [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation, NID `tKSmk2JsMAA`) - `(uint64_t path, const void* data, float gain)` follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Audio/AudioPropagation.cs) and SAROS's call, which passes a handle from `sceAudioPropagationSourceGetAudioPath`, path data and gain 1.0. Path updates are unsupported and every call throws without reading or writing caller memory. The no-propagation model yields no path handles; path allocation, ownership, rendering and console error codes remain unknown. +- [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): the native path lookup and update confirm a 64-bit path handle, a read-only 224-byte path description and a float gain in XMM0. The description contains four 48-byte points, a point count and flags; each point contains a descriptor, room handle, gain and three position floats. The last eight bytes of each point and of the description are not consumed by these paths and remain opaque. Protected implementation remains unchanged; allocation, ownership and rendering are not implemented. - [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` selects reflection order; descriptor validation, ray ownership and path generation remain unimplemented; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. Other functions still throw for invalid input; their native validation precedence is not yet implemented - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From 7a596d28aa1bcc58548159db259233e25705728f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 09:59:24 +0300 Subject: [PATCH 405/523] fix(audiopropagation): restore mutable render descriptor fields --- core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp | 7 ++++--- core/libs/prx/libSceAudioPropagation/Export.cpp | 5 ++++- core/libs/tests/GuestAudioPropagation.cpp | 4 ++++ docs/dev/debt/libSceAudioPropagation.md | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index ad7f009806..63e23fb5e8 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -104,10 +104,10 @@ static_assert(offsetof(AudioPath, flags) == 212); struct RenderInfo { AudioPropagationStructDescriptor desc; AudioPropagationHandle source; - void* output; + float* output; std::size_t outputSize; std::uint32_t format; - std::uint32_t reserved; + std::uint32_t channelCount; }; struct PortalParams { @@ -122,6 +122,7 @@ static_assert(sizeof(Attribute) == 0x20); static_assert(sizeof(RenderInfo) == RenderInfoSize); static_assert(offsetof(RenderInfo, source) == 0x10); static_assert(offsetof(RenderInfo, format) == 0x28); +static_assert(offsetof(RenderInfo, channelCount) == 0x2c); static_assert(sizeof(PortalParams) == PortalParamsSize); static_assert(offsetof(PortalParams, rooms) == 0x50); @@ -151,7 +152,7 @@ std::int32_t APS5_VABI sceAudioPropagationSourceGetRays(AudioPropagationHandle s std::int32_t APS5_VABI sceAudioPropagationSourceCalculateAudioPaths(AudioPropagationHandle source, const void* rays, std::uint32_t rayCount, std::uint32_t reflectionOrder, void* paths, std::uint32_t pathCount); std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPaths(AudioPropagationHandle source, const void* entries, std::uint32_t count); std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHandle path, const AudioPropagation::AudioPath* data, float gain); -std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle system, const AudioPropagation::RenderInfo* infos, std::uint32_t count); +std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle system, AudioPropagation::RenderInfo* infos, std::uint32_t count); } diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index f32ef42485..27fca89aad 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -301,7 +301,7 @@ std::int32_t APS5_VABI sceAudioPropagationSourceSetAudioPath(AudioPropagationHan return 0; } -std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle system, const RenderInfo* infos, std::uint32_t count) { +std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle system, RenderInfo* infos, std::uint32_t count) { require(infos != nullptr && count != 0, __func__); auto& state = registry(); std::lock_guard lock(state.mutex); @@ -313,6 +313,9 @@ std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle sy require(info.output != nullptr && info.outputSize != 0, __func__); if (info.format != RenderFormat) throw std::runtime_error(std::string(__func__) + ": render format " + std::to_string(info.format) + " is unknown"); } + for (std::uint32_t index = 0; index < count; ++index) { + infos[index].channelCount = 0; + } return 0; } diff --git a/core/libs/tests/GuestAudioPropagation.cpp b/core/libs/tests/GuestAudioPropagation.cpp index ebb42f21d9..071ab4f0ce 100644 --- a/core/libs/tests/GuestAudioPropagation.cpp +++ b/core/libs/tests/GuestAudioPropagation.cpp @@ -176,6 +176,10 @@ int main() { reset(); Require(sceAudioPropagationSourceRender(system, infos, 1) == 0, "render failed"); Require(untouched(), "empty source erased an existing mix"); + infos[0].channelCount = 99; + Require(sceAudioPropagationSourceRender(system, infos, 1) == 0 && infos[0].channelCount == 0, "fixed render format clears the channel count"); + const auto* tail = reinterpret_cast(outputs[1]); + Require(tail[0] == 0xab, "render wrote past the output size"); reset(); infos[0].outputSize = sizeof(float) * 16; Require(sceAudioPropagationSourceRender(system, infos, 2) == 0, "render of two sources failed"); diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 98545297ca..61e8a959a9 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -7,5 +7,5 @@ ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): the native path lookup and update confirm a 64-bit path handle, a read-only 224-byte path description and a float gain in XMM0. The description contains four 48-byte points, a point count and flags; each point contains a descriptor, room handle, gain and three position floats. The last eight bytes of each point and of the description are not consumed by these paths and remain opaque. Protected implementation remains unchanged; allocation, ownership and rendering are not implemented. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` selects reflection order; descriptor validation, ray ownership and path generation remain unimplemented; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The meaning of the `RenderInfo` word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one `RenderInfo` per call: a count of 0 throws, and larger counts are handled element by element without proof. Other functions still throw for invalid input; their native validation precedence is not yet implemented +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` selects reflection order; descriptor validation, ray ownership and path generation remain unimplemented; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The native render call accepts an array of mutable 48-byte records, with float output at +0x18, byte capacity at +0x20, format at +0x28 and channel count at +0x2c. Format 2 uses 16 float channels per system frame and clears the channel count after rendering; only this format remains supported by the host silence model. The native count limit and output capacity depend on system configuration that the host does not store; those checks remain unmatched. Other functions still throw for invalid input; their native validation precedence is not yet implemented - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From 8a1bd363ef5cc506e9c3a06dc1d254663c89305d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:04:31 +0300 Subject: [PATCH 406/523] fix(move): restore query validation and invalid handle errors --- core/libs/prx/libSceMove/Export.cpp | 18 ++++++++---------- core/libs/tests/GuestMove.cpp | 7 +++++-- docs/dev/debt/libSceMove.md | 11 ++++------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceMove/Export.cpp b/core/libs/prx/libSceMove/Export.cpp index 2ffc51523e..effeda1e5c 100644 --- a/core/libs/prx/libSceMove/Export.cpp +++ b/core/libs/prx/libSceMove/Export.cpp @@ -28,10 +28,12 @@ struct Slot { std::unordered_map openHandles; -int checkHandle(int32_t handle) { +int checkHandle(int32_t handle, bool validArguments = true) { std::lock_guard lock(stateMutex); if (!initialized) return MOVE_ERROR_NOT_INIT; - return openHandles.contains(handle) ? MOVE_OK : MOVE_ERROR_INVALID_HANDLE; + if (!validArguments) return MOVE_ERROR_INVALID_ARG; + if (!openHandles.contains(handle)) return MOVE_ERROR_INVALID_HANDLE; + return MOVE_OK; } } @@ -73,26 +75,22 @@ int APS5_VABI sceMoveClose(int32_t handle) { } int APS5_VABI sceMoveGetDeviceInfo(int32_t handle, void* info) { - if (const int result = checkHandle(handle); result != MOVE_OK) return result; - if (!info) return MOVE_ERROR_INVALID_ARG; + if (const int result = checkHandle(handle, info != nullptr); result != MOVE_OK) return result; return MOVE_NO_CONTROLLER_CONNECTED; } int APS5_VABI sceMoveGetExtensionPortInfo(int32_t handle, void* data) { - if (const int result = checkHandle(handle); result != MOVE_OK) return result; - if (!data) return MOVE_ERROR_INVALID_ARG; + if (const int result = checkHandle(handle, data != nullptr); result != MOVE_OK) return result; return MOVE_NO_CONTROLLER_CONNECTED; } int APS5_VABI sceMoveReadStateLatest(int32_t handle, void* data) { - if (const int result = checkHandle(handle); result != MOVE_OK) return result; - if (!data) return MOVE_ERROR_INVALID_ARG; + if (const int result = checkHandle(handle, data != nullptr); result != MOVE_OK) return result; return MOVE_NO_CONTROLLER_CONNECTED; } int APS5_VABI sceMoveReadStateRecent(int32_t handle, int64_t timestamp, void* data, int32_t* outCount) { - if (const int result = checkHandle(handle); result != MOVE_OK) return result; - if (timestamp < 0 || !data || !outCount) return MOVE_ERROR_INVALID_ARG; + if (const int result = checkHandle(handle, timestamp >= 0 && data != nullptr && outCount != nullptr); result != MOVE_OK) return result; return MOVE_NO_CONTROLLER_CONNECTED; } diff --git a/core/libs/tests/GuestMove.cpp b/core/libs/tests/GuestMove.cpp index b114030e35..bbb55c541d 100644 --- a/core/libs/tests/GuestMove.cpp +++ b/core/libs/tests/GuestMove.cpp @@ -85,8 +85,11 @@ void RequireUnknownHandle(std::int32_t handle) { Require(sceMoveSetVibration(handle, 0xFF) == kInvalidHandle); Require(sceMoveSetLightSphere(handle, 0xFF, 0, 0) == kInvalidHandle); Require(sceMoveResetLightSphere(handle) == kInvalidHandle); - Require(sceMoveGetDeviceInfo(handle, nullptr) == kInvalidHandle); - Require(sceMoveReadStateRecent(handle, -1, nullptr, nullptr) == kInvalidHandle); + Require(sceMoveGetDeviceInfo(handle, nullptr) == kInvalidArg); + Require(sceMoveGetExtensionPortInfo(handle, nullptr) == kInvalidArg); + Require(sceMoveReadStateLatest(handle, nullptr) == kInvalidArg); + Require(sceMoveReadStateRecent(handle, -1, buffer.bytes, &count) == kInvalidArg); + Require(sceMoveReadStateRecent(handle, 0, buffer.bytes, nullptr) == kInvalidArg); Require(buffer.Untouched()); Require(count == -7); } diff --git a/docs/dev/debt/libSceMove.md b/docs/dev/debt/libSceMove.md index d1660178ce..bc12dfcafb 100644 --- a/docs/dev/debt/libSceMove.md +++ b/docs/dev/debt/libSceMove.md @@ -1,10 +1,7 @@ # libSceMove technical debt -### Silent stubs +### Functional -- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) implements only the no-controller path: initialization and termination succeed, `sceMoveOpen` returns a new handle, `sceMoveClose` and `sceMoveResetLightSphere` succeed for an open handle, and the device info, state, extension port, vibration and `sceMoveSetLightSphere` calls return 1 (no controller connected) and write nothing - -### Unknown function info - -- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) (libSceMove) - behaviour, error codes and the handle values (`0x30B0100`, then every `0x100`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/move/move.cpp) (PS4), which checks no handle or open arguments; none is measured on a PS5. Signatures follow [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), except `sceMoveGetExtensionPortInfo`, which follows shadPS4. Before `sceMoveInit` and after `sceMoveTerm` every other call returns `NOT_INIT` (`0x80EE0001`); a null output or a negative timestamp returns `INVALID_ARG` (`0x80EE0003`). Closes when: measured on a PS5 -- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) (libSceMove) - a handle that was never opened, is closed or was opened before `sceMoveTerm` returns `INVALID_HANDLE` (`0x80EE0004`), checked before `INVALID_ARG`, and `sceMoveOpen` returns `ALREADY_OPENED` (`0x80EE0007`) for a type and index that are already open; both codes are named in [shadPS4's move_error.h](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/move/move_error.h), which never returns them, so the results and their order are not measured. `sceMoveOpen` throws for a user other than the initial user, whose console result is not known, and accepts any type and index. Closes when: measured on a PS5 +- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp): the host implements only disconnected controllers. The native module confirms initialization errors, disconnected return 1 without output writes, invalid-handle error 0x80EE0004, and initialization then argument then handle validation for queries. Reset-light-sphere still omits the device ioctl and its failure mapping. +- [sceMoveOpen and sceMoveClose](../../../core/libs/prx/libSceMove/Export.cpp): protected bodies remain unchanged. Native opening resolves a user/device through the input service, rejects duplicate device handles with 0x80EE0007, limits storage to eight slots with 0x80EE0005, and obtains handles from the device ioctl. The host generates monotonically increasing handles, accepts arbitrary type/index values and returns 0x80EE0007 for duplicate slots and throws for other users. Closing an unknown handle returns the confirmed 0x80EE0004. +- [sceMoveReadStateRecent](../../../core/libs/prx/libSceMove/Export.cpp): the native module permits a null data array for SDK versions through 0x03ffffff while still requiring a count pointer and nonnegative timestamp. The host requires the data array for every SDK version. Connected state records and device-service failures remain unimplemented. From 1371338623d2eb30cb32441d117a4b1fa17c5bfc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:04:33 +0300 Subject: [PATCH 407/523] fix(loginservice): restore read-only device request signature --- core/libs/prx/libSceLoginService/Export.cpp | 4 +++- docs/dev/debt/libSceLoginService.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceLoginService/Export.cpp b/core/libs/prx/libSceLoginService/Export.cpp index 43a1b1dcdf..71262ec59b 100644 --- a/core/libs/prx/libSceLoginService/Export.cpp +++ b/core/libs/prx/libSceLoginService/Export.cpp @@ -2,6 +2,8 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct LoginServiceDeviceRequest; + extern "C" { int APS5_VABI sceLoginServiceInitialize(void) { @@ -12,7 +14,7 @@ int APS5_VABI sceLoginServiceTerminate(void) { return 0; } -int APS5_VABI sceLoginServiceRequestDevices(void* param) { +int APS5_VABI sceLoginServiceRequestDevices(const LoginServiceDeviceRequest* param) { (void)param; NotImplemented_nid_no_patch(__func__); return 0; diff --git a/docs/dev/debt/libSceLoginService.md b/docs/dev/debt/libSceLoginService.md index f33713e90d..db043fd9c7 100644 --- a/docs/dev/debt/libSceLoginService.md +++ b/docs/dev/debt/libSceLoginService.md @@ -6,4 +6,4 @@ ### Unknown function info -- [sceLoginServiceRequestDevices](../../../core/libs/prx/libSceLoginService/Export.cpp) (libSceLoginService) - PPSA34015 passes one pointer to a stack buffer (`lea rdi, [rbp-0x80]`) then calls `sceLoginServiceTerminate`. The buffer layout, other arguments and console errors are unknown, so every call throws without reading or writing caller memory +- [sceLoginServiceRequestDevices](../../../core/libs/prx/libSceLoginService/Export.cpp): the native wrapper confirms one read-only request pointer and transfers exactly 64 bytes to the login service. Null input returns 0x813a0003 before the initialization check; an uninitialized service returns 0x813a0001. The request fields are interpreted by the service backend and remain unresolved. The protected export body is unchanged. From 87c42d8e11028e45e87ee667495b2feba4b4e7f7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:09:36 +0300 Subject: [PATCH 408/523] fix(acm): restore panner command encoding and validation --- core/libs/prx/libSceAcm/Export.cpp | 41 +++++++++++++++++++++++++++++- core/libs/tests/GuestAcm.cpp | 32 +++++++++++++++++++++-- docs/dev/debt/libSceAcm.md | 4 +-- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAcm/Export.cpp b/core/libs/prx/libSceAcm/Export.cpp index abc5dc83bc..499ad6d0ba 100644 --- a/core/libs/prx/libSceAcm/Export.cpp +++ b/core/libs/prx/libSceAcm/Export.cpp @@ -87,7 +87,46 @@ int APS5_VABI sceAcm_FFT(void) { return 0; } -int APS5_VABI sceAcm_Panner(void) { +int APS5_VABI sceAcm_Panner(AcmBatchInfo* batch, std::uint32_t sourceCount, const void* const* sources, std::uint32_t sourceMode, std::uint32_t outputMode, std::uint32_t channelCount, const void* const* sourceParameters, const void* const* sourceMatrices, const void* const* inputChannels, void* const* outputChannels) { + if (!batch || sourceCount == 0 || sourceCount > 65535 || sourceMode > 2 || outputMode > 1 || channelCount == 0 || channelCount > 36 || !sources || !sourceParameters || !sourceMatrices || !outputChannels) return SCE_ACM_ERROR_BAD_POINTER; + std::uintptr_t alignment = reinterpret_cast(sources) | reinterpret_cast(sourceParameters) | reinterpret_cast(sourceMatrices) | reinterpret_cast(inputChannels) | reinterpret_cast(outputChannels); + for (std::uint32_t index = 0; index < sourceCount; ++index) { + if (!sources[index] || !sourceParameters[index] || !sourceMatrices[index]) return SCE_ACM_ERROR_BAD_POINTER; + std::uint32_t parameterMode; + std::memcpy(¶meterMode, sourceParameters[index], sizeof(parameterMode)); + if (parameterMode > 1) return SCE_ACM_ERROR_BAD_POINTER; + alignment |= reinterpret_cast(sources[index]) | reinterpret_cast(sourceParameters[index]) | reinterpret_cast(sourceMatrices[index]); + } + for (std::uint32_t index = 0; index < channelCount; ++index) { + if (!outputChannels[index]) return SCE_ACM_ERROR_BAD_POINTER; + alignment |= reinterpret_cast(outputChannels[index]); + if (inputChannels) alignment |= reinterpret_cast(inputChannels[index]); + } + if (alignment & 127) return static_cast(0x8194000A); + const auto offset = batch->offset; + const auto capacity = batch->buffer_size; + if (offset > capacity || capacity - offset < 8) return static_cast(0x81940009); + auto* buffer = static_cast(batch->buffer); + const auto writeWord = [buffer](std::size_t position, std::uint64_t value) { std::memcpy(buffer + position, &value, sizeof(value)); }; + const auto encodedCount = sourceCount < 255 ? sourceCount : 255; + writeWord(offset, (static_cast(channelCount) << 24) | (static_cast(encodedCount) << 16) | (std::uint64_t{1} << (sourceMode + 32)) | (std::uint64_t{1} << (outputMode + 35)) | 0x300); + if (capacity < 128 || capacity - offset < 152) return static_cast(0x81940009); + const auto descriptorOffset = capacity - 128; + std::memset(buffer + descriptorOffset, 0, 128); + writeWord(descriptorOffset, sourceCount); + writeWord(descriptorOffset + 8, reinterpret_cast(inputChannels)); + writeWord(descriptorOffset + 16, reinterpret_cast(outputChannels)); + writeWord(descriptorOffset + 24, reinterpret_cast(sources)); + writeWord(descriptorOffset + 32, sourceMode | (static_cast(outputMode) << 32)); + writeWord(descriptorOffset + 40, channelCount); + writeWord(descriptorOffset + 48, reinterpret_cast(sourceParameters)); + writeWord(descriptorOffset + 56, reinterpret_cast(sourceMatrices)); + writeWord(offset + 8, 0x8000000000000D); + writeWord(offset + 16, reinterpret_cast(buffer + descriptorOffset)); + if (capacity - offset < 160) return static_cast(0x81940009); + writeWord(offset + 24, 10); + batch->offset = offset + 32; + batch->buffer_size = descriptorOffset; return 0; } diff --git a/core/libs/tests/GuestAcm.cpp b/core/libs/tests/GuestAcm.cpp index c6691f898d..c9760348db 100644 --- a/core/libs/tests/GuestAcm.cpp +++ b/core/libs/tests/GuestAcm.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include "SceTypes.hpp" extern "C" { int APS5_VABI sceAcmContextCreate(std::uint32_t*); @@ -11,7 +13,7 @@ int APS5_VABI sceAcmBatchStartBuffers(std::uint32_t, std::uint32_t, const void* int APS5_VABI sceAcmBatchWait(std::uint32_t, std::uint32_t, std::uint32_t); int APS5_VABI sceAcm_ConvReverb_SharedInput(void); int APS5_VABI sceAcm_FFT(void); -int APS5_VABI sceAcm_Panner(void); +int APS5_VABI sceAcm_Panner(AcmBatchInfo*, std::uint32_t, const void* const*, std::uint32_t, std::uint32_t, std::uint32_t, const void* const*, const void* const*, const void* const*, void* const*); } static void Require(bool value) { @@ -52,7 +54,33 @@ int main() { Require(sceAcm_ConvReverb_SharedInput() == 0); Require(sceAcm_FFT() == 0); - Require(sceAcm_Panner() == 0); + alignas(128) std::byte commands[256]{}; + alignas(128) std::uint32_t sourceData[32]{}; + alignas(128) const void* sources[16]{sourceData}; + alignas(128) void* outputs[16]{sourceData}; + AcmBatchInfo commandBatch{commands, 0, sizeof(commands)}; + Require(sceAcm_Panner(&commandBatch, 0, sources, 0, 0, 1, sources, sources, nullptr, outputs) == BadPointer); + sourceData[0] = 2; + Require(sceAcm_Panner(&commandBatch, 1, sources, 0, 0, 1, sources, sources, nullptr, outputs) == BadPointer); + sourceData[0] = 0; + outputs[0] = reinterpret_cast(sourceData) + 4; + Require(sceAcm_Panner(&commandBatch, 1, sources, 0, 0, 1, sources, sources, nullptr, outputs) == static_cast(0x8194000A)); + outputs[0] = sourceData; + Require(sceAcm_Panner(&commandBatch, 1, sources, 2, 1, 1, sources, sources, nullptr, outputs) == 0); + Require(commandBatch.offset == 32 && commandBatch.buffer_size == 128); + std::uint64_t commandWords[4]; + std::memcpy(commandWords, commands, sizeof(commandWords)); + Require(commandWords[0] == ((std::uint64_t{1} << 34) | (std::uint64_t{1} << 36) | 0x01010300)); + Require(commandWords[1] == 0x8000000000000D && commandWords[2] == reinterpret_cast(commands + 128) && commandWords[3] == 10); + std::uint64_t descriptor[16]; + std::memcpy(descriptor, commands + 128, sizeof(descriptor)); + Require(descriptor[0] == 1 && descriptor[1] == 0 && descriptor[2] == reinterpret_cast(outputs)); + Require(descriptor[3] == reinterpret_cast(sources) && descriptor[4] == 0x100000002 && descriptor[5] == 1); + Require(descriptor[6] == reinterpret_cast(sources) && descriptor[7] == reinterpret_cast(sources)); + for (std::size_t index = 8; index < 16; ++index) Require(descriptor[index] == 0); + commandBatch = {commands, 0, 152}; + Require(sceAcm_Panner(&commandBatch, 1, sources, 0, 0, 1, sources, sources, nullptr, outputs) == static_cast(0x81940009)); + Require(commandBatch.offset == 0 && commandBatch.buffer_size == 152); Require(sceAcmContextDestroy(context1) == 0); Require(sceAcmContextDestroy(context1) == BadContext); diff --git a/docs/dev/debt/libSceAcm.md b/docs/dev/debt/libSceAcm.md index 7bdccfcf99..cde21f87be 100644 --- a/docs/dev/debt/libSceAcm.md +++ b/docs/dev/debt/libSceAcm.md @@ -2,11 +2,11 @@ ### Silent stubs -- [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) batches execute as silent no-ops, so `sceAcm_ConvReverb_SharedInput`, `sceAcm_FFT` and `sceAcm_Panner` return success without encoding audio commands. +- [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) batches execute as silent no-ops, and do not execute encoded panner commands. `sceAcm_ConvReverb_SharedInput` and `sceAcm_FFT` return success without encoding audio commands. ### Unknown function info -- Context validation error codes (0x81940002 for bad context and 0x81940006 for bad pointer) for [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) follow prosper's `hle_audio_spatial.cpp`. KytyPS5 (`acm.cpp`) and prosper do not track individual batch identifiers, so `sceAcmBatchWait` returns success for valid contexts without inspecting batch IDs. Hardware ACM DSP coprocessor scheduling and console error verification remain unmeasured. +- Context creation, batch submission and waiting use host identifiers instead of the native device and ioctl requests. Batch identifiers, timeout handling, DSP scheduling and service-side errors remain unimplemented. Panner source parameter and matrix payloads are forwarded by the native library; their DSP interpretation remains unverified. - [sceAcm_ConvReverb_SharedIr, sceAcm_IFFT](../../../core/libs/prx/libSceAcm/Export.cpp) (libSceAcm) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAcm.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim - [`sceAcmBatchJobNotification`](../../../core/libs/prx/libSceAcm/Export.cpp) is a parameterless pending declaration for import resolution. Its signature and behavior are unverified; it throws through `NotImplemented_nid_no_patch`. From e6a7c626194885f5aefce655233540acc1bb7d7b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:11:48 +0300 Subject: [PATCH 409/523] fix(acm): restore fft command encoding and argument contract --- core/libs/prx/libSceAcm/Export.cpp | 74 +++++++++++++++++++++++++++++- core/libs/tests/GuestAcm.cpp | 40 +++++++++++++++- docs/dev/debt/libSceAcm.md | 4 +- 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAcm/Export.cpp b/core/libs/prx/libSceAcm/Export.cpp index 499ad6d0ba..8ce60dc2df 100644 --- a/core/libs/prx/libSceAcm/Export.cpp +++ b/core/libs/prx/libSceAcm/Export.cpp @@ -83,7 +83,79 @@ int APS5_VABI sceAcm_ConvReverb_SharedInput(void) { return 0; } -int APS5_VABI sceAcm_FFT(void) { +int APS5_VABI sceAcm_FFT(AcmBatchInfo* batch, std::int32_t transformSize, std::uint32_t transformCount, std::int32_t inputFormat, const void* const* inputs, std::int32_t outputFormat, void* const* outputs, std::int32_t flags) { + if (!batch || transformCount == 0 || transformCount > 0x7FFFFFFF || !inputs || !outputs || (flags & 3) == 3 || inputFormat < 0 || inputFormat > 1 || outputFormat < 0 || outputFormat > 1) return SCE_ACM_ERROR_BAD_POINTER; + if (transformSize != 512 && transformSize != 1024 && transformSize != 2048) return SCE_ACM_ERROR_BAD_POINTER; + const bool realInput = (flags & 1) != 0; + if (transformSize == 2048 && !realInput) return SCE_ACM_ERROR_BAD_POINTER; + const std::uint32_t inputBytes = transformSize * (inputFormat == 1 ? 2 : 4); + const std::uint32_t outputBytes = transformSize * (outputFormat == 1 ? 2 : 4); + const std::uint32_t inputBlocks = (inputBytes >> 9) >> (realInput ? 1 : 0); + const std::uint32_t outputBlocks = outputBytes >> 9; + std::uint32_t transformCommand; + std::uint32_t inputCommand; + std::uint32_t outputCommand; + std::uint32_t setupCommand; + std::uint32_t sizeCode; + if (transformSize == 512) { + sizeCode = 4; + transformCommand = realInput ? 7 : 11; + inputCommand = realInput ? 2 * inputFormat : 9 + inputFormat; + outputCommand = 4 | outputFormat; + setupCommand = realInput ? 6 : 12; + } else if (transformSize == 1024) { + sizeCode = 5; + transformCommand = realInput ? 8 : 13; + inputCommand = realInput ? 2 * inputFormat : 14 | inputFormat; + outputCommand = 5 | (2 * outputFormat); + setupCommand = realInput ? 10 : 12; + } else { + sizeCode = 6; + transformCommand = 6; + inputCommand = 2 * inputFormat; + outputCommand = 4 | outputFormat; + setupCommand = 8; + } + auto* buffer = static_cast(batch->buffer); + auto offset = batch->offset; + const auto capacity = batch->buffer_size; + const auto appendWord = [&](std::uint64_t value) { + if (offset > capacity || capacity - offset < sizeof(value)) return false; + std::memcpy(buffer + offset, &value, sizeof(value)); + offset += sizeof(value); + return true; + }; + std::uint64_t header = static_cast(sizeCode) << 8; + if (!(flags & 8)) { + const auto encodedCount = transformCount < 64000 ? transformCount : 64000; + header |= (std::uint64_t{1} << 16) | (static_cast(transformSize) << 8) | (static_cast(flags & 6) << 19) | (static_cast(flags & 1) << 23) | (std::uint64_t{1} << (inputFormat == 1 ? 24 : 26)) | (std::uint64_t{1} << (outputFormat == 1 ? 25 : 27)) | (static_cast(encodedCount) << 28); + } + constexpr int noSpace = static_cast(0x81940009); + constexpr int badAlignment = static_cast(0x8194000A); + if (!appendWord(header) || !appendWord(10 | (setupCommand << 8))) return noSpace; + for (std::uint32_t index = 0; index < transformCount; ++index) { + const auto inputIndex = static_cast(index) * ((flags & 2) ? 2 : 1); + const auto input = reinterpret_cast(inputs[inputIndex]); + const auto secondInput = (flags & 2) ? reinterpret_cast(inputs[inputIndex + 1]) : 0; + if (!(flags & 2) && !input) return SCE_ACM_ERROR_BAD_POINTER; + if ((input | secondInput) & 127) return badAlignment; + const auto inputRecordSize = (flags & 2) ? 24u : 16u; + if (offset > capacity || capacity - offset < inputRecordSize) return noSpace; + appendWord(((flags & 2) ? 7 : 6) | (static_cast(inputBlocks) << 32) | ((flags & 2) ? static_cast(inputBytes / 2) << 48 : 0)); + appendWord(input); + if (flags & 2) appendWord(secondInput); + if (!appendWord(10 | (inputCommand << 8)) || !appendWord(10 | (transformCommand << 8)) || !appendWord(10 | (outputCommand << 8))) return noSpace; + const auto outputIndex = static_cast(index) * ((flags & 4) ? 2 : 1); + const auto output = reinterpret_cast(outputs[outputIndex]); + const auto secondOutput = (flags & 4) ? reinterpret_cast(outputs[outputIndex + 1]) : 0; + if ((output | secondOutput) & 127) return badAlignment; + const auto outputRecordSize = (flags & 4) ? 24u : 16u; + if (offset > capacity || capacity - offset < outputRecordSize) return noSpace; + appendWord(((flags & 4) ? 9 : 8) | (static_cast(outputBlocks) << 32) | ((flags & 4) ? static_cast(outputBytes / 2) << 48 : 0)); + appendWord(output); + if (flags & 4) appendWord(secondOutput); + } + batch->offset = offset; return 0; } diff --git a/core/libs/tests/GuestAcm.cpp b/core/libs/tests/GuestAcm.cpp index c9760348db..9f78524061 100644 --- a/core/libs/tests/GuestAcm.cpp +++ b/core/libs/tests/GuestAcm.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "SceTypes.hpp" extern "C" { @@ -12,7 +13,7 @@ int APS5_VABI sceAcmBatchStartBuffer(std::uint32_t, const void*, std::size_t, vo int APS5_VABI sceAcmBatchStartBuffers(std::uint32_t, std::uint32_t, const void* const[], void*, std::uint32_t*); int APS5_VABI sceAcmBatchWait(std::uint32_t, std::uint32_t, std::uint32_t); int APS5_VABI sceAcm_ConvReverb_SharedInput(void); -int APS5_VABI sceAcm_FFT(void); +int APS5_VABI sceAcm_FFT(AcmBatchInfo*, std::int32_t, std::uint32_t, std::int32_t, const void* const*, std::int32_t, void* const*, std::int32_t); int APS5_VABI sceAcm_Panner(AcmBatchInfo*, std::uint32_t, const void* const*, std::uint32_t, std::uint32_t, std::uint32_t, const void* const*, const void* const*, const void* const*, void* const*); } @@ -53,7 +54,7 @@ int main() { Require(sceAcmBatchWait(context2, batch2, 0) == 0); Require(sceAcm_ConvReverb_SharedInput() == 0); - Require(sceAcm_FFT() == 0); + Require(sceAcm_FFT(nullptr, 0, 0, 0, nullptr, 0, nullptr, 0) == BadPointer); alignas(128) std::byte commands[256]{}; alignas(128) std::uint32_t sourceData[32]{}; alignas(128) const void* sources[16]{sourceData}; @@ -78,6 +79,41 @@ int main() { Require(descriptor[3] == reinterpret_cast(sources) && descriptor[4] == 0x100000002 && descriptor[5] == 1); Require(descriptor[6] == reinterpret_cast(sources) && descriptor[7] == reinterpret_cast(sources)); for (std::size_t index = 8; index < 16; ++index) Require(descriptor[index] == 0); + commandBatch = {commands, 0, sizeof(commands)}; + Require(sceAcm_FFT(&commandBatch, 2048, 1, 0, sources, 0, outputs, 0) == BadPointer); + Require(sceAcm_FFT(&commandBatch, 512, 1, 0, sources, 0, outputs, 3) == BadPointer); + Require(sceAcm_FFT(&commandBatch, 512, 1, 0, sources, 0, outputs, 0) == 0); + Require(commandBatch.offset == 72 && commandBatch.buffer_size == sizeof(commands)); + std::uint64_t fftWords[9]; + std::memcpy(fftWords, commands, sizeof(fftWords)); + Require(fftWords[0] == 0x1C030400 && fftWords[1] == 0xC0A); + Require(fftWords[2] == 0x400000006 && fftWords[3] == reinterpret_cast(sourceData)); + Require(fftWords[4] == 0x90A && fftWords[5] == 0xB0A && fftWords[6] == 0x40A); + Require(fftWords[7] == 0x400000008 && fftWords[8] == reinterpret_cast(sourceData)); + commandBatch = {commands, 0, 71}; + Require(sceAcm_FFT(&commandBatch, 512, 1, 0, sources, 0, outputs, 0) == static_cast(0x81940009)); + Require(commandBatch.offset == 0); + commandBatch = {commands, 0, sizeof(commands)}; + Require(sceAcm_FFT(&commandBatch, 2048, 1, 1, sources, 1, outputs, 9) == 0); + std::memcpy(fftWords, commands, sizeof(fftWords)); + Require(fftWords[0] == 0x600 && fftWords[1] == 0x80A && fftWords[4] == 0x20A && fftWords[5] == 0x60A && fftWords[6] == 0x50A); + sources[1] = sourceData; + outputs[1] = sourceData; + for (const auto size : {512, 1024, 2048}) { + for (std::int32_t inputFormat = 0; inputFormat < 2; ++inputFormat) { + for (std::int32_t outputFormat = 0; outputFormat < 2; ++outputFormat) { + for (std::int32_t flags = 0; flags < 16; ++flags) { + commandBatch = {commands, 0, sizeof(commands)}; + const auto result = sceAcm_FFT(&commandBatch, size, 1, inputFormat, sources, outputFormat, outputs, flags); + if ((flags & 3) == 3 || (size == 2048 && !(flags & 1))) { + Require(result == BadPointer && commandBatch.offset == 0); + } else { + Require(result == 0 && commandBatch.offset == 72 + ((flags & 2) ? 8 : 0) + ((flags & 4) ? 8 : 0)); + } + } + } + } + } commandBatch = {commands, 0, 152}; Require(sceAcm_Panner(&commandBatch, 1, sources, 0, 0, 1, sources, sources, nullptr, outputs) == static_cast(0x81940009)); Require(commandBatch.offset == 0 && commandBatch.buffer_size == 152); diff --git a/docs/dev/debt/libSceAcm.md b/docs/dev/debt/libSceAcm.md index cde21f87be..dde32286b3 100644 --- a/docs/dev/debt/libSceAcm.md +++ b/docs/dev/debt/libSceAcm.md @@ -2,11 +2,11 @@ ### Silent stubs -- [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) batches execute as silent no-ops, and do not execute encoded panner commands. `sceAcm_ConvReverb_SharedInput` and `sceAcm_FFT` return success without encoding audio commands. +- [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) batches execute as silent no-ops, and do not execute encoded panner and FFT commands. `sceAcm_ConvReverb_SharedInput` returns success without encoding audio commands. ### Unknown function info -- Context creation, batch submission and waiting use host identifiers instead of the native device and ioctl requests. Batch identifiers, timeout handling, DSP scheduling and service-side errors remain unimplemented. Panner source parameter and matrix payloads are forwarded by the native library; their DSP interpretation remains unverified. +- [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp) context creation, batch submission and waiting use host identifiers instead of the native device and ioctl requests. Batch identifiers, timeout handling, DSP scheduling and service-side errors remain unimplemented. Panner source parameter and matrix payloads are forwarded by the native library; their DSP interpretation remains unverified. - [sceAcm_ConvReverb_SharedIr, sceAcm_IFFT](../../../core/libs/prx/libSceAcm/Export.cpp) (libSceAcm) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAcm.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim - [`sceAcmBatchJobNotification`](../../../core/libs/prx/libSceAcm/Export.cpp) is a parameterless pending declaration for import resolution. Its signature and behavior are unverified; it throws through `NotImplemented_nid_no_patch`. From c4e56885099a83e67434027e9e707b16dd9e9c5c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:11:49 +0300 Subject: [PATCH 410/523] docs(audiopropagation): clarify remaining render gaps --- docs/dev/debt/libSceAudioPropagation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 61e8a959a9..578552a304 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -7,5 +7,5 @@ ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): the native path lookup and update confirm a 64-bit path handle, a read-only 224-byte path description and a float gain in XMM0. The description contains four 48-byte points, a point count and flags; each point contains a descriptor, room handle, gain and three position floats. The last eight bytes of each point and of the description are not consumed by these paths and remain opaque. Protected implementation remains unchanged; allocation, ownership and rendering are not implemented. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of `sceAudioPropagationSourceCalculateAudioPaths` selects reflection order; descriptor validation, ray ownership and path generation remain unimplemented; `sceAudioPropagationSourceGetAudioPath` output width and invalid-handle/null-output/index errors are confirmed. The native render call accepts an array of mutable 48-byte records, with float output at +0x18, byte capacity at +0x20, format at +0x28 and channel count at +0x2c. Format 2 uses 16 float channels per system frame and clears the channel count after rendering; only this format remains supported by the host silence model. The native count limit and output capacity depend on system configuration that the host does not store; those checks remain unmatched. Other functions still throw for invalid input; their native validation precedence is not yet implemented +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options and frame configuration remain unknown. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes silence over the supplied capacity, without native accumulation or frame/count limits. Other formats and native validation precedence remain unimplemented. Source/system diagnostic fields still need reconstruction. - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From 83fbdef29930035934771596cf41cd611baf2863 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:15:51 +0300 Subject: [PATCH 411/523] fix(acm): recover shared reverb descriptors and signature --- core/libs/SceTypes.hpp | 42 ++++++++++++++++++++++++++++++ core/libs/prx/libSceAcm/Export.cpp | 2 +- core/libs/tests/GuestAcm.cpp | 4 +-- docs/dev/debt/libSceAcm.md | 2 ++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index a85ce12b51..b0d1fd063f 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -432,6 +432,48 @@ struct AcmBatchError { std::uint32_t reserved[8]; }; +struct AcmReverbBuffer { + void* samples; + const void* blockMetadata; +}; + +struct AcmReverbInput { + std::uint32_t blockSize; + std::uint32_t blockCount; + std::uint32_t channelCount; + std::uint32_t sampleFormat; + std::uint32_t currentBlock; + std::uint32_t availableBlocks; + const AcmReverbBuffer* const* history; + const void* const* inputChannels; + std::uint32_t pendingBlocks; +}; + +struct AcmReverbFilter { + std::uint32_t blockSize; + std::uint32_t blockCount; + std::uint32_t channelCount; + std::uint32_t sampleFormat; + const void* blockMetadata; + const AcmReverbBuffer* const* channels; +}; + +struct AcmReverbOutput { + std::uint32_t blockSize; + std::uint32_t channelCount; + void* const* samples; + void* const* workspace; + float gain; + float previousGain; + void* const* previousSamples; +}; + +static_assert(sizeof(AcmReverbBuffer) == 16); +static_assert(offsetof(AcmReverbInput, history) == 24); +static_assert(offsetof(AcmReverbInput, pendingBlocks) == 40); +static_assert(sizeof(AcmReverbFilter) == 32); +static_assert(offsetof(AcmReverbOutput, previousSamples) == 32); + struct AcmBatchInfo { void* buffer; std::size_t offset; diff --git a/core/libs/prx/libSceAcm/Export.cpp b/core/libs/prx/libSceAcm/Export.cpp index 8ce60dc2df..914d474f0b 100644 --- a/core/libs/prx/libSceAcm/Export.cpp +++ b/core/libs/prx/libSceAcm/Export.cpp @@ -79,7 +79,7 @@ int APS5_VABI sceAcmContextDestroy(AcmContextId context) { return 0; } -int APS5_VABI sceAcm_ConvReverb_SharedInput(void) { +int APS5_VABI sceAcm_ConvReverb_SharedInput(AcmBatchInfo* batch, std::uint32_t mode, AcmReverbInput* input, std::uint32_t outputCount, const AcmReverbFilter* const* filters, const float* gains, const AcmReverbOutput* const* outputs) { return 0; } diff --git a/core/libs/tests/GuestAcm.cpp b/core/libs/tests/GuestAcm.cpp index 9f78524061..9ba39f03ee 100644 --- a/core/libs/tests/GuestAcm.cpp +++ b/core/libs/tests/GuestAcm.cpp @@ -12,7 +12,7 @@ int APS5_VABI sceAcmContextDestroy(std::uint32_t); int APS5_VABI sceAcmBatchStartBuffer(std::uint32_t, const void*, std::size_t, void*, std::uint32_t*); int APS5_VABI sceAcmBatchStartBuffers(std::uint32_t, std::uint32_t, const void* const[], void*, std::uint32_t*); int APS5_VABI sceAcmBatchWait(std::uint32_t, std::uint32_t, std::uint32_t); -int APS5_VABI sceAcm_ConvReverb_SharedInput(void); +int APS5_VABI sceAcm_ConvReverb_SharedInput(AcmBatchInfo*, std::uint32_t, AcmReverbInput*, std::uint32_t, const AcmReverbFilter* const*, const float*, const AcmReverbOutput* const*); int APS5_VABI sceAcm_FFT(AcmBatchInfo*, std::int32_t, std::uint32_t, std::int32_t, const void* const*, std::int32_t, void* const*, std::int32_t); int APS5_VABI sceAcm_Panner(AcmBatchInfo*, std::uint32_t, const void* const*, std::uint32_t, std::uint32_t, std::uint32_t, const void* const*, const void* const*, const void* const*, void* const*); } @@ -53,7 +53,7 @@ int main() { Require(sceAcmBatchStartBuffers(context2, 0, nullptr, nullptr, &batch2) == 0 && batch2 != 0); Require(sceAcmBatchWait(context2, batch2, 0) == 0); - Require(sceAcm_ConvReverb_SharedInput() == 0); + Require(sceAcm_ConvReverb_SharedInput(nullptr, 0, nullptr, 0, nullptr, nullptr, nullptr) == 0); Require(sceAcm_FFT(nullptr, 0, 0, 0, nullptr, 0, nullptr, 0) == BadPointer); alignas(128) std::byte commands[256]{}; alignas(128) std::uint32_t sourceData[32]{}; diff --git a/docs/dev/debt/libSceAcm.md b/docs/dev/debt/libSceAcm.md index dde32286b3..c94c8ce717 100644 --- a/docs/dev/debt/libSceAcm.md +++ b/docs/dev/debt/libSceAcm.md @@ -10,3 +10,5 @@ - [sceAcm_ConvReverb_SharedIr, sceAcm_IFFT](../../../core/libs/prx/libSceAcm/Export.cpp) (libSceAcm) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAcm.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim - [`sceAcmBatchJobNotification`](../../../core/libs/prx/libSceAcm/Export.cpp) is a parameterless pending declaration for import resolution. Its signature and behavior are unverified; it throws through `NotImplemented_nid_no_patch`. + +- The shared-input reverb signature and descriptor fields are recovered from native accesses. Input history indices are mutable; block metadata representation, reverb command encoding and DSP interpretation remain unimplemented in [libSceAcm](../../../core/libs/prx/libSceAcm/Export.cpp). From ec85f6280d9f63e682ea5b3bd09048fae699cf6a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:15:52 +0300 Subject: [PATCH 412/523] fix(systemgesture): restore native query error contracts --- core/libs/prx/libSceSystemGesture/Export.cpp | 32 +++++++++----------- core/libs/tests/GuestSystemGesture.cpp | 10 +++--- docs/dev/debt/libSceSystemGesture.md | 4 +-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceSystemGesture/Export.cpp b/core/libs/prx/libSceSystemGesture/Export.cpp index 4d74061f67..c73a119122 100644 --- a/core/libs/prx/libSceSystemGesture/Export.cpp +++ b/core/libs/prx/libSceSystemGesture/Export.cpp @@ -10,9 +10,9 @@ #include static constexpr int32_t GESTURE_HANDLE = 1; -static constexpr int SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT = static_cast(0x80D10002); -static constexpr int SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE = static_cast(0x80D10003); -static constexpr int SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY = static_cast(0x80D10005); +static constexpr int SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT = static_cast(0x80890001); +static constexpr int SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE = static_cast(0x80890006); +static constexpr int SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY = static_cast(0x80890004); namespace { @@ -161,35 +161,33 @@ int APS5_VABI sceSystemGestureFinalizePrimitiveTouchRecognizer(void) { return 0; } -int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByIndex(int32_t gesture_handle, uint32_t index, SystemGesturePrimitiveTouchEvent* event) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByIndex(int32_t gestureHandle, uint32_t index, SystemGesturePrimitiveTouchEvent* event) { + if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (gestureHandle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; std::lock_guard lock(primitiveTouchMutex); if (index >= primitiveTouches.size()) return SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY; - if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; *event = buildPrimitiveTouchEvent(primitiveTouches[index]); return 0; } -int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(int32_t gesture_handle, uint16_t primitiveId, SystemGesturePrimitiveTouchEvent* event) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(int32_t gestureHandle, uint16_t primitiveId, SystemGesturePrimitiveTouchEvent* event) { if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (gestureHandle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; std::lock_guard lock(primitiveTouchMutex); const auto tracked = std::find_if(primitiveTouches.begin(), primitiveTouches.end(), [&](const TrackedTouch& touch) { return touch.primitiveId == primitiveId; }); - if (tracked == primitiveTouches.end()) - throw std::runtime_error(std::string(__func__) + ": no primitive touch event with id " + std::to_string(primitiveId)); + if (tracked == primitiveTouches.end()) return static_cast(0x80890005); *event = buildPrimitiveTouchEvent(*tracked); return 0; } -int APS5_VABI sceSystemGestureGetPrimitiveTouchEvents(int32_t gesture_handle, SystemGesturePrimitiveTouchEvent* event_buffer, uint32_t capacity_of_buffer, uint32_t* number_of_event) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; - if (!number_of_event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; +int APS5_VABI sceSystemGestureGetPrimitiveTouchEvents(int32_t gestureHandle, SystemGesturePrimitiveTouchEvent* eventBuffer, uint32_t capacityOfBuffer, uint32_t* numberOfEvent) { + if (!eventBuffer || !capacityOfBuffer || !numberOfEvent) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (gestureHandle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; std::lock_guard lock(primitiveTouchMutex); - const std::uint32_t reported = static_cast(std::min(primitiveTouches.size(), capacity_of_buffer)); - if (reported > 0 && !event_buffer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; - for (std::uint32_t i = 0; i < reported; ++i) event_buffer[i] = buildPrimitiveTouchEvent(primitiveTouches[i]); - *number_of_event = reported; + const std::uint32_t reported = static_cast(std::min(primitiveTouches.size(), capacityOfBuffer)); + for (std::uint32_t i = 0; i < reported; ++i) eventBuffer[i] = buildPrimitiveTouchEvent(primitiveTouches[i]); + *numberOfEvent = reported; return 0; } diff --git a/core/libs/tests/GuestSystemGesture.cpp b/core/libs/tests/GuestSystemGesture.cpp index 01d5123939..8444279cc7 100644 --- a/core/libs/tests/GuestSystemGesture.cpp +++ b/core/libs/tests/GuestSystemGesture.cpp @@ -174,12 +174,14 @@ static void TestPrimitiveTouchEvents(std::int32_t handle, int invalidArgument, i Require(byId.current_position.x == 300.0f && byId.current_position.y == 400.0f); Require(sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(handle, 4, nullptr) == invalidArgument); Require(sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(handle + 1, 4, &byId) == invalidHandle); - Require(Throws([&] { sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(handle, 9, &byId); })); + Require(sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(handle, 9, &byId) == static_cast(0x80890005)); + Require(sceSystemGestureGetPrimitiveTouchEventByIndex(handle + 1, 0, nullptr) == invalidArgument); std::array events{}; std::uint32_t reported = 7; Require(sceSystemGestureGetPrimitiveTouchEvents(handle, events.data(), 1, &reported) == 0); Require(reported == 1); + Require(sceSystemGestureGetPrimitiveTouchEvents(handle, events.data(), 0, &reported) == invalidArgument); Require(events[0].primitive_id == 3); reported = 7; Require(sceSystemGestureGetPrimitiveTouchEvents(handle, events.data(), 2, &reported) == 0); @@ -209,8 +211,8 @@ static void TestPrimitiveTouchEvents(std::int32_t handle, int invalidArgument, i } int main() { - constexpr int invalidArgument = static_cast(0x80D10002); - constexpr int invalidHandle = static_cast(0x80D10003); + constexpr int invalidArgument = static_cast(0x80890001); + constexpr int invalidHandle = static_cast(0x80890006); const std::int32_t handle = sceSystemGestureOpen(0, nullptr); std::array buffer; @@ -229,7 +231,7 @@ int main() { Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 0); Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle + 1) == invalidHandle); - constexpr int indexOutOfArray = static_cast(0x80D10005); + constexpr int indexOutOfArray = static_cast(0x80890004); SystemGestureTouchRecognizer recognizer{}; SystemGestureRectangle rectangle{}; count = 7; diff --git a/docs/dev/debt/libSceSystemGesture.md b/docs/dev/debt/libSceSystemGesture.md index e0bc2e09c2..eff9ccbe7c 100644 --- a/docs/dev/debt/libSceSystemGesture.md +++ b/docs/dev/debt/libSceSystemGesture.md @@ -2,9 +2,9 @@ ### Silent stubs -- [System gesture recognizers](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - `sceSystemGestureGetTouchEvents`, `GetTouchEventsCount`, `GetTouchEventByIndex`, `GetTouchEventByEventID` and the recognizer append, remove, reset and update calls report that no recognizer ever matches, so a title receives primitive touch events but no tap, drag, hold, pinch, rotation or flick event. Handle and pointer arguments are still checked across all query entry points. Error codes follow the 0x80D1 values main already uses in this file; KytyPS5 uses 0x8089 (INVALID_ARGUMENT 0x80890001, INVALID_HANDLE 0x80890006), and which one the console returns is not verified. `sceSystemGestureGetTouchRecognizerInformation` returns the gesture type and rectangle given to `sceSystemGestureCreateTouchRecognizer` or `UpdateTouchRecognizerRectangle`, stored in the title's recognizer buffer in a layout private to this file, and always reports `updated_time` 0 because no recognizer is ever updated with input. It throws for a recognizer that was not created by `sceSystemGestureCreateTouchRecognizer` or has no rectangle, since the console's result in those cases is not known +- [System gesture recognizers](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - `sceSystemGestureGetTouchEvents`, `GetTouchEventsCount`, `GetTouchEventByIndex`, `GetTouchEventByEventID` and the recognizer append, remove, reset and update calls report that no recognizer ever matches, so a title receives primitive touch events but no tap, drag, hold, pinch, rotation or flick event. Handle and pointer arguments are still checked across all query entry points. `sceSystemGestureGetTouchRecognizerInformation` returns the gesture type and rectangle given to `sceSystemGestureCreateTouchRecognizer` or `UpdateTouchRecognizerRectangle`, stored in the title's recognizer buffer in a layout private to this file, and always reports `updated_time` 0 because no recognizer is ever updated with input. It throws for a recognizer that was not created by `sceSystemGestureCreateTouchRecognizer` or has no rectangle, since the console's result in those cases is not known ### Unknown function info -- [Primitive touch events](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - `sceSystemGestureUpdatePrimitiveTouchRecognizer` reads the pad reports the title passes in, `{int32 padHandle, int32 reportNumber, const ScePadData* padDataBuffer, 8 reserved bytes}`, as [fpPS4](https://github.com/red-prig/fpPS4/blob/585d06fa7c04698c41272b95a696a14c4180e13e/src/ps4_libscesystemgesture.pas) records `SceSystemGestureTouchPadData`; `padHandle` is not checked and a report of a pad other than the one the title opened is not told apart. The touch states 0 inactive, 1 begin, 2 active and 3 end are the Vita ones in [RPCS3](https://github.com/RPCS3/rpcs3/blob/38c444cfa1dbe152daa63b1703c12172078cd056/rpcs3/Emu/PSP2/Modules/sceSystemGesture.h), whose recognizer, rectangle and primitive event layouts are the ones main already uses, and no public source states the PS5 values. A primitive keeps the touch id the pad reports, its positions are the pad's touchpad coordinates unscaled, `delta_time` and `elapsed_time` come from the reports' own `timestamp`, and `is_updated` is 1 for a primitive whose state or position changed in the update; the console's units, scaling and `is_updated` rule are unverified. An ended touch stays readable as one `end` event until the next report sampled drops it. A null `pInputData`, a null `padDataBuffer` or a `reportNumber` of 0 or less leaves the events unchanged and returns success, an event buffer shorter than the event count reports as many events as it holds, and `sceSystemGestureGetPrimitiveTouchEventByPrimitiveID` throws for an id that no tracked touch has; what the console answers in those cases is not known +- [Primitive touch events](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - `sceSystemGestureUpdatePrimitiveTouchRecognizer` reads the pad reports the title passes in, `{int32 padHandle, int32 reportNumber, const ScePadData* padDataBuffer, 8 reserved bytes}`, as [fpPS4](https://github.com/red-prig/fpPS4/blob/585d06fa7c04698c41272b95a696a14c4180e13e/src/ps4_libscesystemgesture.pas) records `SceSystemGestureTouchPadData`; `padHandle` is not checked and a report of a pad other than the one the title opened is not told apart. The touch states 0 inactive, 1 begin, 2 active and 3 end are the Vita ones in [RPCS3](https://github.com/RPCS3/rpcs3/blob/38c444cfa1dbe152daa63b1703c12172078cd056/rpcs3/Emu/PSP2/Modules/sceSystemGesture.h), whose recognizer, rectangle and primitive event layouts are the ones main already uses, and no public source states the PS5 values. A primitive keeps the touch id the pad reports, its positions are the pad's touchpad coordinates unscaled, `delta_time` and `elapsed_time` come from the reports' own `timestamp`, and `is_updated` is 1 for a primitive whose state or position changed in the update; the console's units, scaling and `is_updated` rule are unverified. An ended touch stays readable as one `end` event until the next report sampled drops it. A null `pInputData`, a null `padDataBuffer` or a `reportNumber` of 0 or less leaves the events unchanged and returns success, an event buffer shorter than the event count reports as many events as it holds, while native update null-pointer and report-count checks are not yet matched - [sceSystemGestureDebugGetVersion](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemGesture.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim From b27046d68d0c052f02ebc48d1e4a60ffce680021 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:16:51 +0300 Subject: [PATCH 413/523] fix(systemgesture): isolate contexts and restore handle lifecycle --- core/libs/prx/libSceSystemGesture/Export.cpp | 183 ++++++++++++------- core/libs/tests/GuestSystemGesture.cpp | 29 ++- 2 files changed, 146 insertions(+), 66 deletions(-) diff --git a/core/libs/prx/libSceSystemGesture/Export.cpp b/core/libs/prx/libSceSystemGesture/Export.cpp index c73a119122..346cd57893 100644 --- a/core/libs/prx/libSceSystemGesture/Export.cpp +++ b/core/libs/prx/libSceSystemGesture/Export.cpp @@ -3,13 +3,13 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include +#include #include #include #include #include #include -static constexpr int32_t GESTURE_HANDLE = 1; static constexpr int SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT = static_cast(0x80890001); static constexpr int SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE = static_cast(0x80890006); static constexpr int SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY = static_cast(0x80890004); @@ -54,7 +54,19 @@ struct TrackedTouch { }; std::mutex primitiveTouchMutex; -std::vector primitiveTouches; +struct GestureContext { + std::int32_t handle = 0; + std::vector touches; +}; + +std::array gestureContexts; +bool gestureInitialized = false; +std::uint16_t gestureGeneration = 0; + +GestureContext* findGestureContext(std::int32_t gestureHandle) { + const auto found = std::find_if(gestureContexts.begin(), gestureContexts.end(), [gestureHandle](const GestureContext& context) { return context.handle != 0 && context.handle == gestureHandle; }); + return found == gestureContexts.end() ? nullptr : &*found; +} struct TouchSample { std::uint16_t primitiveId; @@ -73,7 +85,7 @@ std::vector reportSamples(const PadData& report) { return samples; } -void sampleReport(const PadData& report) { +void sampleReport(std::vector& primitiveTouches, const PadData& report) { std::erase_if(primitiveTouches, [](const TrackedTouch& touch) { return touch.eventState == TOUCH_STATE_END; }); for (TrackedTouch& touch : primitiveTouches) { touch.sampled = false; @@ -132,19 +144,29 @@ SystemGesturePrimitiveTouchEvent buildPrimitiveTouchEvent(const TrackedTouch& to extern "C" { -int APS5_VABI sceSystemGestureAppendTouchRecognizer(int32_t gesture_handle, SystemGestureTouchRecognizer* recognizer) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureAppendTouchRecognizer(int32_t gestureHandle, SystemGestureTouchRecognizer* recognizer) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; return 0; } -int APS5_VABI sceSystemGestureClose(int32_t gesture_handle) { - return gesture_handle == GESTURE_HANDLE ? 0 : SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureClose(int32_t gestureHandle) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + auto* context = findGestureContext(gestureHandle); + if (!context) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + context->touches.clear(); + context->handle = 0; + return 0; } -int APS5_VABI sceSystemGestureCreateTouchRecognizer(int32_t gesture_handle, SystemGestureTouchRecognizer* recognizer, int32_t type, const SystemGestureRectangle* rectangle, const void* param) { +int APS5_VABI sceSystemGestureCreateTouchRecognizer(int32_t gestureHandle, SystemGestureTouchRecognizer* recognizer, int32_t type, const SystemGestureRectangle* rectangle, const void* param) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); (void)param; - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; RecognizerState state{}; state.magic = RECOGNIZER_STATE_MAGIC; @@ -157,79 +179,93 @@ int APS5_VABI sceSystemGestureCreateTouchRecognizer(int32_t gesture_handle, Syst int APS5_VABI sceSystemGestureFinalizePrimitiveTouchRecognizer(void) { std::lock_guard lock(primitiveTouchMutex); - primitiveTouches.clear(); + gestureInitialized = false; return 0; } int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByIndex(int32_t gestureHandle, uint32_t index, SystemGesturePrimitiveTouchEvent* event) { - if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; - if (gestureHandle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; std::lock_guard lock(primitiveTouchMutex); - if (index >= primitiveTouches.size()) return SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY; - *event = buildPrimitiveTouchEvent(primitiveTouches[index]); + if (!gestureInitialized) return static_cast(0x80890003); + if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + if (index >= findGestureContext(gestureHandle)->touches.size()) return SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY; + *event = buildPrimitiveTouchEvent(findGestureContext(gestureHandle)->touches[index]); return 0; } int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(int32_t gestureHandle, uint16_t primitiveId, SystemGesturePrimitiveTouchEvent* event) { - if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; - if (gestureHandle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; std::lock_guard lock(primitiveTouchMutex); - const auto tracked = std::find_if(primitiveTouches.begin(), primitiveTouches.end(), + if (!gestureInitialized) return static_cast(0x80890003); + if (!event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + const auto tracked = std::find_if(findGestureContext(gestureHandle)->touches.begin(), findGestureContext(gestureHandle)->touches.end(), [&](const TrackedTouch& touch) { return touch.primitiveId == primitiveId; }); - if (tracked == primitiveTouches.end()) return static_cast(0x80890005); + if (tracked == findGestureContext(gestureHandle)->touches.end()) return static_cast(0x80890005); *event = buildPrimitiveTouchEvent(*tracked); return 0; } int APS5_VABI sceSystemGestureGetPrimitiveTouchEvents(int32_t gestureHandle, SystemGesturePrimitiveTouchEvent* eventBuffer, uint32_t capacityOfBuffer, uint32_t* numberOfEvent) { - if (!eventBuffer || !capacityOfBuffer || !numberOfEvent) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; - if (gestureHandle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; std::lock_guard lock(primitiveTouchMutex); - const std::uint32_t reported = static_cast(std::min(primitiveTouches.size(), capacityOfBuffer)); - for (std::uint32_t i = 0; i < reported; ++i) eventBuffer[i] = buildPrimitiveTouchEvent(primitiveTouches[i]); + if (!gestureInitialized) return static_cast(0x80890003); + if (!eventBuffer || !capacityOfBuffer || !numberOfEvent) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + const std::uint32_t reported = static_cast(std::min(findGestureContext(gestureHandle)->touches.size(), capacityOfBuffer)); + for (std::uint32_t i = 0; i < reported; ++i) eventBuffer[i] = buildPrimitiveTouchEvent(findGestureContext(gestureHandle)->touches[i]); *numberOfEvent = reported; return 0; } -int APS5_VABI sceSystemGestureGetPrimitiveTouchEventsCount(int32_t gesture_handle) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureGetPrimitiveTouchEventsCount(int32_t gestureHandle) { std::lock_guard lock(primitiveTouchMutex); - return static_cast(primitiveTouches.size()); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + return static_cast(findGestureContext(gestureHandle)->touches.size()); } -int APS5_VABI sceSystemGestureGetTouchEventByEventID(int32_t gesture_handle, const SystemGestureTouchRecognizer* recognizer, uint32_t eventId, SystemGestureTouchEvent* event) { +int APS5_VABI sceSystemGestureGetTouchEventByEventID(int32_t gestureHandle, const SystemGestureTouchRecognizer* recognizer, uint32_t eventId, SystemGestureTouchEvent* event) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); (void)eventId; (void)event; - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; return SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY; } -int APS5_VABI sceSystemGestureGetTouchEventByIndex(int32_t gesture_handle, const SystemGestureTouchRecognizer* recognizer, uint32_t index, SystemGestureTouchEvent* event) { +int APS5_VABI sceSystemGestureGetTouchEventByIndex(int32_t gestureHandle, const SystemGestureTouchRecognizer* recognizer, uint32_t index, SystemGestureTouchEvent* event) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); (void)index; (void)event; - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; return SCE_SYSTEM_GESTURE_ERROR_INDEX_OUT_OF_ARRAY; } -int APS5_VABI sceSystemGestureGetTouchEvents(int32_t gesture_handle, const SystemGestureTouchRecognizer* recognizer, SystemGestureTouchEvent* event_buffer, uint32_t capacity_of_buffer, uint32_t* number_of_event) { +int APS5_VABI sceSystemGestureGetTouchEvents(int32_t gestureHandle, const SystemGestureTouchRecognizer* recognizer, SystemGestureTouchEvent* eventBuffer, uint32_t capacityOfBuffer, uint32_t* numberOfEvent) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); (void)recognizer; - (void)event_buffer; - (void)capacity_of_buffer; - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; - if (!number_of_event) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; - *number_of_event = 0; + (void)eventBuffer; + (void)capacityOfBuffer; + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + if (!numberOfEvent) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + *numberOfEvent = 0; return 0; } -int APS5_VABI sceSystemGestureGetTouchEventsCount(int32_t gesture_handle, const SystemGestureTouchRecognizer* recognizer) { +int APS5_VABI sceSystemGestureGetTouchEventsCount(int32_t gestureHandle, const SystemGestureTouchRecognizer* recognizer) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); (void)recognizer; - return gesture_handle == GESTURE_HANDLE ? 0 : SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + return findGestureContext(gestureHandle) != nullptr ? 0 : SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; } -int APS5_VABI sceSystemGestureGetTouchRecognizerInformation(int32_t gesture_handle, const SystemGestureTouchRecognizer* recognizer, SystemGestureTouchRecognizerInformation* information) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureGetTouchRecognizerInformation(int32_t gestureHandle, const SystemGestureTouchRecognizer* recognizer, SystemGestureTouchRecognizerInformation* information) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer || !information) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; RecognizerState state{}; if (!loadRecognizerState(recognizer, state)) @@ -242,61 +278,82 @@ int APS5_VABI sceSystemGestureGetTouchRecognizerInformation(int32_t gesture_hand return 0; } -int APS5_VABI sceSystemGestureInitializePrimitiveTouchRecognizer(const void* param) { - (void)param; +int APS5_VABI sceSystemGestureInitializePrimitiveTouchRecognizer() { std::lock_guard lock(primitiveTouchMutex); - primitiveTouches.clear(); + gestureInitialized = true; return 0; } -int32_t APS5_VABI sceSystemGestureOpen(int32_t input_type, const void* param) { - (void)input_type; - (void)param; - return GESTURE_HANDLE; +int32_t APS5_VABI sceSystemGestureOpen(int32_t inputType, const void* param) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (inputType != 0 || param) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + for (std::size_t index = 0; index < gestureContexts.size(); ++index) { + auto& context = gestureContexts[index]; + if (context.handle != 0) continue; + ++gestureGeneration; + if (gestureGeneration == 0) gestureGeneration = 1; + context.touches.clear(); + context.handle = static_cast(1191182336u + (static_cast(gestureGeneration) << 8) + index); + return context.handle; + } + return static_cast(0x8089000A); } -int APS5_VABI sceSystemGestureRemoveTouchRecognizer(int32_t gesture_handle, SystemGestureTouchRecognizer* recognizer) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureRemoveTouchRecognizer(int32_t gestureHandle, SystemGestureTouchRecognizer* recognizer) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; return 0; } -int APS5_VABI sceSystemGestureResetPrimitiveTouchRecognizer(int32_t gesture_handle) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureResetPrimitiveTouchRecognizer(int32_t gestureHandle) { std::lock_guard lock(primitiveTouchMutex); - primitiveTouches.clear(); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + findGestureContext(gestureHandle)->touches.clear(); return 0; } -int APS5_VABI sceSystemGestureResetTouchRecognizer(int32_t gesture_handle, SystemGestureTouchRecognizer* recognizer) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureResetTouchRecognizer(int32_t gestureHandle, SystemGestureTouchRecognizer* recognizer) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; return 0; } -int APS5_VABI sceSystemGestureUpdateAllTouchRecognizer(int32_t gesture_handle) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureUpdateAllTouchRecognizer(int32_t gestureHandle) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; return 0; } -int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(int32_t gesture_handle, const void* param) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(int32_t gestureHandle, const void* param) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!param) return 0; const auto* input = static_cast(param); if (!input->pad_data_buffer || input->report_number <= 0) return 0; - std::lock_guard lock(primitiveTouchMutex); - for (std::int32_t i = 0; i < input->report_number; ++i) sampleReport(input->pad_data_buffer[i]); + for (std::int32_t i = 0; i < input->report_number; ++i) sampleReport(findGestureContext(gestureHandle)->touches, input->pad_data_buffer[i]); return 0; } -int APS5_VABI sceSystemGestureUpdateTouchRecognizer(int32_t gesture_handle, SystemGestureTouchRecognizer* recognizer) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureUpdateTouchRecognizer(int32_t gestureHandle, SystemGestureTouchRecognizer* recognizer) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; return 0; } -int APS5_VABI sceSystemGestureUpdateTouchRecognizerRectangle(int32_t gesture_handle, SystemGestureTouchRecognizer* recognizer, const SystemGestureRectangle* rectangle) { - if (gesture_handle != GESTURE_HANDLE) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; +int APS5_VABI sceSystemGestureUpdateTouchRecognizerRectangle(int32_t gestureHandle, SystemGestureTouchRecognizer* recognizer, const SystemGestureRectangle* rectangle) { + std::lock_guard lock(primitiveTouchMutex); + if (!gestureInitialized) return static_cast(0x80890003); + if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; if (!recognizer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; if (!rectangle) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; RecognizerState state{}; diff --git a/core/libs/tests/GuestSystemGesture.cpp b/core/libs/tests/GuestSystemGesture.cpp index 8444279cc7..db06160408 100644 --- a/core/libs/tests/GuestSystemGesture.cpp +++ b/core/libs/tests/GuestSystemGesture.cpp @@ -10,6 +10,7 @@ #include extern "C" { +int APS5_VABI sceSystemGestureClose(std::int32_t); std::int32_t APS5_VABI sceSystemGestureOpen(std::int32_t, const void*); int APS5_VABI sceSystemGestureGetPrimitiveTouchEvents(std::int32_t, void*, std::uint32_t, std::uint32_t*); int APS5_VABI sceSystemGestureGetPrimitiveTouchEventsCount(std::int32_t); @@ -27,7 +28,7 @@ int APS5_VABI sceSystemGestureUpdateTouchRecognizerRectangle(std::int32_t, void* int APS5_VABI sceSystemGestureResetPrimitiveTouchRecognizer(std::int32_t); int APS5_VABI sceSystemGestureUpdateAllTouchRecognizer(std::int32_t); int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(std::int32_t, const void*); -int APS5_VABI sceSystemGestureInitializePrimitiveTouchRecognizer(const void*); +int APS5_VABI sceSystemGestureInitializePrimitiveTouchRecognizer(); int APS5_VABI sceSystemGestureFinalizePrimitiveTouchRecognizer(void); int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(std::int32_t, std::uint16_t, void*); } @@ -118,7 +119,7 @@ static SystemGesturePrimitiveTouchEvent SingleEvent(std::int32_t handle) { } static void TestPrimitiveTouchEvents(std::int32_t handle, int invalidArgument, int invalidHandle, int indexOutOfArray) { - Require(sceSystemGestureInitializePrimitiveTouchRecognizer(nullptr) == 0); + Require(sceSystemGestureInitializePrimitiveTouchRecognizer() == 0); const PadData pressed = TouchReport(1000, {{7, 480, 471}}); Require(UpdateWithReports(handle, &pressed, 1) == 0); @@ -207,12 +208,17 @@ static void TestPrimitiveTouchEvents(std::int32_t handle, int invalidArgument, i Require(UpdateWithReports(handle, &pressed, 1) == 0); Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 1); Require(sceSystemGestureFinalizePrimitiveTouchRecognizer() == 0); - Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 0); + Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == static_cast(0x80890003)); + Require(sceSystemGestureInitializePrimitiveTouchRecognizer() == 0); + Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 1); } int main() { constexpr int invalidArgument = static_cast(0x80890001); constexpr int invalidHandle = static_cast(0x80890006); + Require(sceSystemGestureOpen(0, nullptr) == static_cast(0x80890003)); + Require(sceSystemGestureInitializePrimitiveTouchRecognizer() == 0); + Require(sceSystemGestureOpen(1, nullptr) == invalidArgument); const std::int32_t handle = sceSystemGestureOpen(0, nullptr); std::array buffer; @@ -273,5 +279,22 @@ int main() { TestTouchRecognizerInformation(handle, invalidArgument, invalidHandle); TestPrimitiveTouchEvents(handle, invalidArgument, invalidHandle, indexOutOfArray); + std::array otherHandles{}; + for (auto& otherHandle : otherHandles) { + otherHandle = sceSystemGestureOpen(0, nullptr); + Require(otherHandle > 0 && otherHandle != handle); + Require(sceSystemGestureGetPrimitiveTouchEventsCount(otherHandle) == 0); + } + Require(sceSystemGestureOpen(0, nullptr) == static_cast(0x8089000A)); + Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 1); + Require(sceSystemGestureClose(otherHandles[0]) == 0); + Require(sceSystemGestureClose(otherHandles[0]) == invalidHandle); + const auto replacement = sceSystemGestureOpen(0, nullptr); + Require(replacement > 0 && replacement != otherHandles[0]); + Require(sceSystemGestureGetPrimitiveTouchEventsCount(otherHandles[0]) == invalidHandle); + Require(sceSystemGestureClose(replacement) == 0); + for (std::size_t index = 1; index < otherHandles.size(); ++index) Require(sceSystemGestureClose(otherHandles[index]) == 0); + Require(sceSystemGestureClose(handle) == 0); + Require(sceSystemGestureFinalizePrimitiveTouchRecognizer() == 0); return 0; } From 91c0535cd7c0f8e515fcd8479bcc58fdce7acdeb Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:17:39 +0300 Subject: [PATCH 414/523] fix(systemgesture): validate native pad report input --- core/libs/prx/libSceSystemGesture/Export.cpp | 12 ++++++------ core/libs/tests/GuestSystemGesture.cpp | 13 ++++++++----- docs/dev/debt/libSceSystemGesture.md | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceSystemGesture/Export.cpp b/core/libs/prx/libSceSystemGesture/Export.cpp index 346cd57893..5e1fb249e6 100644 --- a/core/libs/prx/libSceSystemGesture/Export.cpp +++ b/core/libs/prx/libSceSystemGesture/Export.cpp @@ -331,14 +331,14 @@ int APS5_VABI sceSystemGestureUpdateAllTouchRecognizer(int32_t gestureHandle) { return 0; } -int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(int32_t gestureHandle, const void* param) { +int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(int32_t gestureHandle, const SystemGestureTouchPadData* input) { std::lock_guard lock(primitiveTouchMutex); if (!gestureInitialized) return static_cast(0x80890003); - if (!findGestureContext(gestureHandle)) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; - if (!param) return 0; - const auto* input = static_cast(param); - if (!input->pad_data_buffer || input->report_number <= 0) return 0; - for (std::int32_t i = 0; i < input->report_number; ++i) sampleReport(findGestureContext(gestureHandle)->touches, input->pad_data_buffer[i]); + if (!input || !input->pad_data_buffer) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + auto* context = findGestureContext(gestureHandle); + if (!context) return SCE_SYSTEM_GESTURE_ERROR_INVALID_HANDLE; + if (input->report_number < 0 || input->report_number > 1) return SCE_SYSTEM_GESTURE_ERROR_INVALID_ARGUMENT; + if (input->report_number == 1) sampleReport(context->touches, *input->pad_data_buffer); return 0; } diff --git a/core/libs/tests/GuestSystemGesture.cpp b/core/libs/tests/GuestSystemGesture.cpp index db06160408..656f943aca 100644 --- a/core/libs/tests/GuestSystemGesture.cpp +++ b/core/libs/tests/GuestSystemGesture.cpp @@ -27,7 +27,7 @@ int APS5_VABI sceSystemGestureUpdateTouchRecognizer(std::int32_t, void*); int APS5_VABI sceSystemGestureUpdateTouchRecognizerRectangle(std::int32_t, void*, const void*); int APS5_VABI sceSystemGestureResetPrimitiveTouchRecognizer(std::int32_t); int APS5_VABI sceSystemGestureUpdateAllTouchRecognizer(std::int32_t); -int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(std::int32_t, const void*); +int APS5_VABI sceSystemGestureUpdatePrimitiveTouchRecognizer(std::int32_t, const SystemGestureTouchPadData*); int APS5_VABI sceSystemGestureInitializePrimitiveTouchRecognizer(); int APS5_VABI sceSystemGestureFinalizePrimitiveTouchRecognizer(void); int APS5_VABI sceSystemGestureGetPrimitiveTouchEventByPrimitiveID(std::int32_t, std::uint16_t, void*); @@ -191,11 +191,14 @@ static void TestPrimitiveTouchEvents(std::int32_t handle, int invalidArgument, i Require(sceSystemGestureGetPrimitiveTouchEventByIndex(handle, 2, &event) == indexOutOfArray); Require(UpdateWithReports(handle + 1, &pair, 1) == invalidHandle); - Require(sceSystemGestureUpdatePrimitiveTouchRecognizer(handle, nullptr) == 0); + Require(sceSystemGestureUpdatePrimitiveTouchRecognizer(handle, nullptr) == invalidArgument); Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 2); const std::array sequence{TouchReport(7000, {{3, 110, 200}}), TouchReport(8000, {{3, 130, 210}})}; - Require(UpdateWithReports(handle, sequence.data(), 2) == 0); + Require(UpdateWithReports(handle, sequence.data(), 2) == invalidArgument); + Require(UpdateWithReports(handle, sequence.data(), -1) == invalidArgument); + Require(UpdateWithReports(handle, sequence.data(), 1) == 0); + Require(UpdateWithReports(handle, sequence.data() + 1, 1) == 0); Require(sceSystemGestureGetPrimitiveTouchEventsCount(handle) == 1); event = SingleEvent(handle); Require(event.primitive_id == 3); @@ -261,7 +264,7 @@ int main() { Require(sceSystemGestureUpdateTouchRecognizerRectangle(handle, &recognizer, &rectangle) == 0); Require(sceSystemGestureResetPrimitiveTouchRecognizer(handle) == 0); Require(sceSystemGestureUpdateAllTouchRecognizer(handle) == 0); - Require(sceSystemGestureUpdatePrimitiveTouchRecognizer(handle, nullptr) == 0); + Require(sceSystemGestureUpdatePrimitiveTouchRecognizer(handle, nullptr) == invalidArgument); Require(sceSystemGestureAppendTouchRecognizer(handle, nullptr) == invalidArgument); Require(sceSystemGestureUpdateTouchRecognizerRectangle(handle, &recognizer, nullptr) == invalidArgument); Require(sceSystemGestureAppendTouchRecognizer(handle + 1, &recognizer) == invalidHandle); @@ -275,7 +278,7 @@ int main() { Require(sceSystemGestureUpdateTouchRecognizerRectangle(handle + 1, &recognizer, &rectangle) == invalidHandle); Require(sceSystemGestureResetPrimitiveTouchRecognizer(handle + 1) == invalidHandle); Require(sceSystemGestureUpdateAllTouchRecognizer(handle + 1) == invalidHandle); - Require(sceSystemGestureUpdatePrimitiveTouchRecognizer(handle + 1, nullptr) == invalidHandle); + Require(sceSystemGestureUpdatePrimitiveTouchRecognizer(handle + 1, nullptr) == invalidArgument); TestTouchRecognizerInformation(handle, invalidArgument, invalidHandle); TestPrimitiveTouchEvents(handle, invalidArgument, invalidHandle, indexOutOfArray); diff --git a/docs/dev/debt/libSceSystemGesture.md b/docs/dev/debt/libSceSystemGesture.md index eff9ccbe7c..5d9acec690 100644 --- a/docs/dev/debt/libSceSystemGesture.md +++ b/docs/dev/debt/libSceSystemGesture.md @@ -6,5 +6,5 @@ ### Unknown function info -- [Primitive touch events](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - `sceSystemGestureUpdatePrimitiveTouchRecognizer` reads the pad reports the title passes in, `{int32 padHandle, int32 reportNumber, const ScePadData* padDataBuffer, 8 reserved bytes}`, as [fpPS4](https://github.com/red-prig/fpPS4/blob/585d06fa7c04698c41272b95a696a14c4180e13e/src/ps4_libscesystemgesture.pas) records `SceSystemGestureTouchPadData`; `padHandle` is not checked and a report of a pad other than the one the title opened is not told apart. The touch states 0 inactive, 1 begin, 2 active and 3 end are the Vita ones in [RPCS3](https://github.com/RPCS3/rpcs3/blob/38c444cfa1dbe152daa63b1703c12172078cd056/rpcs3/Emu/PSP2/Modules/sceSystemGesture.h), whose recognizer, rectangle and primitive event layouts are the ones main already uses, and no public source states the PS5 values. A primitive keeps the touch id the pad reports, its positions are the pad's touchpad coordinates unscaled, `delta_time` and `elapsed_time` come from the reports' own `timestamp`, and `is_updated` is 1 for a primitive whose state or position changed in the update; the console's units, scaling and `is_updated` rule are unverified. An ended touch stays readable as one `end` event until the next report sampled drops it. A null `pInputData`, a null `padDataBuffer` or a `reportNumber` of 0 or less leaves the events unchanged and returns success, an event buffer shorter than the event count reports as many events as it holds, while native update null-pointer and report-count checks are not yet matched +- [Primitive touch events](../../../core/libs/prx/libSceSystemGesture/Export.cpp): pad handles are not validated against native controller information. Begin, active and end states are confirmed, but cancellation state 4, motion thresholds and SDK-dependent duplicate-report handling remain unimplemented. Timing, coordinate conversion and update flags still use the host approximation. Recognizer allocation and native descriptor layouts remain incomplete. - [sceSystemGestureDebugGetVersion](../../../core/libs/prx/libSceSystemGesture/Export.cpp) (libSceSystemGesture) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSystemGesture.c), but no credible prototype was found. It is declared without parameters solely to resolve known imports; this is not an ABI claim From 25b3a519b0a6dc9cdfe546e81cfc57efe652131c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:20:03 +0300 Subject: [PATCH 415/523] fix(playgo): enforce optional chunk types and query limits --- core/libs/prx/libScePlayGo/Export.cpp | 32 ++++++++++++++------------- core/libs/tests/GuestPlayGo.cpp | 24 ++++++++++++++++---- docs/dev/debt/libScePlayGo.md | 2 +- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/core/libs/prx/libScePlayGo/Export.cpp b/core/libs/prx/libScePlayGo/Export.cpp index 165a767c99..9fae1d940e 100644 --- a/core/libs/prx/libScePlayGo/Export.cpp +++ b/core/libs/prx/libScePlayGo/Export.cpp @@ -131,20 +131,21 @@ int APS5_VABI scePlayGoClose(int handle) { return 0; } -int APS5_VABI scePlayGoGetChunkId(int handle, uint16_t* out_chunk_id_list, uint32_t number_of_entries, uint32_t* out_entries) { +int APS5_VABI scePlayGoGetChunkId(int handle, uint16_t* outChunkIdList, uint32_t numberOfEntries, uint32_t* outEntries) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; - if (!out_entries) return SCE_PLAYGO_ERROR_BAD_POINTER; + if (!outEntries) return SCE_PLAYGO_ERROR_BAD_POINTER; const auto& valid = Chunks().ids; - if (!out_chunk_id_list) { - *out_entries = static_cast(valid.size()); + if (!outChunkIdList) { + *outEntries = static_cast(valid.size()); return 0; } + if (numberOfEntries == 0 || numberOfEntries > 1000) return SCE_PLAYGO_ERROR_BAD_SIZE; uint32_t written = 0; for (const auto id : valid) { - if (written == number_of_entries) break; - out_chunk_id_list[written++] = id; + if (written == numberOfEntries) break; + outChunkIdList[written++] = id; } - *out_entries = written; + *outEntries = written; return 0; } @@ -174,18 +175,19 @@ int APS5_VABI scePlayGoGetLanguageMask(int handle, uint64_t* out_language_mask) return 0; } -int APS5_VABI scePlayGoGetLocus(int handle, const uint16_t* chunk_ids, uint32_t number_of_entries, int8_t* out_loci) { +int APS5_VABI scePlayGoGetLocus(int handle, const uint16_t* chunkIds, uint32_t numberOfEntries, int8_t* outLoci) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; - if (!out_loci) return SCE_PLAYGO_ERROR_BAD_POINTER; - if (const int error = ValidateChunks(chunk_ids, number_of_entries)) return error; - for (uint32_t index = 0; index < number_of_entries; ++index) out_loci[index] = PLAYGO_LOCUS_LOCAL_FAST; + if (!outLoci) return SCE_PLAYGO_ERROR_BAD_POINTER; + if (chunkIds && numberOfEntries > 1000) return SCE_PLAYGO_ERROR_BAD_SIZE; + if (const int error = ValidateChunks(chunkIds, numberOfEntries)) return error; + for (uint32_t index = 0; index < numberOfEntries; ++index) outLoci[index] = PLAYGO_LOCUS_LOCAL_FAST; return 0; } -int APS5_VABI scePlayGoGetOptionalChunk(int handle, int32_t type, PlayGoOptionalChunk* option) { +int APS5_VABI scePlayGoGetOptionalChunk(int handle, std::uint32_t type, PlayGoOptionalChunk* option) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; - (void)type; if (!option) return SCE_PLAYGO_ERROR_BAD_POINTER; + if (type > 2) return static_cast(0x80B20024); option->bitmask = PLAYGO_OPTIONAL_CHUNK_MASK_ALL; return 0; } @@ -199,10 +201,10 @@ int APS5_VABI scePlayGoGetProgress(int handle, const uint16_t* chunk_ids, uint32 return 0; } -int APS5_VABI scePlayGoGetSupportedOptionalChunk(int handle, int32_t type, PlayGoOptionalChunk* option) { +int APS5_VABI scePlayGoGetSupportedOptionalChunk(int handle, std::uint32_t type, PlayGoOptionalChunk* option) { if (handle != PLAYGO_HANDLE) return SCE_PLAYGO_ERROR_BAD_HANDLE; - (void)type; if (!option) return SCE_PLAYGO_ERROR_BAD_POINTER; + if (type > 2) return static_cast(0x80B20024); option->bitmask = PLAYGO_OPTIONAL_CHUNK_MASK_ALL; return 0; } diff --git a/core/libs/tests/GuestPlayGo.cpp b/core/libs/tests/GuestPlayGo.cpp index 918e6ad031..c03f4761ca 100644 --- a/core/libs/tests/GuestPlayGo.cpp +++ b/core/libs/tests/GuestPlayGo.cpp @@ -2,6 +2,8 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include +#include #include #include #include @@ -18,15 +20,19 @@ int APS5_VABI scePlayGoOpen(int*, const void*); int APS5_VABI scePlayGoClose(int); int APS5_VABI scePlayGoGetLanguageMask(int, std::uint64_t*); int APS5_VABI scePlayGoSetLanguageMask(int, std::uint64_t); -int APS5_VABI scePlayGoGetSupportedOptionalChunk(int, std::int32_t, PlayGoOptionalChunk*); -int APS5_VABI scePlayGoGetOptionalChunk(int, std::int32_t, PlayGoOptionalChunk*); +int APS5_VABI scePlayGoGetSupportedOptionalChunk(int, std::uint32_t, PlayGoOptionalChunk*); +int APS5_VABI scePlayGoGetOptionalChunk(int, std::uint32_t, PlayGoOptionalChunk*); int APS5_VABI scePlayGoSetToDoList(int, const PlayGoToDo*, std::uint32_t); int APS5_VABI scePlayGoGetToDoList(int, PlayGoToDo*, std::uint32_t, std::uint32_t*); int APS5_VABI scePlayGoGetLocus(int, const std::uint16_t*, std::uint32_t, std::int8_t*); int APS5_VABI scePlayGoGetChunkId(int, std::uint16_t*, std::uint32_t, std::uint32_t*); } -static void Require(bool value) { if (!value) std::abort(); } +static void Require(bool value, const std::source_location location = std::source_location::current()) { + if (value) return; + std::fprintf(stderr, "requirement failed at line %u\n", location.line()); + std::abort(); +} template static void RequireMissingChunkMetadata(Operation&& operation) { @@ -75,8 +81,9 @@ static int CheckScenarioWithoutChunkTable(const char* root) { std::uint32_t chunkCount = 0; Require(scePlayGoGetChunkId(handle, nullptr, 0, &chunkCount) == 0 && chunkCount == 1000); std::vector ids(chunkCount + 1, 0xFFFF); - Require(scePlayGoGetChunkId(handle, ids.data(), static_cast(ids.size()), &chunkCount) == 0 && chunkCount == 1000); + Require(scePlayGoGetChunkId(handle, ids.data(), chunkCount, &chunkCount) == 0 && chunkCount == 1000); for (std::uint32_t index = 0; index < chunkCount; ++index) Require(ids[index] == index); + Require(ids.back() == 0xFFFF); std::vector listedLoci(chunkCount, 0); Require(scePlayGoGetLocus(handle, ids.data(), chunkCount, listedLoci.data()) == 0); for (const auto locus : listedLoci) Require(locus == 3); @@ -116,6 +123,9 @@ static int CheckNoPlayGoFile(const char* root) { Require(getOptionalChunk(handle + 1, 0, &option) == badHandle); Require(getOptionalChunk(handle, 0, nullptr) == badPointer); Require(getOptionalChunk(handle, 0, &option) == 0 && option.bitmask == ~0ull); + Require(getOptionalChunk(handle, 3, &option) == static_cast(0x80B20024)); + Require(getOptionalChunk(handle, 0xFFFFFFFF, &option) == static_cast(0x80B20024)); + Require(getOptionalChunk(handle, 3, nullptr) == badPointer); } PlayGoToDo todo[2] = {{0, 3, 0}, {0, 0, 0}}; @@ -135,6 +145,12 @@ static int CheckNoPlayGoFile(const char* root) { const std::uint16_t chunk = 0; std::int8_t locus = 0; Require(scePlayGoGetLocus(handle, &chunk, 0, &locus) == badSize); + Require(scePlayGoGetLocus(handle, &chunk, 1001, &locus) == badSize); + std::uint32_t invalidListCount = 42; + std::uint16_t invalidListOutput = 0; + Require(scePlayGoGetChunkId(handle, &invalidListOutput, 0, &invalidListCount) == badSize); + Require(scePlayGoGetChunkId(handle, &invalidListOutput, 1001, &invalidListCount) == badSize); + Require(invalidListCount == 42); Require(scePlayGoGetLocus(handle, &chunk, 1, &locus) == 0 && locus == 3); const std::uint16_t undeclared[2] = {1, 10}; std::int8_t loci[2] = {0, 0}; diff --git a/docs/dev/debt/libScePlayGo.md b/docs/dev/debt/libScePlayGo.md index cfd6b6de0d..40a6135747 100644 --- a/docs/dev/debt/libScePlayGo.md +++ b/docs/dev/debt/libScePlayGo.md @@ -5,4 +5,4 @@ - [scePlayGoRequestNextChunk, scePlayGoSnapshot](../../../core/libs/prx/libScePlayGo/Export.cpp) (libScePlayGo) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libScePlayGo.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim - [scePlayGoSetLanguageMask](../../../core/libs/prx/libScePlayGo/Export.cpp) (libScePlayGo) - stores the mask that `scePlayGoGetLanguageMask` then reports, and returns `SCE_PLAYGO_ERROR_BAD_HANDLE` for another handle, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/8d50471b7dc141dd700b51276b8edd1f293bce26/src/core/libraries/playgo/playgo.cpp#L346-L359) does on the PS4. shadPS4 also returns `SCE_PLAYGO_ERROR_NOT_INITIALIZED` before `scePlayGoInitialize` and starts from the system language's mask; here the mask starts with every language and initialization is not tracked. Not measured on a PS5 - [libScePlayGo](../../../core/libs/prx/libScePlayGo/Export.cpp) - the chunk set comes from `playgo-chunkdefs.xml` (every listed chunk plus chunks 0 through the default chunk), otherwise from `sce_sys/playgo-chunk.dat`, whose `plgx` header is read as holding the chunk count as a 16-bit value at offset 0x0A and ids 0 to count - 1 are taken as valid. The field was identified on four dumps, where exactly that many 0x20-byte entries follow from offset 0x100 (RIDE 4 PPSA01599: 35, Persona 5 Royal PPSA05110: 25, LEGO Horizon Adventures PPSA14632 and Kena PPSA01802: 1); the rest of the format is not decoded and ids are assumed to be contiguous. Every listed chunk is reported as installed (`LOCAL_FAST`). Without either file, a package with `sce_sys/playgo-scenario.json` (PPSA01748, whose dump lists no chunks) accepts every chunk id as installed, and `scePlayGoGetChunkId` lists ids 0 to 999. A title that builds its set of installed chunks from that list skips any chunk it does not see. The console's chunk limit is not known here, and a package with more chunks would see ids from 1000 up as not listed. A package with no `playgo-*` file in `sce_sys` (PPSA03001, PPSA03490 and PPSA01285 here) accepts chunk 0; querying any other id throws with a message naming the missing `playgo-chunk.dat` and `playgo-scenario.json`, since the incomplete dump cannot establish whether that id exists; `APS5_PLAYGO_UNLISTED_CHUNKS=installed` answers every such id as installed (`LOCAL_FAST`, which PPSA15388 needs to load its saves) and `=absent` answers `SCE_PLAYGO_ERROR_BAD_CHUNK_ID` (which ends PPSA03001's id walk), as the user's choice for that dump. A `sce_sys` that holds other `playgo-*` files (`playgo-ficm.dat`, `playgo-hash-table.dat`) without `playgo-chunk.dat` or `playgo-scenario.json`, or a `playgo-chunk.dat` without a `plgx` header and a nonzero count, throws naming the file, because no such dump is known. These answers are unverified on a console, as is the console's answer for an id outside a package's chunk table. -- [libScePlayGo](../../../core/libs/prx/libScePlayGo/Export.cpp) - `scePlayGoGetSupportedOptionalChunk` and `scePlayGoGetOptionalChunk` report every bit set for any `type`, i.e. every optional chunk (language pack) supported and installed, like `scePlayGoGetLanguageMask`. The bit layout and the values of `type` are not known; PPSA01748 calls both with type 0 and hides every text language listed in `playgo-scenario.json` `chunkSupportedLanguages` when the bitmask is 0. The console's answer for a package that ships no optional chunks is unverified. +- [libScePlayGo](../../../core/libs/prx/libScePlayGo/Export.cpp) - `scePlayGoGetSupportedOptionalChunk` and `scePlayGoGetOptionalChunk` report every bit set for `type` 0, 1 or 2, i.e. every optional chunk (language pack) supported and installed, like `scePlayGoGetLanguageMask`. Native requests confirm types 0 through 2 and an eight-byte result; their meanings and bit layout are still determined by the external service, not this module; PPSA01748 calls both with type 0 and hides every text language listed in `playgo-scenario.json` `chunkSupportedLanguages` when the bitmask is 0. The console's answer for a package that ships no optional chunks is unverified. From f2224eae0c689fcb43948518ac6c4dc6fcd390c3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:23:18 +0300 Subject: [PATCH 416/523] docs(netctl): narrow unresolved aprp signature evidence --- docs/dev/debt/libSceNetCtl.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/dev/debt/libSceNetCtl.md b/docs/dev/debt/libSceNetCtl.md index 5e377b9392..2e936ee5bc 100644 --- a/docs/dev/debt/libSceNetCtl.md +++ b/docs/dev/debt/libSceNetCtl.md @@ -11,17 +11,7 @@ - [sceNetCtlGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - answers from the host interface that holds an IPv4 address, preferring the one that carries the default route: its address, netmask, gateway, MTU and first two IPv4 DNS servers. The device is reported as wired, the link as connected, the IP configuration as DHCP and the HTTP proxy as off, whatever the host uses, and the Ethernet address is the one `sceNetGetMacAddress` reports. A gateway or DNS server the interface does not have is reported as `0.0.0.0`; what a console reports then is unknown. The DHCP host name (12), the PPPoE user name (13), the HTTP proxy server (20) and the Wi-Fi BSSID and SSID (5, 6) are empty strings, as that configuration sets none of them; the remaining Wi-Fi codes (7-10) and the HTTP proxy port (21) report 0; this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/network.cpp) and is not measured on hardware - [sceNetCtlGetInfoV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `Jy1EO5GdlcM`) - imported by PPSA08587. `(int code, SceNetCtlInfoV6* info)` with codes 1-5 (IP address, link-local address, default route, primary and secondary DNS) comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero), whose source is unknown; shadPS4 and fpPS4 only stub it. A null `info` answers `INVALID_ADDR`, a code outside 1-5 `INVALID_CODE`, and a host without an IPv6 address `NOT_CONNECTED`, as `sceNetCtlGetStateV6` reports it. The layout of each answer is unverified, so every code throws on a host with an IPv6 address - [sceNetCtlGetResultV6](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl, NID `H5yARg37U5g`) - `(int eventType, int* errorCode)` comes only from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/NetCtl.cs#L246-L251), whose source is unknown; shadPS4 only stubs it. Callbacks registered with `sceNetCtlRegisterCallbackV6` receive no event, so event types 1-3 write 0 to `errorCode`; what a console writes before the first event is unmeasured. A null `errorCode` answers `INVALID_ADDR`, as `sceNetCtlGetResult` does, and other event types throw, as their error code on a console is unknown -- [sceNetCtlApRpCheckCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpClearEvent](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpRegisterCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpUnregisterCallback](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpGetResult](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpGetState](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpGetInfo](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpStart](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpStartWithRetry](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpStartConf](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature -- [sceNetCtlApRpStop](../../../core/libs/prx/libSceNetCtl/Export.cpp) (libSceNetCtl) - unknown signature +- [ApRp exports](../../../core/libs/prx/libSceNetCtl/Export.cpp) are constant-success functions in the supplied native module and consume no arguments. No callers were found in the supplied system-library decompilations, so unused callback, output and configuration argument types remain unresolved. Protected stub bodies are unchanged. ### Functional From d9c2facdb9f3ae64073066e16dd9d7c067893c30 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:29:25 +0300 Subject: [PATCH 417/523] fix(ampr): preserve nop payload when data is absent --- core/libs/prx/libSceAmpr/Export.cpp | 2 -- core/libs/tests/GuestAmpr.cpp | 4 ++++ docs/dev/debt/libSceAmpr.md | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAmpr/Export.cpp b/core/libs/prx/libSceAmpr/Export.cpp index 6e55ecbd7b..fe55109c4c 100644 --- a/core/libs/prx/libSceAmpr/Export.cpp +++ b/core/libs/prx/libSceAmpr/Export.cpp @@ -96,7 +96,6 @@ static int AppendNop(Apr::CommandBufferObject* buffer, std::uint32_t dwords, con if (bytes > RemainingCommandBytes(*buffer)) return SCE_KERNEL_ERROR_EBUSY; const Apr::CommandHeader header{Apr::Opcode::Nop, static_cast(bytes)}; std::uint8_t* destination = buffer->base + buffer->offset; - std::memset(destination, 0, bytes); std::memcpy(destination, &header, sizeof(header)); if (data) std::memcpy(destination + sizeof(header), data, std::uint64_t{dwords} * 4u); buffer->offset += static_cast(bytes); @@ -432,7 +431,6 @@ int APS5_VABI sceAmprCommandBufferNop(Apr::CommandBufferObject* buffer, std::uin int APS5_VABI sceAmprCommandBufferNopWithData(Apr::CommandBufferObject* buffer, std::uint32_t dwords, const std::uint32_t* data) { if (dwords > 15u) return SCE_KERNEL_ERROR_EINVAL; - if (dwords != 0u && !data) throw std::invalid_argument("sceAmprCommandBufferNopWithData: null data"); return AppendNop(buffer, dwords, data); } diff --git a/core/libs/tests/GuestAmpr.cpp b/core/libs/tests/GuestAmpr.cpp index ca17fc2c23..f6017f152f 100644 --- a/core/libs/tests/GuestAmpr.cpp +++ b/core/libs/tests/GuestAmpr.cpp @@ -470,6 +470,10 @@ void TestNops() { RequireAppended(recorder, offset, commands, Apr::Opcode::Nop, sceAmprMeasureCommandSizeNopWithData(4)); Require(std::memcmp(recorder.memory.data() + offset + sizeof(Apr::CommandHeader), data, sizeof(data)) == 0); Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 0, nullptr) == 0); + const auto emptyPayloadOffset = recorder.Offset(); + std::memset(recorder.memory.data() + emptyPayloadOffset, 0x5A, sceAmprMeasureCommandSizeNopWithData(4)); + Require(sceAmprCommandBufferNopWithData(&recorder.buffer, 3, nullptr) == 0); + for (std::size_t index = 0; index < 12; ++index) Require(recorder.memory[emptyPayloadOffset + sizeof(Apr::CommandHeader) + index] == 0x5A); Require(sceKernelAprSubmitCommandBuffer(&recorder.buffer, 0) == 0); const auto rejected = static_cast(static_cast(invalidArgument)); diff --git a/docs/dev/debt/libSceAmpr.md b/docs/dev/debt/libSceAmpr.md index 764ea7ec20..ae062de3f3 100644 --- a/docs/dev/debt/libSceAmpr.md +++ b/docs/dev/debt/libSceAmpr.md @@ -7,7 +7,7 @@ - [AMPR command buffer offsets](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - recording with a current offset greater than the bound memory size throws before calculating remaining capacity or writing a command. This validates the emulator's own command-buffer encoding; the console's response to a corrupted opaque buffer object is unknown. - [AMPR command sizes](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - commands use the emulator's own encoding with an 8-byte header, so `Nop(n)` takes 8 + 4n bytes where the console record is n dwords including its header (`NopWithData(n)`: (n + 1) dwords). The measure functions agree with the appended sizes, but a title that pads to an exact offset or the end of the buffer with `Nop(remaining / 4)` gets `SCE_KERNEL_ERROR_EBUSY` or a shifted offset. The at-start flag of the `_04_00` writes is ignored; commands run in order, each after the previous one completes - [APR map begin and end](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - as in ampr_emu, `MapBegin`/`MapDirectBegin` map when they run and `MapEnd` only closes the region; while a region is open a second begin and every completion write return `EPERM`, the at-start `_04_00` writes are accepted -- [AMPR markers](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - signatures taken from the [ampr_emu](https://github.com/drakmor/ampr_emu) reimplementation, not confirmed on a title: `sceAmprCommandBufferSetMarkerWithColor` takes the color by pointer, the push and measure variants by value. Colors are not recorded +- [AMPR markers](../../../core/libs/prx/libSceAmpr/Export.cpp): colors are not recorded and command sizes use the host encoding. Native signatures confirm that set takes a color pointer and push/measure take the color value. - [__cxa_call_unexpected](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - takes the in-flight exception's unwind header and never returns, as [libcxxabi 18.1.8](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_personality.cpp#L1171-L1211) does. No `std::set_unexpected` is exported and the exception header's unexpected handler is never set, so the handler is always the default one, which calls `std::terminate` ([cxa_default_handlers.cpp](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_default_handlers.cpp#L88-L99)); it therefore forwards to libc's `__cxa_call_terminate`, which begins the catch and runs the current terminate handler. That matches libcxxabi except for which terminate handler runs: there `std::terminate` after `__cxa_begin_catch` ([cxa_exception.cpp L470-L477](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_exception.cpp#L470-L477)) runs the handler saved in the caught exception's header ([cxa_handlers.cpp L72-L93](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_handlers.cpp#L72-L93)), which `__cxa_throw` and `__cxa_init_primary_exception` set to `std::get_terminate()` when the exception is thrown ([cxa_exception.cpp L209-L214](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_exception.cpp#L209-L214), [L279](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_exception.cpp#L279)), while here the current global handler runs, as AnyPS5's own `std::terminate` does. The two differ only if `std::set_terminate` was called between the throw and this call. A null header terminates because this emulator's libc `__cxa_begin_catch` terminates on null (libcxxabi does not define that case). Dynamic exception specifications that would let a replaced unexpected handler throw a permitted type are not modelled ### Functional From 808e9d9da21f79d4f33d0bab570fc40b2676756a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:29:27 +0300 Subject: [PATCH 418/523] fix(vrtracker2): restore legacy input pointer contracts --- core/libs/prx/libSceVrTracker2/Export.cpp | 33 ++++++++++++++++++++--- docs/dev/debt/libSceVrTracker2.md | 2 ++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceVrTracker2/Export.cpp b/core/libs/prx/libSceVrTracker2/Export.cpp index db5458a308..2e521f452e 100644 --- a/core/libs/prx/libSceVrTracker2/Export.cpp +++ b/core/libs/prx/libSceVrTracker2/Export.cpp @@ -41,6 +41,21 @@ static_assert(offsetof(VrTracker2InitializeParameter, coordinateSystem) == 52); static_assert(offsetof(VrTracker2InitializeParameter, reserved) == 58); static_assert(sizeof(VrTracker2MemoryParameter) == 32); static_assert(sizeof(VrTracker2MemoryRequirement) == 32); +struct VrTracker2LegacyParameter12 { + std::uint32_t size; + std::uint8_t opaque[8]; +}; + +struct VrTracker2LegacyParameter20 { + std::uint32_t size; + std::uint8_t opaque[16]; +}; + +struct VrTracker2UnsupportedCoordinateParameter; + +static_assert(sizeof(VrTracker2LegacyParameter12) == 12); +static_assert(sizeof(VrTracker2LegacyParameter20) == 20); + struct VrTracker2CalibrationResult; struct VrTracker2TelemetryData; struct VrTracker2PoseParameter; @@ -408,12 +423,24 @@ std::int32_t APS5_VABI VrTracker2GetScalarResult(VrTracker2ScalarResult* result) return VR_TRACKER2_ERROR_NOT_INITIALIZED; } -APS5_EXPORT("omJ7b8FqAN0", VrTracker2NotInitialized); -APS5_EXPORT("G2PWj1HLqn8", VrTracker2NotInitialized); +APS5_EXPORT("omJ7b8FqAN0", VrTracker2LegacyQuery12); +std::int32_t APS5_VABI VrTracker2LegacyQuery12(const VrTracker2LegacyParameter12* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + +APS5_EXPORT("G2PWj1HLqn8", VrTracker2LegacyQuery20); +std::int32_t APS5_VABI VrTracker2LegacyQuery20(const VrTracker2LegacyParameter20* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("73zcxAFawYk", VrTracker2NotInitialized); APS5_EXPORT("vegrWFNiQwc", VrTracker2NotInitialized); APS5_EXPORT("6r01IawmypA", VrTracker2NotInitialized); -APS5_EXPORT("zeof7sF2fqE", VrTracker2NotInitialized); +APS5_EXPORT("zeof7sF2fqE", VrTracker2UnsupportedCoordinateOperation); +std::int32_t APS5_VABI VrTracker2UnsupportedCoordinateOperation(const VrTracker2UnsupportedCoordinateParameter* parameter) { + return VR_TRACKER2_ERROR_NOT_INITIALIZED; +} + APS5_EXPORT("Z0rfHOHQV1w", VrTracker2NotInitialized); APS5_EXPORT("4l7x+uy3dWw", VrTracker2NotInitialized); APS5_EXPORT("ZXlt-Xb3da4", VrTracker2NotInitialized); diff --git a/docs/dev/debt/libSceVrTracker2.md b/docs/dev/debt/libSceVrTracker2.md index 8e138fc9db..29b19cc61c 100644 --- a/docs/dev/debt/libSceVrTracker2.md +++ b/docs/dev/debt/libSceVrTracker2.md @@ -24,3 +24,5 @@ - [Extended result and boundary fitting](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. The extended result spans 816 bytes; its coordinate-specific parameter prefixes the 48-byte query with a coordinate selector and four-byte padding. Boundary fitting takes the recovered point list, optionally returns two 80-byte bounds, and writes four byte flags plus four floats; flag and dimension semantics remain unresolved. The device-center output is a 16-byte size/three-float record. - [Mesh and image-pose utilities](../../../core/libs/prx/libSceVrTracker2/Export.cpp) retain uninitialized responses. Mesh generation takes vertex and 16-bit index outputs plus a 32-bit format. Timed-pose queries take an in/out result, timestamp and user tag. Image submission consumes a record with format at byte 16 and a data pointer at byte 80; image-pose retrieval uses an in/out record with a buffer pointer and capacity. Record fields and initialized algorithms remain incomplete. Scalar queries use float outputs; the map-scalar query also consumes map data. + +- [Legacy unsupported operations](../../../core/libs/prx/libSceVrTracker2/Export.cpp) have separate input-pointer signatures. Two consume only the size prefix of 12/20-byte records, leaving their other fields unrecoverable from these bodies. The coordinate operation reads a selector at byte 4 but always returns unsupported after initialization; the remaining record layout is unresolved. From ffbb7aabbe6645890bc0112fd96401036bfa87d3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:29:30 +0300 Subject: [PATCH 419/523] fix(ime): restore configuration argument slots --- core/libs/prx/libSceIme/Export.cpp | 4 ++-- docs/dev/debt/libSceIme.md | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceIme/Export.cpp b/core/libs/prx/libSceIme/Export.cpp index a67cd80b39..9485063034 100644 --- a/core/libs/prx/libSceIme/Export.cpp +++ b/core/libs/prx/libSceIme/Export.cpp @@ -779,12 +779,12 @@ int APS5_VABI sceImeVshNotifyPreeditMetaInfo(ImeVshContext* context, const ImePr return 0; } -int APS5_VABI sceImeConfigSet(void) { +int APS5_VABI sceImeConfigSet(std::uint64_t unusedArgument, std::uint64_t configuration, std::uint32_t key, std::uint32_t value) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceImeConfigGet(void) { +int APS5_VABI sceImeConfigGet(std::uint64_t unusedArgument, std::uint64_t configuration, std::uint32_t key) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceIme.md b/docs/dev/debt/libSceIme.md index d7134b33e2..b26437726a 100644 --- a/docs/dev/debt/libSceIme.md +++ b/docs/dev/debt/libSceIme.md @@ -11,9 +11,8 @@ - [sceImeGetPanelPositionAndForm](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the output is taken to be the `PositionAndForm` that `sceImeDialogGetPanelPositionAndForm` fills. While a panel is open it reports the type, position and alignments passed to `sceImeOpen` and the size `sceImeGetPanelSize` gives; the console's values for a panel moved by its own position adjustment are unknown. - [sceImeFilterText](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the behaviour follows the disassembly of `libSceIme.sprx` from firmware 13.60 and is differential-tested against that function in an emulator, not measured on a console. [ImeFilterCodepages.hpp](../../../core/libs/prx/libSceIme/include/ImeFilterCodepages.hpp) holds the two codepage checks (helpers at 0x4850 and 0x48f0), rebuilt from Unicode's public [CP932](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT) and [CP936](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT) tables plus the Latin-1, CJK Extension A and a few other units the firmware also accepts (listed in the PR). The parameter layout beyond `type` is inferred: `mask` is the allowed-class bitmask, and `version` and `variant` derive it when it is 0. [sceImeCheckFilterText](../../../core/libs/prx/libSceIme/Export.cpp) throws, as its contract is unknown - [sceImeOpen, sceImeUpdate, sceImeSetText, sceImeSetCaret, sceImeClose](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - the event ids (Open 0, UpdateText 1, UpdateCaret 2, PressClose 4, PressEnter 5), the caret directions (left 1, right 2), the error codes and the text event payload follow the PS4 reimplementation in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/b72489d8c24e63fc56a29fe2c90120ab768a210d/src/core/libraries/ime/ime.cpp); none is measured on a PS5. Events are queued and delivered by `sceImeUpdate` only when it is passed the handler given to `sceImeOpen`, with that call's `arg`. A text event points `str` at a copy of the text kept in `work`, and `inputTextBuffer` holds the same text; both are terminated, `caret_index` is the caret after the edit and the one edit area has the edit position and the change in UTF-16 units (0 for Enter and Close). An edit sends one UpdateText; a caret move sends one UpdateCaret per UTF-16 unit, as shadPS4 does. Enter inserts a line break with the multiline option and sends PressEnter otherwise; Escape sends PressClose. Typed text is cut to `maxTextLength` without splitting a surrogate pair, line breaks are dropped without the multiline option, and the BasicLatin, URL, mail and number types do not filter characters. `sceImeOpen` checks, in shadPS4's order, the user, type, enter label, input method, option bits (the `sceImeGetPanelSize` mask), `maxTextLength` (1 to 2048), the text buffer, the position (below 1920x1080, or 3840x2160 with the over-2K option), the alignments, the extended priority and reserved bytes, `work` (non-null, 4-byte aligned), the handler, the reserved bytes and the initial text (no unpaired surrogate, no line break without the multiline option); shadPS4's SDK-dependent option, language and extended-option masks and its work-overlap check are not applied, and only the text copy is written to `work`. `sceImeSetText` keeps the caret unless the text became shorter, `sceImeSetCaret` rejects an index past the text with `0x80bc0030`, and neither sends an event. `sceImeSetTextGeometry` accepts any geometry while a panel is open, since no panel is drawn -- [sceImeForTestFunction](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeConfigSet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature -- [sceImeConfigGet](../../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) - unknown signature +- [sceImeForTestFunction](../../../core/libs/prx/libSceIme/Export.cpp) returns a constant error without consuming arguments in the supplied module; unused argument types cannot be recovered from that body. +- [sceImeConfigSet and sceImeConfigGet](../../../core/libs/prx/libSceIme/Export.cpp) preserve the native argument slots: an unused first slot, a 64-bit configuration value, a 32-bit key and, for set, a 32-bit value. The service receives a 24-byte request; configuration/key semantics and unused argument types remain unresolved. The getter advertises one output transfer but does not initialize its descriptor in this module. Protected bodies remain unchanged. - [libSceIme pending exports](../../../core/libs/prx/libSceIme/Export.cpp) - 2 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceIme.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`. From 3823004797a1b38c2a4aadbcec6a49b1ed745a27 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:29:32 +0300 Subject: [PATCH 420/523] fix(mouse): validate default absolute position bounds --- core/libs/prx/libSceMouse/src/mouse_impl.cpp | 3 ++- core/libs/prx/libSceMouse/tests/Mouse.cpp | 3 +++ docs/dev/debt/libSceMouse.md | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceMouse/src/mouse_impl.cpp b/core/libs/prx/libSceMouse/src/mouse_impl.cpp index d73ccb929d..b4df78375a 100644 --- a/core/libs/prx/libSceMouse/src/mouse_impl.cpp +++ b/core/libs/prx/libSceMouse/src/mouse_impl.cpp @@ -82,10 +82,11 @@ int Read(std::int32_t handle, MouseData* data, std::int32_t num) { return available; } -int ResetAbsolutePosition(std::int32_t handle, std::uint16_t, std::uint16_t) { +int ResetAbsolutePosition(std::int32_t handle, std::uint16_t x, std::uint16_t y) { std::lock_guard lock(mouseMutex); if (!initialized) return MOUSE_ERROR_NOT_INITIALIZED; if (handle != MOUSE_HANDLE || !opened) return MOUSE_ERROR_INVALID_HANDLE; + if (x >= 1920 || y >= 1080) return MOUSE_ERROR_INVALID_ARG; return MOUSE_OK; } diff --git a/core/libs/prx/libSceMouse/tests/Mouse.cpp b/core/libs/prx/libSceMouse/tests/Mouse.cpp index 3fd5a90ca2..7e7b1fcf13 100644 --- a/core/libs/prx/libSceMouse/tests/Mouse.cpp +++ b/core/libs/prx/libSceMouse/tests/Mouse.cpp @@ -34,6 +34,9 @@ int main() { Require(sceMouseOpen(1, 0, 0, nullptr) == MOUSE_ERROR_ALREADY_OPENED); Require(sceMouseResetAbsolutePosition(42, 959, 539) == MOUSE_ERROR_INVALID_HANDLE); Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 959, 539) == MOUSE_OK); + Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 1919, 1079) == MOUSE_OK); + Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 1920, 1079) == MOUSE_ERROR_INVALID_ARG); + Require(sceMouseResetAbsolutePosition(MOUSE_HANDLE, 1919, 1080) == MOUSE_ERROR_INVALID_ARG); Require(sceMouseRead(42, data, 1) == MOUSE_ERROR_INVALID_HANDLE); Require(sceMouseRead(MOUSE_HANDLE, nullptr, 1) == MOUSE_ERROR_INVALID_ARG); Require(sceMouseRead(MOUSE_HANDLE, data, 65) == MOUSE_ERROR_INVALID_ARG); diff --git a/docs/dev/debt/libSceMouse.md b/docs/dev/debt/libSceMouse.md index 19fe9ab3b5..afca5f0db7 100644 --- a/docs/dev/debt/libSceMouse.md +++ b/docs/dev/debt/libSceMouse.md @@ -6,4 +6,4 @@ ### Functional -- [sceMouseResetAbsolutePosition](../../../core/libs/prx/libSceMouse/Export.cpp) (libSceMouse) - signature taken from a call site (a handle, then x and y as 16-bit values: PPSA30601 passes the centre of its display size); behaviour not measured on hardware. Validates initialization and the handle like `sceMouseRead` and returns `MOUSE_OK`; the position is not stored because the library reports relative motion only +- [sceMouseResetAbsolutePosition](../../../core/libs/prx/libSceMouse/Export.cpp) (libSceMouse) - the position is not stored because the host reports relative motion only. Native input widths and validation are confirmed, including the default 1920 by 1080 bounds; other native coordinate modes and absolute reports remain unimplemented From e621c63b572b1f4b273c0c4fa23fbb3832e6b562 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:29:34 +0300 Subject: [PATCH 421/523] docs(http): clarify unresolved debug exports --- docs/dev/debt/libSceHttp.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libSceHttp.md b/docs/dev/debt/libSceHttp.md index 3df2c812ff..fce148fc5f 100644 --- a/docs/dev/debt/libSceHttp.md +++ b/docs/dev/debt/libSceHttp.md @@ -21,8 +21,8 @@ - [sceHttpSetAuthInfoCallback](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - signature and callback type follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.h) (PS4); no PS5 reference has them. The `id` is not checked, as in the other libSceHttp setters - [sceHttpWaitRequest](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the timeout is assumed to be in microseconds and a negative timeout to wait without limit; the invalid-value error for a null handle or event array or a non-positive event count is assumed, not measured. No request is ever pending, so a wait returns 0 events when its timeout passes - [sceHttpUriCopy](../../../core/libs/prx/libSceHttp/src/Uri.cpp) (libSceHttp) - read from the disassembly of libSceHttp.sprx on firmware 13.60, not measured on a console and not compared with other emulators. The arguments are (out, src, pool, prepare, require), `prepare` before `require`, the reverse of `sceHttpUriParse`. A null source returns `ERROR_INVALID_URL`; without an output or a pool, a null `require` returns `ERROR_INVALID_VALUE`, otherwise the size is only reported. Only the scheme, hostname, path, username and password are copied, in that order, each with its terminator (a missing one becomes an empty string and takes one byte), and the port; the query, the fragment and the opaque flag stay 0. A pool that is too small clears the output and the pool and returns `ERROR_OUT_OF_MEMORY` (0x80431022, the console's out-of-size error) -- [sceHttpDbgEnableProfile](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature -- [sceHttpDbgSetPrintf](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - unknown signature +- [sceHttpDbgEnableProfile](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the export is absent from the supplied native module; its signature remains unresolved. +- [sceHttpDbgSetPrintf](../../../core/libs/prx/libSceHttp/Export.cpp) returns constant success without consuming arguments in the supplied module; the callback contract remains unresolved. - [sceHttpDbgShowStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. - [sceHttpDbgShowConnectionStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. - [sceHttpDbgShowRequestStat](../../../core/libs/prx/libSceHttp/Export.cpp) (libSceHttp) - the native diagnostic consumes no arguments; it prints library state. The throwing implementation remains unchanged. From ce4d3ab989a50240abb428578acea370e2381057 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:34:41 +0300 Subject: [PATCH 422/523] fix(audiopropagation): recover system options and cpu memory sizing --- .../AudioPropagation.hpp | 24 ++++++- .../prx/libSceAudioPropagation/Export.cpp | 39 +++++++++-- core/libs/tests/GuestAudioPropagation.cpp | 69 ++++++++++++++----- docs/dev/debt/libSceAudioPropagation.md | 4 +- 4 files changed, 108 insertions(+), 28 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index 63e23fb5e8..99db7ef6e3 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -27,6 +27,26 @@ inline constexpr std::size_t PortalParamsSize = 0x60; inline constexpr std::uint32_t PathEntryId = 0x010107d9; inline constexpr std::size_t PathEntrySize = 0x28; +inline constexpr std::uint32_t SystemOptionsId = 0x010107d5; +inline constexpr std::size_t SystemOptionsSize = 56; + +struct SystemOptions { + AudioPropagationStructDescriptor desc; + std::uint32_t maxSources; + std::uint32_t maxMaterials; + std::uint32_t parameter24; + std::uint32_t reflectionOrder; + std::uint32_t frameSamples; + float propagationSpeed; + float maxPathLength; + std::uint32_t flags; + std::uint8_t opaque[8]; +}; + +static_assert(sizeof(SystemOptions) == SystemOptionsSize); +static_assert(offsetof(SystemOptions, frameSamples) == 32); +static_assert(offsetof(SystemOptions, flags) == 44); + struct SourceInfo; struct SystemInfo; struct ReportRegistration; @@ -130,8 +150,8 @@ static_assert(offsetof(PortalParams, rooms) == 0x50); extern "C" { -std::int32_t APS5_VABI sceAudioPropagationSystemQueryMemory(const void* options, AudioPropagationSystemMemory* outMemory); -std::int32_t APS5_VABI sceAudioPropagationSystemCreate(const void* options, const AudioPropagationSystemMemory* memory, AudioPropagationHandle* outSystem); +std::int32_t APS5_VABI sceAudioPropagationSystemQueryMemory(const AudioPropagation::SystemOptions* options, AudioPropagationSystemMemory* outMemory); +std::int32_t APS5_VABI sceAudioPropagationSystemCreate(const AudioPropagation::SystemOptions* options, const AudioPropagationSystemMemory* memory, AudioPropagationHandle* outSystem); std::int32_t APS5_VABI sceAudioPropagationSystemDestroy(AudioPropagationHandle system); std::int32_t APS5_VABI sceAudioPropagationSystemSetAttributes(AudioPropagationHandle system, const AudioPropagation::Attribute* attributes, std::uint32_t count); std::int32_t APS5_VABI sceAudioPropagationSystemRegisterMaterial(AudioPropagationHandle system, const AudioPropagationStructDescriptor* material, AudioPropagationHandle* outMaterial); diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index 27fca89aad..bf79473aa2 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -2,6 +2,7 @@ #include "prx/libc/include/General.hpp" #include +#include #include #include #include @@ -115,19 +116,45 @@ void requireNoPropagation(bool none, const char* function) { extern "C" { -std::int32_t APS5_VABI sceAudioPropagationSystemQueryMemory(const void* options, AudioPropagationSystemMemory* outMemory) { - require(options != nullptr, __func__); - requireDescriptor(outMemory != nullptr ? &outMemory->desc : nullptr, SystemMemoryId, SystemMemorySize, __func__); - outMemory->size_cpu_mem = sizeof(SystemState); +std::int32_t APS5_VABI sceAudioPropagationSystemQueryMemory(const SystemOptions* options, AudioPropagationSystemMemory* outMemory) { + if (!options || !outMemory) return ErrorNullPointer; + if (outMemory->desc.id != SystemMemoryId || outMemory->desc.size != SystemMemorySize) return static_cast(0x8A700007); + outMemory->p_cpu_mem = nullptr; + outMemory->size_cpu_mem = 0; + outMemory->p_gpu_mem = nullptr; outMemory->size_gpu_mem = 0; + if (options->desc.id != SystemOptionsId || options->desc.size != SystemOptionsSize) return static_cast(0x8A700007); + if (options->maxSources == 0 || options->maxSources > 64 || options->maxMaterials == 0 || options->maxMaterials > 64 || options->parameter24 != 6 || options->reflectionOrder == 0 || options->reflectionOrder > 2) return ErrorInvalidArgument; + const auto frameSamples = options->frameSamples; + if (frameSamples != 256 && frameSamples != 512 && frameSamples != 1024 && frameSamples != 2048 && frameSamples != 4096) return ErrorInvalidArgument; + if (!(options->propagationSpeed > 0x1p-23f) || options->propagationSpeed > 1372.0f || !(options->maxPathLength > 0x1p-23f) || options->maxPathLength > 1372.0f || options->flags > 31 || ((options->flags & 1) && frameSamples != 512)) return ErrorInvalidArgument; + if (options->flags & 1) throw std::runtime_error(std::string(__func__) + ": GPU memory requirements depend on an unsupported native runtime query"); + const std::uint64_t sources = options->maxSources; + const std::uint64_t paths = sources * (options->reflectionOrder == 1 ? 6 : 36); + const float delaySeconds = options->maxPathLength / options->propagationSpeed; + const float delaySamples = delaySeconds * 48000.0f; + const auto delayCapacity = static_cast(std::ceil(delaySamples)); + std::uint64_t bytes = sources * 1024 + paths * (32 * (options->reflectionOrder + 2) + 8928); + bytes += frameSamples == 256 ? 2048 : 4 * frameSamples; + bytes += 4 * delayCapacity * sources; + bytes += bytes & 15; + bytes += 160 * paths + 12352 + 4208 * options->maxMaterials; + if (frameSamples == 256) bytes += sources * ((options->flags & 2) ? 36896 : 16416); + if (options->flags & 4) bytes += 2000656; + if (options->flags & 8) bytes += 288 * paths; + if (options->flags & 16) bytes += 144 * paths; + outMemory->size_cpu_mem = bytes + 5368 - ((bytes & 15) ^ 8); return 0; } -std::int32_t APS5_VABI sceAudioPropagationSystemCreate(const void* options, const AudioPropagationSystemMemory* memory, AudioPropagationHandle* outSystem) { +std::int32_t APS5_VABI sceAudioPropagationSystemCreate(const SystemOptions* options, const AudioPropagationSystemMemory* memory, AudioPropagationHandle* outSystem) { require(options != nullptr && outSystem != nullptr, __func__); requireDescriptor(memory != nullptr ? &memory->desc : nullptr, SystemMemoryId, SystemMemorySize, __func__); + auto requiredMemory = *memory; + const auto result = sceAudioPropagationSystemQueryMemory(options, &requiredMemory); + if (result != 0) return result; const auto address = reinterpret_cast(memory->p_cpu_mem); - require(address != 0 && address % alignof(SystemState) == 0 && memory->size_cpu_mem >= sizeof(SystemState) && memory->size_gpu_mem == 0, __func__); + require(address != 0 && address % alignof(SystemState) == 0 && memory->size_cpu_mem >= requiredMemory.size_cpu_mem && memory->size_gpu_mem == 0, __func__); auto& state = registry(); std::lock_guard lock(state.mutex); require(!state.systems.contains(address), __func__); diff --git a/core/libs/tests/GuestAudioPropagation.cpp b/core/libs/tests/GuestAudioPropagation.cpp index 071ab4f0ce..5e03aefa0f 100644 --- a/core/libs/tests/GuestAudioPropagation.cpp +++ b/core/libs/tests/GuestAudioPropagation.cpp @@ -1,6 +1,7 @@ #include "prx/libSceAudioPropagation/AudioPropagation.hpp" #include +#include #include #include #include @@ -50,38 +51,69 @@ AudioPropagation::Attribute Attribute(const void* value, std::size_t size) { } int main() { - const std::uint64_t options[7]{}; - alignas(16) std::uint8_t arena[64]{}; + const AudioPropagation::SystemOptions options{{AudioPropagation::SystemOptionsId, AudioPropagation::SystemOptionsSize}, 2, 2, 6, 1, 256, 343.0f, 343.0f, 0, {}}; auto memory = Memory(); - Require(sceAudioPropagationSystemQueryMemory(options, &memory) == 0, "query memory failed"); - Require(memory.size_cpu_mem != 0 && memory.size_cpu_mem <= sizeof(arena), "cpu size outside the arena"); + Require(sceAudioPropagationSystemQueryMemory(&options, &memory) == 0, "query memory failed"); + Require(memory.size_cpu_mem == 557264, "native CPU memory size mismatch"); + std::vector arenaStorage((memory.size_cpu_mem + 7) / 8); + auto* arena = reinterpret_cast(arenaStorage.data()); Require(memory.size_gpu_mem == 0 && memory.p_cpu_mem == nullptr && memory.p_gpu_mem == nullptr, "query wrote more than the sizes"); - RequireThrows([&] { sceAudioPropagationSystemQueryMemory(nullptr, &memory); }, "null options"); - RequireThrows([&] { sceAudioPropagationSystemQueryMemory(options, nullptr); }, "null memory"); + Require(sceAudioPropagationSystemQueryMemory(nullptr, &memory) == AudioPropagation::ErrorNullPointer, "null options"); + Require(sceAudioPropagationSystemQueryMemory(&options, nullptr) == AudioPropagation::ErrorNullPointer, "null memory"); auto wrong = Memory(); wrong.desc.size = 0x28; - RequireThrows([&] { sceAudioPropagationSystemQueryMemory(options, &wrong); }, "wrong descriptor size"); + Require(sceAudioPropagationSystemQueryMemory(&options, &wrong) == static_cast(0x8A700007), "wrong descriptor size"); wrong = Memory(); wrong.desc.id = AudioPropagation::RayId; - RequireThrows([&] { sceAudioPropagationSystemQueryMemory(options, &wrong); }, "wrong descriptor id"); + Require(sceAudioPropagationSystemQueryMemory(&options, &wrong) == static_cast(0x8A700007), "wrong descriptor id"); + + auto probeOptions = options; + auto probeMemory = Memory(); + probeMemory.p_cpu_mem = arena; + probeMemory.p_gpu_mem = arena; + probeOptions.maxSources = 0; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == AudioPropagation::ErrorInvalidArgument, "zero source capacity accepted"); + Require(probeMemory.p_cpu_mem == nullptr && probeMemory.p_gpu_mem == nullptr && probeMemory.size_cpu_mem == 0 && probeMemory.size_gpu_mem == 0, "invalid options did not clear memory output"); + probeOptions = options; + probeOptions.parameter24 = 7; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == AudioPropagation::ErrorInvalidArgument, "unsupported parameter accepted"); + probeOptions = options; + probeOptions.frameSamples = 768; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == AudioPropagation::ErrorInvalidArgument, "unsupported frame size accepted"); + probeOptions = options; + probeOptions.propagationSpeed = 0x1p-23f; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == AudioPropagation::ErrorInvalidArgument, "invalid propagation speed accepted"); + probeOptions = options; + probeOptions.frameSamples = 512; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == 0 && probeMemory.size_cpu_mem == 524432, "512 sample memory size mismatch"); + probeOptions = options; + probeOptions.reflectionOrder = 2; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == 0 && probeMemory.size_cpu_mem == 1110608, "second order memory size mismatch"); + probeOptions = options; + probeOptions.flags = 30; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == 0 && probeMemory.size_cpu_mem == 2604064, "option memory sizes mismatch"); + probeOptions.flags = 1; + Require(sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory) == AudioPropagation::ErrorInvalidArgument, "GPU frame size constraint ignored"); + probeOptions.frameSamples = 512; + RequireThrows([&] { sceAudioPropagationSystemQueryMemory(&probeOptions, &probeMemory); }, "unknown GPU memory query returned a size"); AudioPropagationHandle system = 0; - RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "create without cpu memory"); + RequireThrows([&] { sceAudioPropagationSystemCreate(&options, &memory, &system); }, "create without cpu memory"); memory.p_cpu_mem = arena + 1; - RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "misaligned cpu memory"); + RequireThrows([&] { sceAudioPropagationSystemCreate(&options, &memory, &system); }, "misaligned cpu memory"); memory.p_cpu_mem = arena; memory.size_gpu_mem = 16; - RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "unrequested gpu memory"); + RequireThrows([&] { sceAudioPropagationSystemCreate(&options, &memory, &system); }, "unrequested gpu memory"); memory.size_gpu_mem = 0; const auto required = memory.size_cpu_mem; memory.size_cpu_mem = required - 1; - RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &system); }, "cpu memory too small"); + RequireThrows([&] { sceAudioPropagationSystemCreate(&options, &memory, &system); }, "cpu memory too small"); memory.size_cpu_mem = required; - Require(sceAudioPropagationSystemCreate(options, &memory, &system) == 0, "create failed"); + Require(sceAudioPropagationSystemCreate(&options, &memory, &system) == 0, "create failed"); Require(system == reinterpret_cast(arena), "system handle is not its memory"); AudioPropagationHandle twice = 0; - RequireThrows([&] { sceAudioPropagationSystemCreate(options, &memory, &twice); }, "second system in the same memory"); + RequireThrows([&] { sceAudioPropagationSystemCreate(&options, &memory, &twice); }, "second system in the same memory"); const std::uint64_t value = 1; const auto attribute = Attribute(&value, sizeof(value)); @@ -145,12 +177,13 @@ int main() { Require(sceAudioPropagationSourceSetAudioPaths(source, nullptr, 0) == 0, "empty audio paths failed"); RequireThrows([&] { sceAudioPropagationSourceSetAudioPaths(source, rays, 1); }, "set audio paths"); - alignas(16) std::uint8_t otherArena[64]{}; + std::vector otherArenaStorage(arenaStorage.size()); + auto* otherArena = reinterpret_cast(otherArenaStorage.data()); auto otherMemory = Memory(); - Require(sceAudioPropagationSystemQueryMemory(options, &otherMemory) == 0, "second query memory failed"); + Require(sceAudioPropagationSystemQueryMemory(&options, &otherMemory) == 0, "second query memory failed"); otherMemory.p_cpu_mem = otherArena; AudioPropagationHandle otherSystem = 0; - Require(sceAudioPropagationSystemCreate(options, &otherMemory, &otherSystem) == 0, "second system create failed"); + Require(sceAudioPropagationSystemCreate(&options, &otherMemory, &otherSystem) == 0, "second system create failed"); AudioPropagationHandle otherSource = 0; Require(sceAudioPropagationSourceCreate(otherSystem, &otherSource) == 0, "second system source create failed"); AudioPropagationHandle secondSource = 0; @@ -251,7 +284,7 @@ int main() { Require(sceAudioPropagationSystemUnregisterMaterial(lateMaterial) == 0, "orphaned material unregister is not a no-op"); RequireThrows([&] { sceAudioPropagationSourceDestroy(system, lateSource); }, "orphan destroyed twice"); RequireThrows([&] { sceAudioPropagationSystemDestroy(system); }, "destroy twice"); - Require(sceAudioPropagationSystemCreate(options, &memory, &system) == 0, "memory reuse after destroy failed"); + Require(sceAudioPropagationSystemCreate(&options, &memory, &system) == 0, "memory reuse after destroy failed"); Require(sceAudioPropagationSystemDestroy(system) == 0, "second destroy failed"); RequireThrows([&] { sceAudioPropagationPathGetNumPoints(); }, "path get num points throw"); diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 578552a304..3d57114611 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -2,10 +2,10 @@ ### Silent stubs -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) lacks acoustic propagation: attributes have no effect, sources expose no paths or rays, rendering preserves the destination mix without adding path contributions, and memory queries return only a 16-byte CPU header. Ray results and audio paths accept empty lists and throw otherwise; calculation returns initial native validation errors and throws for unsupported propagation. Room, portal, source and material handles are tracked, but destruction after their system is destroyed silently succeeds without native lifetime semantics being confirmed. Occlusion, reflections and reverb are absent. +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) lacks acoustic propagation: attributes have no effect, sources expose no paths or rays, rendering preserves the destination mix without adding path contributions, and allocated CPU work memory is mostly unused. Ray results and audio paths accept empty lists and throw otherwise; calculation returns initial native validation errors and throws for unsupported propagation. Room, portal, source and material handles are tracked, but destruction after their system is destroyed silently succeeds without native lifetime semantics being confirmed. Occlusion, reflections and reverb are absent. ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): the native path lookup and update confirm a 64-bit path handle, a read-only 224-byte path description and a float gain in XMM0. The description contains four 48-byte points, a point count and flags; each point contains a descriptor, room handle, gain and three position floats. The last eight bytes of each point and of the description are not consumed by these paths and remain opaque. Protected implementation remains unchanged; allocation, ownership and rendering are not implemented. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options and frame configuration remain unknown. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes silence over the supplied capacity, without native accumulation or frame/count limits. Other formats and native validation precedence remain unimplemented. Source/system diagnostic fields still need reconstruction. +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options use a recovered 56-byte record; the field at byte 24 and final eight bytes remain semantically unresolved. CPU memory requirements and option limits are recovered; GPU memory queries throw because their platform-specific runtime condition is unresolved. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes silence over the supplied capacity, without native accumulation or frame/count limits. Other formats and native validation precedence remain unimplemented. Source/system diagnostic fields still need reconstruction. - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From 05380f7402242caa2492c438833a2cb4f0e672e9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:35:43 +0300 Subject: [PATCH 423/523] fix(audiopropagation): enforce configured render frame limits --- .../prx/libSceAudioPropagation/Export.cpp | 10 ++++++--- core/libs/tests/GuestAudioPropagation.cpp | 21 ++++++++++++------- docs/dev/debt/libSceAudioPropagation.md | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/Export.cpp b/core/libs/prx/libSceAudioPropagation/Export.cpp index bf79473aa2..fb835aca4d 100644 --- a/core/libs/prx/libSceAudioPropagation/Export.cpp +++ b/core/libs/prx/libSceAudioPropagation/Export.cpp @@ -23,7 +23,8 @@ constexpr std::uint64_t SystemMagic = [] { struct SystemState { std::uint64_t magic; - std::uint64_t reserved; + std::uint32_t frameSamples; + std::uint32_t maxSources; }; enum class Kind { Room, Portal, Source, Material }; @@ -158,7 +159,7 @@ std::int32_t APS5_VABI sceAudioPropagationSystemCreate(const SystemOptions* opti auto& state = registry(); std::lock_guard lock(state.mutex); require(!state.systems.contains(address), __func__); - *static_cast(memory->p_cpu_mem) = SystemState{SystemMagic, 0}; + *static_cast(memory->p_cpu_mem) = SystemState{SystemMagic, options->frameSamples, options->maxSources}; state.systems.insert(address); *outSystem = address; return 0; @@ -333,11 +334,14 @@ std::int32_t APS5_VABI sceAudioPropagationSourceRender(AudioPropagationHandle sy auto& state = registry(); std::lock_guard lock(state.mutex); requireSystem(state, system, __func__); + const auto& configuration = *reinterpret_cast(system); + require(count <= configuration.maxSources, __func__); + const auto requiredBytes = static_cast(configuration.frameSamples) * 16 * sizeof(float); for (std::uint32_t index = 0; index < count; ++index) { const auto& info = infos[index]; requireDescriptor(&info.desc, RenderInfoId, RenderInfoSize, __func__); require(requireObject(state, info.source, Kind::Source, __func__).system == system, __func__); - require(info.output != nullptr && info.outputSize != 0, __func__); + require(info.output != nullptr && info.outputSize >= requiredBytes, __func__); if (info.format != RenderFormat) throw std::runtime_error(std::string(__func__) + ": render format " + std::to_string(info.format) + " is unknown"); } for (std::uint32_t index = 0; index < count; ++index) { diff --git a/core/libs/tests/GuestAudioPropagation.cpp b/core/libs/tests/GuestAudioPropagation.cpp index 5e03aefa0f..0f060fd0a5 100644 --- a/core/libs/tests/GuestAudioPropagation.cpp +++ b/core/libs/tests/GuestAudioPropagation.cpp @@ -189,11 +189,12 @@ int main() { AudioPropagationHandle secondSource = 0; Require(sceAudioPropagationSourceCreate(system, &secondSource) == 0, "second source create failed"); - float outputs[2][64]; - float expected[2][64]; + constexpr std::size_t frameValues = 256 * 16; + float outputs[2][frameValues + 16]; + float expected[2][frameValues + 16]; const auto fill = [&] { for (std::size_t channel = 0; channel < 2; ++channel) { - for (std::size_t sample = 0; sample < 64; ++sample) { + for (std::size_t sample = 0; sample < frameValues + 16; ++sample) { outputs[channel][sample] = static_cast(sample + 1) / (channel == 0 ? 128.0f : -128.0f); } } @@ -211,10 +212,13 @@ int main() { Require(untouched(), "empty source erased an existing mix"); infos[0].channelCount = 99; Require(sceAudioPropagationSourceRender(system, infos, 1) == 0 && infos[0].channelCount == 0, "fixed render format clears the channel count"); - const auto* tail = reinterpret_cast(outputs[1]); - Require(tail[0] == 0xab, "render wrote past the output size"); + Require(untouched(), "render wrote past the output size"); reset(); - infos[0].outputSize = sizeof(float) * 16; + infos[0].outputSize = sizeof(float) * (frameValues - 1); + RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 2); }, "short frame output accepted"); + Require(untouched(), "short output modified a render buffer"); + reset(); + infos[0].outputSize = sizeof(float) * frameValues; Require(sceAudioPropagationSourceRender(system, infos, 2) == 0, "render of two sources failed"); Require(untouched(), "empty sources modified separate destination mixes or their tails"); reset(); @@ -222,9 +226,9 @@ int main() { Require(sceAudioPropagationSourceRender(system, infos, 2) == 0, "render into a shared mix failed"); Require(untouched(), "empty sources modified a shared destination mix"); reset(); - infos[0].outputSize = sizeof(float) * 32; + infos[0].outputSize = sizeof(float) * frameValues; infos[1].output = outputs[0] + 16; - infos[1].outputSize = sizeof(float) * 32; + infos[1].outputSize = sizeof(float) * frameValues; Require(sceAudioPropagationSourceRender(system, infos, 2) == 0, "render into overlapping mixes failed"); Require(untouched(), "empty sources modified overlapping destination mixes"); reset(); @@ -235,6 +239,7 @@ int main() { reset(); RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 0); }, "render without infos"); + RequireThrows([&] { sceAudioPropagationSourceRender(system, infos, 3); }, "render exceeds source capacity"); RequireThrows([&] { sceAudioPropagationSourceRender(system, nullptr, 1); }, "render with null infos"); RequireThrows([&] { sceAudioPropagationSourceRender(system + 16, infos, 1); }, "render on an unknown system"); infos[1].desc.id = AudioPropagation::RayId; diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 3d57114611..501b37adab 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -7,5 +7,5 @@ ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): the native path lookup and update confirm a 64-bit path handle, a read-only 224-byte path description and a float gain in XMM0. The description contains four 48-byte points, a point count and flags; each point contains a descriptor, room handle, gain and three position floats. The last eight bytes of each point and of the description are not consumed by these paths and remain opaque. Protected implementation remains unchanged; allocation, ownership and rendering are not implemented. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options use a recovered 56-byte record; the field at byte 24 and final eight bytes remain semantically unresolved. CPU memory requirements and option limits are recovered; GPU memory queries throw because their platform-specific runtime condition is unresolved. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes silence over the supplied capacity, without native accumulation or frame/count limits. Other formats and native validation precedence remain unimplemented. Source/system diagnostic fields still need reconstruction. +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options use a recovered 56-byte record; the field at byte 24 and final eight bytes remain semantically unresolved. CPU memory requirements and option limits are recovered; GPU memory queries throw because their platform-specific runtime condition is unresolved. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes one silent frame without native accumulation. Frame capacity and source-count limits follow the recovered options. Other formats and native validation precedence remain unimplemented. Source/system diagnostic fields still need reconstruction. - [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. From 6f70da54125088b77a13dcd90f8496aaceea4e05 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:36:38 +0300 Subject: [PATCH 424/523] fix(hmd): restore default led output signature --- core/libs/prx/libSceHmd/Export.cpp | 2 +- docs/dev/debt/libSceHmd.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceHmd/Export.cpp b/core/libs/prx/libSceHmd/Export.cpp index 6f28a18447..3ede810dfe 100644 --- a/core/libs/prx/libSceHmd/Export.cpp +++ b/core/libs/prx/libSceHmd/Export.cpp @@ -542,7 +542,7 @@ int APS5_VABI sceHmdReprojectionStartCapture(void) { return 0; } -int APS5_VABI sceHmdInternalGetDefaultLedData(void) { +int APS5_VABI sceHmdInternalGetDefaultLedData(std::int32_t handle, std::uint32_t* mask, HmdLedData* data) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceHmd.md b/docs/dev/debt/libSceHmd.md index 76f7c2767d..3af366507e 100644 --- a/docs/dev/debt/libSceHmd.md +++ b/docs/dev/debt/libSceHmd.md @@ -9,8 +9,8 @@ - [libSceHmd internal and capture exports](../../../core/libs/prx/libSceHmd/Export.cpp) - input/output directions and argument registers are recovered for shared VR2D data, device binding, optical and serial queries, screen status and capture operations. VR2D records are 128 bytes, optical output 16, serial output 16, PU position output 20, crash-dump output 68, audio/video status 32, and reprojection debug outputs 216 or 1136. Payload field meanings and capture callback contracts remain incomplete; these structures use forward declarations. - [libSceHmd queries without a headset](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - the signatures, the distortion work memory (`0x20000`, aligned to `0x400`) and reprojection buffer sizes (garlic `0x100000`, onion `0x810`, both aligned to `0x100`), and the results of `sceHmdGetFieldOfView`, `sceHmdGet2DEyeOffset`, `sceHmdGetInertialSensorData` (`HANDLE_INVALID` once initialized; field of view checks its output for null first) and `sceHmdGetAssyError` (null output, then `DEVICE_DISCONNECTED` once initialized) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/a808e76/src/core/libraries/hmd/hmd.cpp) (PS4) with no headset, not a PS5 measurement. Before initialization `sceHmdGetFieldOfView` returns `NOT_INITIALIZED` for a non-null output, where shadPS4 checks the handle first and returns `HANDLE_INVALID`. The third argument of `sceHmdGetInertialSensorData` is unnamed there and taken as a count -- [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature -- [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) (libSceHmd) - unknown signature +- [sceHmdDistortionGetCompoundEyeCorrectionCommand](../../../core/libs/prx/libSceHmd/Export.cpp) returns a constant error without consuming arguments in the supplied module; unused argument types remain unresolved. +- [sceHmdInternalGetDefaultLedData](../../../core/libs/prx/libSceHmd/Export.cpp) takes an unused handle, a 32-bit mask output and a nine-byte LED output. Output sizes match the paired setter; mask bits and LED payload semantics remain unresolved. The protected body is unchanged. [Device, field-of-view, assembly-error, distortion, sensor and DFU query signatures](../../../core/libs/prx/libSceHmd/Export.cpp) are recovered from native register use. Field of view and assembly errors each output four floats; device information occupies 64 bytes and distortion output spans 76 bytes. The remaining device/sensor/report field semantics and DFU status bit meanings are unresolved. Assembly confirms integer error returns where decompilation incorrectly inferred void. Protected bodies are unchanged. From 597d17ecb21f837151e2878df1b3001243dd7783 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:36:59 +0300 Subject: [PATCH 425/523] docs(net): record verified thread exit contract --- docs/dev/debt/libSceNet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libSceNet.md b/docs/dev/debt/libSceNet.md index 8c3e26db4d..f10d1eb873 100644 --- a/docs/dev/debt/libSceNet.md +++ b/docs/dev/debt/libSceNet.md @@ -17,7 +17,7 @@ - [sceNetGetRandom](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - generates values with the host random device; the system library maintains its own seeded PRNG state. - [sceNetInetPtonEx](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the `(int af, const char* src, void* dst, int flags)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/net/net.cpp); IPv4 conversion and the ignored flags follow PS4 firmware 10.71, not yet confirmed on PS5. There IPv4 text is read as `inet_aton` reads it (`a`, `a.b`, `a.b.c`, hex and octal parts, values wrapping at 32 bits, the text ending at the first space, tab or VT), except that a part starting with `09` is decimal (`"0989"` is 989) and an octal part with `8` or `9` fails. Flags 0, each single bit up to 0x100, 0x10000, 0x80000000 and -1 gave the same results; other values were not measured. LF, FF and CR as terminators, and `NET_EINVAL` for a null `src` or `dst`, follow `sceNetInetPton` and were not measured for this function. IPv6 converts as `sceNetInetPton` does. - [sceNetMemoryAllocate and sceNetMemoryFree](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - allocations use the guest heap instead of the system network pool. Pool limits, its allocation accounting and free-time fill are not reproduced. Allocation flag 4 bypasses the system pool reserve threshold; it throws here because the pool reserve is not implemented. Flag 2 zero-fills and other bits are ignored, matching the system allocator. -- [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - unknown signature +- [sceNetThreadExit](../../../core/libs/prx/libSceNet/Export.cpp) is a zero-argument, non-returning void export in the native module. The protected host stub still declares int and contains `return 0`; changing it to void requires changing that protected body, so the return-type mismatch is retained. - [sceNetConfigCleanUpAllInterfaces](../../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - the native routine consumes no arguments; the throwing implementation remains unchanged. - [sceNetDhcpdStart](../../../core/libs/prx/libSceNet/Export.cpp): six arguments and the three-argument address callback are confirmed. The 24-byte parameter contains five network-order addresses and a client limit. Individual address roles and the two unused thread-option meanings remain unresolved. - [sceNetPppoeStart](../../../core/libs/prx/libSceNet/Export.cpp): seven arguments are confirmed, including the parameter pointer on the stack. The 272-byte parameter contains two DNS addresses, two 128-byte credential arrays, MTU and flags. Callback arguments are user data, event and error; the two unused thread-option meanings remain unresolved. From e2a2427c69907c2226307f865ae87d1f01489f54 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:41:32 +0300 Subject: [PATCH 426/523] fix(ult): remove nonexistent creation version arguments --- core/libs/prx/libSceUlt/Export.cpp | 28 ++++++++-------- core/libs/tests/GuestUltThread.cpp | 52 +++++++++++++++--------------- docs/dev/debt/libSceUlt.md | 3 +- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index f449f06dba..8cae0b365c 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -876,40 +876,40 @@ int APS5_VABI sceUltUlthreadRuntimeDestroy(void* runtime) { return ULT_OK; } -int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name, std::uint32_t numThreads, std::uint32_t numSyncObjects, void* workArea, const void* optParam, std::uint32_t buildVersion) { - return sceUltWaitingQueueResourcePoolCreate(pool, name, numThreads, numSyncObjects, workArea, optParam, buildVersion); +int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name, std::uint32_t numThreads, std::uint32_t numSyncObjects, void* workArea, const void* optParam) { + return sceUltWaitingQueueResourcePoolCreate(pool, name, numThreads, numSyncObjects, workArea, optParam, 0); } -int APS5_VABI _sceUltSemaphoreCreate(void* semaphore, const char* name, std::int32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam, std::uint32_t buildVersion) { - return sceUltSemaphoreCreate(semaphore, name, numInitialResource, waitingQueueResourcePool, optParam, buildVersion); +int APS5_VABI _sceUltSemaphoreCreate(void* semaphore, const char* name, std::int32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam) { + return sceUltSemaphoreCreate(semaphore, name, numInitialResource, waitingQueueResourcePool, optParam, 0); } int APS5_VABI _sceUltMutexOptParamInitialize(UltMutexOptParam* optParam, std::uint32_t buildVersion) { return sceUltMutexOptParamInitialize(optParam, buildVersion); } -int APS5_VABI _sceUltMutexCreate(void* mutex, const char* name, void* waitingQueueResourcePool, const UltMutexOptParam* optParam, std::uint32_t buildVersion) { - return sceUltMutexCreate(mutex, name, waitingQueueResourcePool, optParam, buildVersion); +int APS5_VABI _sceUltMutexCreate(void* mutex, const char* name, void* waitingQueueResourcePool, const UltMutexOptParam* optParam) { + return sceUltMutexCreate(mutex, name, waitingQueueResourcePool, optParam, 0); } -int APS5_VABI _sceUltQueueDataResourcePoolCreate(void* pool, const char* name, std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject, void* waitingQueueResourcePool, void* workArea, const void* optParam, std::uint32_t buildVersion) { - return sceUltQueueDataResourcePoolCreate(pool, name, numData, dataSize, numQueueObject, waitingQueueResourcePool, workArea, optParam, buildVersion); +int APS5_VABI _sceUltQueueDataResourcePoolCreate(void* pool, const char* name, std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject, void* waitingQueueResourcePool, void* workArea, const void* optParam) { + return sceUltQueueDataResourcePoolCreate(pool, name, numData, dataSize, numQueueObject, waitingQueueResourcePool, workArea, optParam, 0); } -int APS5_VABI _sceUltQueueCreate(void* queue, const char* name, std::uint64_t dataSize, void* waitingQueueResourcePool, void* queueDataResourcePool, const void* optParam, std::uint32_t buildVersion) { - return sceUltQueueCreate(queue, name, dataSize, waitingQueueResourcePool, queueDataResourcePool, optParam, buildVersion); +int APS5_VABI _sceUltQueueCreate(void* queue, const char* name, std::uint64_t dataSize, void* waitingQueueResourcePool, void* queueDataResourcePool, const void* optParam) { + return sceUltQueueCreate(queue, name, dataSize, waitingQueueResourcePool, queueDataResourcePool, optParam, 0); } int APS5_VABI _sceUltUlthreadRuntimeOptParamInitialize(UltUlthreadRuntimeOptParam* optParam, std::uint32_t buildVersion) { return sceUltUlthreadRuntimeOptParamInitialize(optParam, buildVersion); } -int APS5_VABI _sceUltUlthreadRuntimeCreate(void* runtime, const char* name, std::uint32_t maxNumUlthread, std::uint32_t numWorkerThread, void* workArea, const void* optParam, std::uint32_t buildVersion) { - return sceUltUlthreadRuntimeCreate(runtime, name, maxNumUlthread, numWorkerThread, workArea, optParam, buildVersion); +int APS5_VABI _sceUltUlthreadRuntimeCreate(void* runtime, const char* name, std::uint32_t maxNumUlthread, std::uint32_t numWorkerThread, void* workArea, const void* optParam) { + return sceUltUlthreadRuntimeCreate(runtime, name, maxNumUlthread, numWorkerThread, workArea, optParam, 0); } -int APS5_VABI _sceUltUlthreadCreate(void* ulthread, const char* name, UltUlthreadEntry entry, std::uint64_t arg, void* context, std::uint64_t sizeContext, void* runtime, const void* optParam, std::uint32_t buildVersion) { - return sceUltUlthreadCreate(ulthread, name, entry, arg, context, sizeContext, runtime, optParam, buildVersion); +int APS5_VABI _sceUltUlthreadCreate(void* ulthread, const char* name, UltUlthreadEntry entry, std::uint64_t arg, void* context, std::uint64_t sizeContext, void* runtime, const void* optParam) { + return sceUltUlthreadCreate(ulthread, name, entry, arg, context, sizeContext, runtime, optParam, 0); } int APS5_VABI sceUltConditionVariableSignalAll(void* conditionVariable) { diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 9f4a146251..b2d3cec977 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -21,18 +21,18 @@ int APS5_VABI sceUltUlthreadJoin(void*, std::int32_t*); int APS5_VABI sceUltUlthreadTryJoin(void*, std::int32_t*); int APS5_VABI sceUltUlthreadRuntimeDestroy(void*); int APS5_VABI _sceUltUlthreadRuntimeOptParamInitialize(void*, std::uint32_t); -int APS5_VABI _sceUltUlthreadRuntimeCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*, std::uint32_t); -int APS5_VABI _sceUltUlthreadCreate(void*, const char*, UltUlthreadEntry, std::uint64_t, void*, std::uint64_t, void*, const void*, std::uint32_t); -int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*, std::uint32_t); -int APS5_VABI _sceUltSemaphoreCreate(void*, const char*, std::int32_t, void*, const void*, std::uint32_t); +int APS5_VABI _sceUltUlthreadRuntimeCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*); +int APS5_VABI _sceUltUlthreadCreate(void*, const char*, UltUlthreadEntry, std::uint64_t, void*, std::uint64_t, void*, const void*); +int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*); +int APS5_VABI _sceUltSemaphoreCreate(void*, const char*, std::int32_t, void*, const void*); int APS5_VABI sceUltSemaphoreAcquire(void*, std::int32_t); int APS5_VABI sceUltSemaphoreRelease(void*, std::int32_t); int APS5_VABI _sceUltMutexOptParamInitialize(void*, std::uint32_t); -int APS5_VABI _sceUltMutexCreate(void*, const char*, void*, const void*, std::uint32_t); +int APS5_VABI _sceUltMutexCreate(void*, const char*, void*, const void*); int APS5_VABI sceUltMutexLock(void*); int APS5_VABI sceUltMutexUnlock(void*); -int APS5_VABI _sceUltQueueDataResourcePoolCreate(void*, const char*, std::uint32_t, std::uint64_t, std::uint32_t, void*, void*, const void*, std::uint32_t); -int APS5_VABI _sceUltQueueCreate(void*, const char*, std::uint64_t, void*, void*, const void*, std::uint32_t); +int APS5_VABI _sceUltQueueDataResourcePoolCreate(void*, const char*, std::uint32_t, std::uint64_t, std::uint32_t, void*, void*, const void*); +int APS5_VABI _sceUltQueueCreate(void*, const char*, std::uint64_t, void*, void*, const void*); int APS5_VABI sceUltQueuePush(void*, const void*); int APS5_VABI sceUltQueuePop(void*, void*); int APS5_VABI sceUltMutexDestroy(void*); @@ -132,8 +132,8 @@ static void TryJoin() { std::memset(optParam, 0xff, sizeof(optParam)); CHECK(_sceUltUlthreadRuntimeOptParamInitialize(nullptr, 0) == Null); CHECK(_sceUltUlthreadRuntimeOptParamInitialize(optParam, 0) == Ok && optParam[0] == 0); - CHECK(_sceUltUlthreadRuntimeCreate(&runtime, "runtime", 1, 1, nullptr, nullptr, 0) == Ok); - CHECK(_sceUltUlthreadCreate(&thread, "parked", Parked, 0x3C, nullptr, 0, &runtime, nullptr, 0) == Ok); + CHECK(_sceUltUlthreadRuntimeCreate(&runtime, "runtime", 1, 1, nullptr, nullptr) == Ok); + CHECK(_sceUltUlthreadCreate(&thread, "parked", Parked, 0x3C, nullptr, 0, &runtime, nullptr) == Ok); while (!gStarted.load(std::memory_order_acquire)) std::this_thread::yield(); std::int32_t status = 0; @@ -155,16 +155,16 @@ static void Destroy() { Thread thread; CHECK(sceUltUlthreadRuntimeDestroy(nullptr) == Null); CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == State); - CHECK(_sceUltUlthreadRuntimeCreate(&runtime, "runtime", 1, 1, nullptr, nullptr, 0) == Ok); - CHECK(_sceUltUlthreadRuntimeCreate(&other, "other", 1, 1, nullptr, nullptr, 0) == Ok); - CHECK(_sceUltUlthreadCreate(&thread, "thread", Immediate, 0x11, nullptr, 0, &runtime, nullptr, 0) == Ok); + CHECK(_sceUltUlthreadRuntimeCreate(&runtime, "runtime", 1, 1, nullptr, nullptr) == Ok); + CHECK(_sceUltUlthreadRuntimeCreate(&other, "other", 1, 1, nullptr, nullptr) == Ok); + CHECK(_sceUltUlthreadCreate(&thread, "thread", Immediate, 0x11, nullptr, 0, &runtime, nullptr) == Ok); CHECK(sceUltUlthreadRuntimeDestroy(&other) == Ok); CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == State); std::int32_t status = 0; CHECK(sceUltUlthreadJoin(&thread, &status) == Ok && status == 0x11); CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == Ok); CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == State); - CHECK(_sceUltUlthreadCreate(&thread, "thread", Immediate, 0, nullptr, 0, &runtime, nullptr, 0) == State); + CHECK(_sceUltUlthreadCreate(&thread, "thread", Immediate, 0, nullptr, 0, &runtime, nullptr) == State); CHECK(sceUltFinalize() == Ok); } @@ -172,13 +172,13 @@ static void UnderscoredCreate() { alignas(8) static std::uint8_t pool[256]; alignas(8) static std::uint8_t semaphore[256]; alignas(8) static std::uint8_t unknownPool[256]; - CHECK(_sceUltWaitingQueueResourcePoolCreate(nullptr, "pool", 1, 1, nullptr, nullptr, 0) == Null); - CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, nullptr, nullptr, 0) == Ok); - CHECK(_sceUltSemaphoreCreate(nullptr, "semaphore", 1, pool, nullptr, 0) == Null); - CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", -1, pool, nullptr, 0) != Ok); - CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, unknownPool, nullptr, 0) != Ok); - CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr, 0) == Ok); - CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr, 0) == State); + CHECK(_sceUltWaitingQueueResourcePoolCreate(nullptr, "pool", 1, 1, nullptr, nullptr) == Null); + CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, nullptr, nullptr) == Ok); + CHECK(_sceUltSemaphoreCreate(nullptr, "semaphore", 1, pool, nullptr) == Null); + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", -1, pool, nullptr) != Ok); + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, unknownPool, nullptr) != Ok); + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr) == Ok); + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr) == State); CHECK(sceUltSemaphoreAcquire(semaphore, 2) == Ok); CHECK(sceUltSemaphoreRelease(semaphore, 2) == Ok); alignas(8) static std::uint8_t mutexOptParam[128]; @@ -187,16 +187,16 @@ static void UnderscoredCreate() { CHECK(_sceUltMutexOptParamInitialize(nullptr, 0) == Null); CHECK(_sceUltMutexOptParamInitialize(mutexOptParam, 0) == Ok); CHECK(mutexOptParam[8] == 0 && mutexOptParam[15] == 0); - CHECK(_sceUltMutexCreate(nullptr, "mutex", pool, mutexOptParam, 0) == Null); - CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam, 0) == Ok); + CHECK(_sceUltMutexCreate(nullptr, "mutex", pool, mutexOptParam) == Null); + CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Ok); CHECK(sceUltMutexLock(mutex) == Ok); CHECK(sceUltMutexUnlock(mutex) == Ok); alignas(8) static std::uint8_t dataPool[512]; alignas(8) static std::uint8_t queue[512]; - CHECK(_sceUltQueueDataResourcePoolCreate(nullptr, "data", 1, 8, 1, pool, nullptr, nullptr, 0) == Null); - CHECK(_sceUltQueueDataResourcePoolCreate(dataPool, "data", 1, 8, 1, pool, nullptr, nullptr, 0) == Ok); - CHECK(_sceUltQueueCreate(nullptr, "queue", 8, pool, dataPool, nullptr, 0) == Null); - CHECK(_sceUltQueueCreate(queue, "queue", 8, pool, dataPool, nullptr, 0) == Ok); + CHECK(_sceUltQueueDataResourcePoolCreate(nullptr, "data", 1, 8, 1, pool, nullptr, nullptr) == Null); + CHECK(_sceUltQueueDataResourcePoolCreate(dataPool, "data", 1, 8, 1, pool, nullptr, nullptr) == Ok); + CHECK(_sceUltQueueCreate(nullptr, "queue", 8, pool, dataPool, nullptr) == Null); + CHECK(_sceUltQueueCreate(queue, "queue", 8, pool, dataPool, nullptr) == Ok); std::uint64_t value = 0x1122334455667788ull, output = 0; CHECK(sceUltQueuePush(queue, &value) == Ok); CHECK(sceUltQueuePop(queue, &output) == Ok && output == value); diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index 69ec383a4a..e20ac05764 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -4,10 +4,9 @@ - [Bound condition variables](../../../core/libs/prx/libSceUlt/Export.cpp) use the creation-time mutex, inferred from PPSA28061 callers of `_sceUltConditionVariableCreate`; the wait caller reloads only the condition argument. Error precedence, busy/state errors, FIFO signal selection and unsupported recursive waits have not been measured on PS5 hardware. Non-null options and waits at recursion depth above one throw. A live condition prevents destroying its bound mutex. Finalization follows the existing host mutex lifecycle: it invalidates conditions and wakes queued or reacquiring waiters with a state error. Scheduling priority and build-version validation are not implemented. -- [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - `_sceUltUlthreadRuntimeOptParamInitialize`, `_sceUltUlthreadRuntimeCreate` and `_sceUltUlthreadCreate` are assumed to take the same arguments as the existing exports without the underscore, including the trailing build version. Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. +- [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - the added `sceUltQueuePop`, `sceUltQueueTryPush`, `sceUltQueueDestroy` and `sceUltQueueDataResourcePoolDestroy` signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with `ULT_ERROR_BUSY` and otherwise discard queued data; pool exhaustion returns `ULT_ERROR_AGAIN`. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. - [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) - a non-null option parameter is accepted and ignored, as the other Ult create calls already do. [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/00d087b2323bf377fd39b7163a256977383cb232/include/orbis/Ult.h) declares `sceUltQueueOptParamInitialize` and `sceUltQueueDataResourcePoolOptParamInitialize` without their structures; the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.h#L25-L78) defines both as a 16-byte reserved header followed by reserved fields, 128 bytes, with no settings, and [fpPS4](https://github.com/red-prig/fpPS4/blob/5ffbdbbd76b4da6e4b8721743ea491f494b28913/src/ps4_libsceult.pas#L142-L172) keeps them as 128 opaque bytes. That the PS5 structures carry no settings either is assumed from these, not verified -- [_sceUltWaitingQueueResourcePoolCreate, _sceUltSemaphoreCreate, _sceUltMutexOptParamInitialize, _sceUltMutexCreate, _sceUltQueueDataResourcePoolCreate and _sceUltQueueCreate](../../../core/libs/prx/libSceUlt/Export.cpp) (libSceUlt) forward to the exports without the underscore; that their argument lists match is inferred from the other underscore Ult exports, not measured - [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. The nested context in `UltUlthreadInfo` still requires reconstruction. - [sceUltMutexTryLock, the reader-writer lock and sceUltUlthreadGetSelf, sceUltUlthreadYield and sceUltUlthreadExit](../../../core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp) (libSceUlt) - the signatures follow the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.cpp) (a 256-byte lock and 128-byte option structures), not a PS5 binary. The rules are assumed: readers may take the lock while a writer waits, a thread that holds the write lock gets `ULT_ERROR_STATE` for any further lock instead of waiting on itself, a reader asking for the write lock waits on its own read lock, and the try forms return `ULT_ERROR_BUSY`. Outside an ulthread, `sceUltUlthreadGetSelf`, `sceUltUlthreadYield` and `sceUltUlthreadExit` return `ULT_ERROR_STATE`; inside one they act on the guest thread it runs on, since ulthreads are not scheduled cooperatively here From 0a603c963401038cfacba9d1acf8e57014f1af3e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:42:28 +0300 Subject: [PATCH 427/523] fix(ult): restore resource pool work area formulas --- core/libs/prx/libSceUlt/Export.cpp | 13 +++++-------- core/libs/tests/GuestUltQueue.cpp | 7 ++++++- docs/dev/debt/libSceUlt.md | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 8cae0b365c..3e77855736 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -443,8 +443,8 @@ int APS5_VABI sceUltConditionVariableWait(void* condition) { return ULT_OK; } -int APS5_VABI sceUltWaitingQueueResourcePoolGetWorkAreaSize(std::uint32_t numThreads, std::uint32_t numSyncObjects) { - return static_cast(alignUp(static_cast(numThreads + numSyncObjects) * 256u, 8u)); +std::uint64_t APS5_VABI sceUltWaitingQueueResourcePoolGetWorkAreaSize(std::uint32_t numThreads, std::uint32_t numSyncObjects) { + return 32ull * (numSyncObjects + 2u * numThreads + 2u); } int APS5_VABI sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name, std::uint32_t numThreads, std::uint32_t numSyncObjects, void* workArea, const void* optParam, std::uint32_t buildVersion) { @@ -501,14 +501,11 @@ int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void* pool) { } std::uint64_t APS5_VABI sceUltQueueDataResourcePoolGetWorkAreaSize(std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject) { - if (dataSize > UINT64_MAX - 7u) throw std::out_of_range("ULT queue data size overflow"); - const auto stride = alignUp(dataSize, 8u); - const auto queueArea = static_cast(numQueueObject) * 512u; - if (stride != 0 && numData > (UINT64_MAX - queueArea) / stride) { + const auto nodeArea = 32ull * (numQueueObject + 2u * numData + 3u); + if (dataSize != 0 && numData > (UINT64_MAX - nodeArea) / dataSize) { throw std::out_of_range("ULT queue work area size overflow"); } - const std::uint64_t dataArea = static_cast(numData) * stride; - return alignUp(dataArea + queueArea, 8u); + return static_cast(numData) * dataSize + nodeArea; } int APS5_VABI sceUltQueueDataResourcePoolCreate(void* pool, const char* name, std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject, void* waitingQueueResourcePool, void* workArea, const void* optParam, std::uint32_t buildVersion) { diff --git a/core/libs/tests/GuestUltQueue.cpp b/core/libs/tests/GuestUltQueue.cpp index 2a93f78c0c..91fba11ef9 100644 --- a/core/libs/tests/GuestUltQueue.cpp +++ b/core/libs/tests/GuestUltQueue.cpp @@ -13,6 +13,7 @@ extern "C" { int APS5_VABI sceUltInitialize(); int APS5_VABI sceUltFinalize(); +std::uint64_t APS5_VABI sceUltWaitingQueueResourcePoolGetWorkAreaSize(std::uint32_t, std::uint32_t); std::uint64_t APS5_VABI sceUltQueueDataResourcePoolGetWorkAreaSize(std::uint32_t, std::uint64_t, std::uint32_t); int APS5_VABI sceUltQueueDataResourcePoolCreate(void*, const char*, std::uint32_t, std::uint64_t, std::uint32_t, void*, void*, const void*, std::uint32_t); int APS5_VABI sceUltQueueDataResourcePoolDestroy(void*); @@ -93,7 +94,11 @@ static void Validation() { CHECK(sceUltQueueDataResourcePoolDestroy(&fixture.pool) == Busy); CHECK(sceUltQueueDataResourcePoolDestroy(nullptr) == Null); CHECK(sceUltQueueDestroy(nullptr) == Null); - CHECK(sceUltQueueDataResourcePoolGetWorkAreaSize(3, 9, 2) == 1072); + CHECK(sceUltQueueDataResourcePoolGetWorkAreaSize(3, 9, 2) == 379); + CHECK(sceUltQueueDataResourcePoolGetWorkAreaSize(0, 0, 0) == 96); + CHECK(sceUltWaitingQueueResourcePoolGetWorkAreaSize(0, 0) == 64); + CHECK(sceUltWaitingQueueResourcePoolGetWorkAreaSize(3, 2) == 320); + CHECK(sceUltWaitingQueueResourcePoolGetWorkAreaSize(0, 0x08000000u) == 0x100000040ull); bool overflow = false; try { sceUltQueueDataResourcePoolGetWorkAreaSize(1, UINT64_MAX, 1); } catch (const std::out_of_range&) { overflow = true; } diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index e20ac05764..3998c27c93 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -12,7 +12,7 @@ - [sceUltMutexTryLock, the reader-writer lock and sceUltUlthreadGetSelf, sceUltUlthreadYield and sceUltUlthreadExit](../../../core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp) (libSceUlt) - the signatures follow the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.cpp) (a 256-byte lock and 128-byte option structures), not a PS5 binary. The rules are assumed: readers may take the lock while a writer waits, a thread that holds the write lock gets `ULT_ERROR_STATE` for any further lock instead of waiting on itself, a reader asking for the write lock waits on its own read lock, and the try forms return `ULT_ERROR_BUSY`. Outside an ulthread, `sceUltUlthreadGetSelf`, `sceUltUlthreadYield` and `sceUltUlthreadExit` return `ULT_ERROR_STATE`; inside one they act on the guest thread it runs on, since ulthreads are not scheduled cooperatively here ### Functional -- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; its existing size formula and the 512-byte object layouts remain unverified. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. +- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; the guest object layouts remain incomplete. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. - [sceUltWaitingQueueResourcePoolDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) refuses with `ULT_ERROR_BUSY` only when a thread is currently waiting on a pool-backed mutex, semaphore or queue, matching the PS5 libSceUlt disassembly (0x473a in or55417wcDk); bound but idle objects do not block pool destruction. Waiting-queue pool guest work area node accounting is not implemented. - [sceUltMutexDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) returns `ULT_ERROR_BUSY` when the mutex is held or has waiting threads, tracked via owner and waiter counts in `UltMutexState`. Prosper (`hle_ult.cpp`) logs a destroy of a held mutex and returns `ULT_OK`; AnyPS5 returns `ULT_ERROR_BUSY` to prevent destroying active mutexes. From ab8265170090c9a4773d277b7167205b34edc5d0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:44:19 +0300 Subject: [PATCH 428/523] fix(ult): restore mutex option layout and validation --- core/libs/SceTypes.hpp | 10 +++++++--- core/libs/prx/libSceUlt/Export.cpp | 18 +++++++++++++++++- core/libs/tests/GuestUltThread.cpp | 25 +++++++++++++++++++++++-- docs/dev/debt/libSceUlt.md | 3 ++- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index b0d1fd063f..38b336e42c 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -2117,12 +2117,16 @@ struct UserServiceGamePresets { std::uint32_t audio_language; }; -struct UltMutexOptParam { - std::uint32_t reserved_header[2]; +struct alignas(8) UltMutexOptParam { + std::uint32_t header[3]; + std::uint32_t buildVersion; std::uint32_t attribute; - std::uint32_t reserved0; + std::uint32_t reserved[27]; }; +static_assert(sizeof(UltMutexOptParam) == 128); +static_assert(offsetof(UltMutexOptParam, attribute) == 16); + struct UltUlthreadRuntimeOptParam { std::uint8_t bytes[128]; }; using UltUlthreadEntry = std::int32_t (APS5_VABI *)(std::uint64_t); diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 3e77855736..b200b94561 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -188,11 +189,17 @@ int APS5_VABI sceUltFinalize() { } int APS5_VABI sceUltMutexOptParamInitialize(UltMutexOptParam* optParam, std::uint32_t buildVersion) { - (void)buildVersion; if (optParam == nullptr) { return ULT_ERROR_NULL; } + if ((reinterpret_cast(optParam) & 7u) != 0) { + return ULT_ERROR_ALIGNMENT; + } std::memset(optParam, 0, sizeof(*optParam)); + optParam->header[0] = 0x6C696275; + optParam->header[1] = 0x6C746D61; + optParam->header[2] = 0x67696300; + optParam->buildVersion = buildVersion; return ULT_OK; } @@ -886,6 +893,15 @@ int APS5_VABI _sceUltMutexOptParamInitialize(UltMutexOptParam* optParam, std::ui } int APS5_VABI _sceUltMutexCreate(void* mutex, const char* name, void* waitingQueueResourcePool, const UltMutexOptParam* optParam) { + if (mutex == nullptr || name == nullptr || waitingQueueResourcePool == nullptr) { + return ULT_ERROR_NULL; + } + if (((reinterpret_cast(mutex) | reinterpret_cast(optParam) | reinterpret_cast(waitingQueueResourcePool)) & 7u) != 0) { + return ULT_ERROR_ALIGNMENT; + } + if (optParam != nullptr && (optParam->header[0] != 0x6C696275 || optParam->header[1] != 0x6C746D61 || optParam->header[2] != 0x67696300 || optParam->attribute > 1 || std::any_of(std::begin(optParam->reserved), std::end(optParam->reserved), [](std::uint32_t value) { return value != 0; }))) { + return ULT_ERROR_INVALID; + } return sceUltMutexCreate(mutex, name, waitingQueueResourcePool, optParam, 0); } diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index b2d3cec977..ffa71f1597 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -47,6 +47,8 @@ int APS5_VABI sceUltGetConditionVariableInfo(void*, UltConditionVariableInfo*, U static constexpr int Ok = 0; static constexpr int Null = -2139029503; +static constexpr int Alignment = -2139029502; +static constexpr int Invalid = -2139029500; static constexpr int State = -2139029498; static constexpr int Busy = -2139029497; static constexpr int Again = -2139029496; @@ -181,12 +183,31 @@ static void UnderscoredCreate() { CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr) == State); CHECK(sceUltSemaphoreAcquire(semaphore, 2) == Ok); CHECK(sceUltSemaphoreRelease(semaphore, 2) == Ok); - alignas(8) static std::uint8_t mutexOptParam[128]; + alignas(8) static std::uint8_t mutexOptParam[136]; alignas(8) static std::uint8_t mutex[256]; std::memset(mutexOptParam, 0xFF, sizeof(mutexOptParam)); CHECK(_sceUltMutexOptParamInitialize(nullptr, 0) == Null); + CHECK(_sceUltMutexOptParamInitialize(mutexOptParam + 1, 0) == Alignment); + CHECK(mutexOptParam[0] == 0xFF && mutexOptParam[127] == 0xFF); + CHECK(_sceUltMutexOptParamInitialize(mutexOptParam, 0x12345678) == Ok); + UltMutexOptParam options{}; + std::memcpy(&options, mutexOptParam, sizeof(options)); + CHECK(options.header[0] == 0x6C696275 && options.header[1] == 0x6C746D61 && options.header[2] == 0x67696300); + CHECK(options.buildVersion == 0x12345678 && options.attribute == 0); + for (std::size_t index = 16; index < 128; ++index) CHECK(mutexOptParam[index] == 0); + for (std::size_t index = 128; index < sizeof(mutexOptParam); ++index) CHECK(mutexOptParam[index] == 0xFF); + CHECK(_sceUltMutexCreate(mutex, nullptr, pool, mutexOptParam) == Null); + CHECK(_sceUltMutexCreate(mutex, "mutex", nullptr, mutexOptParam) == Null); + CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam + 1) == Alignment); + mutexOptParam[127] = 1; + CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Invalid); + mutexOptParam[127] = 0; + mutexOptParam[16] = 2; + CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Invalid); + mutexOptParam[16] = 0; + mutexOptParam[0] = 0; + CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Invalid); CHECK(_sceUltMutexOptParamInitialize(mutexOptParam, 0) == Ok); - CHECK(mutexOptParam[8] == 0 && mutexOptParam[15] == 0); CHECK(_sceUltMutexCreate(nullptr, "mutex", pool, mutexOptParam) == Null); CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Ok); CHECK(sceUltMutexLock(mutex) == Ok); diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index 3998c27c93..7ec281e84d 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -4,7 +4,7 @@ - [Bound condition variables](../../../core/libs/prx/libSceUlt/Export.cpp) use the creation-time mutex, inferred from PPSA28061 callers of `_sceUltConditionVariableCreate`; the wait caller reloads only the condition argument. Error precedence, busy/state errors, FIFO signal selection and unsupported recursive waits have not been measured on PS5 hardware. Non-null options and waits at recursion depth above one throw. A live condition prevents destroying its bound mutex. Finalization follows the existing host mutex lifecycle: it invalidates conditions and wakes queued or reacquiring waiters with a state error. Scheduling priority and build-version validation are not implemented. -- [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. +- [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - Runtime option defaults still omit the native header, stack size, worker affinity and priority fields; affinity depends on unresolved process configuration queries. Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - the added `sceUltQueuePop`, `sceUltQueueTryPush`, `sceUltQueueDestroy` and `sceUltQueueDataResourcePoolDestroy` signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with `ULT_ERROR_BUSY` and otherwise discard queued data; pool exhaustion returns `ULT_ERROR_AGAIN`. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. - [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) - a non-null option parameter is accepted and ignored, as the other Ult create calls already do. [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/00d087b2323bf377fd39b7163a256977383cb232/include/orbis/Ult.h) declares `sceUltQueueOptParamInitialize` and `sceUltQueueDataResourcePoolOptParamInitialize` without their structures; the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.h#L25-L78) defines both as a 16-byte reserved header followed by reserved fields, 128 bytes, with no settings, and [fpPS4](https://github.com/red-prig/fpPS4/blob/5ffbdbbd76b4da6e4b8721743ea491f494b28913/src/ps4_libsceult.pas#L142-L172) keeps them as 128 opaque bytes. That the PS5 structures carry no settings either is assumed from these, not verified - [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. The nested context in `UltUlthreadInfo` still requires reconstruction. @@ -14,6 +14,7 @@ - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; the guest object layouts remain incomplete. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. - [sceUltWaitingQueueResourcePoolDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) refuses with `ULT_ERROR_BUSY` only when a thread is currently waiting on a pool-backed mutex, semaphore or queue, matching the PS5 libSceUlt disassembly (0x473a in or55417wcDk); bound but idle objects do not block pool destruction. Waiting-queue pool guest work area node accounting is not implemented. +- [sceUltMutexLock](../../../core/libs/prx/libSceUlt/Export.cpp) permits recursive acquisition regardless of the recovered option attribute; native recursion limits are not enforced. - [sceUltMutexDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) returns `ULT_ERROR_BUSY` when the mutex is held or has waiting threads, tracked via owner and waiter counts in `UltMutexState`. Prosper (`hle_ult.cpp`) logs a destroy of a held mutex and returns `ULT_OK`; AnyPS5 returns `ULT_ERROR_BUSY` to prevent destroying active mutexes. - The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`_sceUltConditionVariableOptParamInitialize`](../../../core/libs/prx/libSceUlt/Export.cpp). From 4c3c1345d7a08d0bf5ed0b5036598ff952066bdc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:45:12 +0300 Subject: [PATCH 429/523] fix(ult): restore runtime work area sizing --- core/libs/prx/libSceUlt/Export.cpp | 9 +++++---- core/libs/tests/GuestUltThread.cpp | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index b200b94561..6c21c1712d 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -35,9 +35,6 @@ std::unordered_map> gQueues; std::unordered_map gRuntimes; std::unordered_map> gUlthreads; -std::uint64_t alignUp(std::uint64_t value, std::uint64_t alignment) { - return (value + alignment - 1u) & ~(alignment - 1u); -} int semaphoreGetState(void* semaphore, std::shared_ptr* out) { std::lock_guard lock(gMutex); @@ -743,7 +740,11 @@ int APS5_VABI sceUltUlthreadRuntimeOptParamInitialize(UltUlthreadRuntimeOptParam } std::uint64_t APS5_VABI sceUltUlthreadRuntimeGetWorkAreaSize(std::uint32_t maxNumUlthread, std::uint32_t numWorkerThread) { - return alignUp(static_cast(maxNumUlthread) * 256u + static_cast(numWorkerThread) * 16u * 1024u, 8u); + const std::uint32_t totalThreads = maxNumUlthread + numWorkerThread; + const auto threadNodes = 32ull * (2u * maxNumUlthread + 4u); + const auto threadArea = (threadNodes + 4ull * maxNumUlthread + 7u) & 0x3FFFFFFFF8ull; + const auto schedulerArea = 32ull * (2u * numWorkerThread + 4u) + 32ull * (2u * totalThreads + 4u); + return threadArea + schedulerArea + 512ull * numWorkerThread + 8ull * (static_cast(maxNumUlthread) + totalThreads); } int APS5_VABI sceUltUlthreadRuntimeCreate(void* runtime, const char* name, std::uint32_t maxNumUlthread, std::uint32_t numWorkerThread, void* workArea, const void* optParam, std::uint32_t buildVersion) { diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index ffa71f1597..7aa43d4dbd 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -115,7 +115,7 @@ static void Validation() { std::int32_t status = 0; CHECK(sceUltUlthreadJoin(&thread, &status) == Ok && status == 0x27); CHECK(sceUltFinalize() == Ok); - CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(1, 1) == 256u + 16u * 1024u); + CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(1, 1) == 1184); CHECK(sceUltConditionVariableDestroy(nullptr) == -2139029503); CHECK(sceUltReaderWriterLockDestroy(nullptr) == ULT_ERROR_NULL); bool threwInfo = false; @@ -128,6 +128,10 @@ static void Validation() { } static void TryJoin() { + CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(0, 0) == 384); + CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(1, 1) == 1184); + CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(3, 2) == 2128); + CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(0x80000000u, 0) == 0xA00000180ull); Runtime runtime; Thread thread; std::uint8_t optParam[128]; From 574cf2943ae609d1db6956c6ece3c68ae3afe51f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:46:00 +0300 Subject: [PATCH 430/523] fix(ult): enforce mutex recursion and unlock ownership --- core/libs/prx/libSceUlt/Export.cpp | 8 +++++++- core/libs/prx/libSceUlt/SceUltTypes.hpp | 1 + core/libs/tests/GuestUltThread.cpp | 13 +++++++++++++ docs/dev/debt/libSceUlt.md | 3 --- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 6c21c1712d..ba2978be02 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -281,6 +281,9 @@ int APS5_VABI sceUltMutexLock(void* mutex) { return ULT_ERROR_STATE; } if (state->_owner == self) { + if (state->_attribute == 0) { + return ULT_ERROR_STATE; + } ++state->_lockCount; return ULT_OK; } @@ -341,9 +344,12 @@ int APS5_VABI sceUltMutexUnlock(void* mutex) { } const auto self = std::this_thread::get_id(); std::lock_guard lock(state->_mutex); - if (!state->_alive || state->_owner != self) { + if (!state->_alive) { return ULT_ERROR_STATE; } + if (state->_owner != self) { + return ULT_ERROR_PERMISSION; + } --state->_lockCount; if (state->_lockCount == 0) { state->_owner = {}; diff --git a/core/libs/prx/libSceUlt/SceUltTypes.hpp b/core/libs/prx/libSceUlt/SceUltTypes.hpp index e02d67f2b5..35fa6b302c 100644 --- a/core/libs/prx/libSceUlt/SceUltTypes.hpp +++ b/core/libs/prx/libSceUlt/SceUltTypes.hpp @@ -19,6 +19,7 @@ constexpr int ULT_ERROR_NULL = -2139029503; constexpr int ULT_ERROR_ALIGNMENT = -2139029502; constexpr int ULT_ERROR_RANGE = -2139029501; constexpr int ULT_ERROR_INVALID = -2139029500; +constexpr int ULT_ERROR_PERMISSION = -2139029499; constexpr int ULT_ERROR_STATE = -2139029498; constexpr int ULT_ERROR_BUSY = -2139029497; constexpr int ULT_ERROR_AGAIN = -2139029496; diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 7aa43d4dbd..0057ef4d0e 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -49,6 +49,7 @@ static constexpr int Ok = 0; static constexpr int Null = -2139029503; static constexpr int Alignment = -2139029502; static constexpr int Invalid = -2139029500; +static constexpr int Permission = -2139029499; static constexpr int State = -2139029498; static constexpr int Busy = -2139029497; static constexpr int Again = -2139029496; @@ -215,6 +216,18 @@ static void UnderscoredCreate() { CHECK(_sceUltMutexCreate(nullptr, "mutex", pool, mutexOptParam) == Null); CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Ok); CHECK(sceUltMutexLock(mutex) == Ok); + CHECK(sceUltMutexLock(mutex) == State); + std::thread foreignUnlock([&] { CHECK(sceUltMutexUnlock(mutex) == Permission); }); + foreignUnlock.join(); + CHECK(sceUltMutexUnlock(mutex) == Ok); + CHECK(sceUltMutexUnlock(mutex) == Permission); + CHECK(sceUltMutexDestroy(mutex) == Ok); + mutexOptParam[16] = 1; + CHECK(_sceUltMutexCreate(mutex, "mutex", pool, mutexOptParam) == Ok); + CHECK(sceUltMutexLock(mutex) == Ok); + CHECK(sceUltMutexLock(mutex) == Ok); + CHECK(sceUltMutexUnlock(mutex) == Ok); + CHECK(sceUltMutexDestroy(mutex) == Busy); CHECK(sceUltMutexUnlock(mutex) == Ok); alignas(8) static std::uint8_t dataPool[512]; alignas(8) static std::uint8_t queue[512]; diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index 7ec281e84d..3e8b94f62a 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -14,7 +14,4 @@ - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; the guest object layouts remain incomplete. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. - [sceUltWaitingQueueResourcePoolDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) refuses with `ULT_ERROR_BUSY` only when a thread is currently waiting on a pool-backed mutex, semaphore or queue, matching the PS5 libSceUlt disassembly (0x473a in or55417wcDk); bound but idle objects do not block pool destruction. Waiting-queue pool guest work area node accounting is not implemented. -- [sceUltMutexLock](../../../core/libs/prx/libSceUlt/Export.cpp) permits recursive acquisition regardless of the recovered option attribute; native recursion limits are not enforced. -- [sceUltMutexDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) returns `ULT_ERROR_BUSY` when the mutex is held or has waiting threads, tracked via owner and waiter counts in `UltMutexState`. Prosper (`hle_ult.cpp`) logs a destroy of a held mutex and returns `ULT_OK`; AnyPS5 returns `ULT_ERROR_BUSY` to prevent destroying active mutexes. - - The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`_sceUltConditionVariableOptParamInitialize`](../../../core/libs/prx/libSceUlt/Export.cpp). From a41a9615815782968d19afce1af4c018e8e2b664 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:49:36 +0300 Subject: [PATCH 431/523] fix(dialog): restore g2p and player review parameter contracts --- core/libs/prx/libSceCdlgG2P/Export.cpp | 55 ++++++++++--------- .../prx/libSceCdlgPlayerReview/Export.cpp | 18 +++--- .../prx/libSceCommonDialog/CommonDialog.hpp | 42 ++++++++++++++ core/libs/tests/GuestG2PDialog.cpp | 34 ++++++++++-- core/libs/tests/GuestPlayerReviewDialog.cpp | 46 ++++++++++++---- docs/dev/debt/libSceCdlgG2P.md | 2 +- docs/dev/debt/libSceCdlgPlayerReview.md | 2 +- 7 files changed, 147 insertions(+), 52 deletions(-) diff --git a/core/libs/prx/libSceCdlgG2P/Export.cpp b/core/libs/prx/libSceCdlgG2P/Export.cpp index 082b4bb331..8a50061006 100644 --- a/core/libs/prx/libSceCdlgG2P/Export.cpp +++ b/core/libs/prx/libSceCdlgG2P/Export.cpp @@ -2,7 +2,8 @@ #include #include #include -#include "SceTypes.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" namespace { @@ -12,7 +13,7 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); @@ -25,47 +26,47 @@ std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; extern "C" { int APS5_VABI sceG2PDialogInitialize(void) { - int expected = COMMON_DIALOG_STATUS_NONE; - if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; - return 0; + int expected = COMMON_DIALOG_STATUS_NONE; + if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; + return 0; } -int APS5_VABI sceG2PDialogOpen(const void* param) { - const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; - g_status = COMMON_DIALOG_STATUS_FINISHED; - return 0; +int APS5_VABI sceG2PDialogOpen(const G2PDialogParam* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; + if (!ValidateCommonDialogBaseParam(param->base) || param->size != sizeof(*param) || param->userId == -1 || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + g_status = COMMON_DIALOG_STATUS_FINISHED; + return 0; } int APS5_VABI sceG2PDialogUpdateStatus(void) { - return g_status.load(); + return g_status.load(); } int APS5_VABI sceG2PDialogGetStatus(void) { - return g_status.load(); + return g_status.load(); } -int APS5_VABI sceG2PDialogGetResult(void* result) { - const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; - if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &canceled, sizeof(canceled)); - return 0; +int APS5_VABI sceG2PDialogGetResult(CommonDialogResultPrefix* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + return 0; } int APS5_VABI sceG2PDialogClose(void) { - if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - g_status = COMMON_DIALOG_STATUS_FINISHED; - return 0; + if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + g_status = COMMON_DIALOG_STATUS_FINISHED; + return 0; } int APS5_VABI sceG2PDialogTerminate(void) { - if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - return 0; + if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + return 0; } } diff --git a/core/libs/prx/libSceCdlgPlayerReview/Export.cpp b/core/libs/prx/libSceCdlgPlayerReview/Export.cpp index 2e454c8741..d63a6ad02b 100644 --- a/core/libs/prx/libSceCdlgPlayerReview/Export.cpp +++ b/core/libs/prx/libSceCdlgPlayerReview/Export.cpp @@ -1,6 +1,7 @@ #include #include -#include "SceTypes.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" namespace { @@ -10,8 +11,8 @@ constexpr int COMMON_DIALOG_STATUS_NONE = 0; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); @@ -35,11 +36,12 @@ int APS5_VABI scePlayerReviewDialogTerminate(void) { return COMMON_DIALOG_ERROR_NOT_RUNNING; } -int APS5_VABI scePlayerReviewDialogOpen(const void* param) { +int APS5_VABI scePlayerReviewDialogOpen(const PlayerReviewDialogParam* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base) || param->size != sizeof(*param) || param->userId == -1 || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; }) || param->mode > 1) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -58,13 +60,13 @@ int APS5_VABI scePlayerReviewDialogUpdateStatus(void) { return g_status.load(); } -int APS5_VABI scePlayerReviewDialogGetResult(void* result) { +int APS5_VABI scePlayerReviewDialogGetResult(CommonDialogResultPrefix* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - *static_cast(result) = COMMON_DIALOG_RESULT_USER_CANCELED; - return 0; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + return COMMON_DIALOG_RESULT_USER_CANCELED; } } diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index c9eb03df55..b4d577c0db 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -1,6 +1,48 @@ #ifndef CORE_LIBS_PRX_LIBSCECOMMONDIALOG_COMMONDIALOG_HPP #define CORE_LIBS_PRX_LIBSCECOMMONDIALOG_COMMONDIALOG_HPP +#include +#include +#include +#include + +struct CommonDialogBaseParam { + std::uint64_t size; + std::uint8_t reserved[36]; + std::uint32_t magic; +}; + +struct G2PDialogParam { + CommonDialogBaseParam base; + std::int32_t userId; + std::uint32_t padding; + std::uint64_t size; + std::uint8_t reserved[64]; +}; + +struct PlayerReviewDialogParam { + CommonDialogBaseParam base; + std::uint64_t size; + std::int32_t userId; + std::uint8_t target[40]; + std::uint32_t mode; + std::uint8_t reserved[64]; +}; + +struct CommonDialogResultPrefix { + std::uint8_t untouched[8]; + std::int32_t result; +}; + +static_assert(sizeof(CommonDialogBaseParam) == 48); +static_assert(sizeof(G2PDialogParam) == 128); +static_assert(sizeof(PlayerReviewDialogParam) == 168); +static_assert(offsetof(CommonDialogResultPrefix, result) == 8); + +inline bool ValidateCommonDialogBaseParam(const CommonDialogBaseParam& param) { + return param.size == sizeof(param) && param.magic == static_cast(reinterpret_cast(¶m)) - 1060003575u && std::all_of(std::begin(param.reserved), std::end(param.reserved), [](std::uint8_t value) { return value == 0; }); +} + constexpr int COMMON_DIALOG_OK = 0; constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004); diff --git a/core/libs/tests/GuestG2PDialog.cpp b/core/libs/tests/GuestG2PDialog.cpp index b3e0cb2b85..0afa1a12e7 100644 --- a/core/libs/tests/GuestG2PDialog.cpp +++ b/core/libs/tests/GuestG2PDialog.cpp @@ -1,5 +1,6 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include extern "C" { @@ -28,13 +29,19 @@ constexpr int kResultUserCanceled = 1; } int main() { - std::int64_t param = 0; - std::int32_t result[2] = {-1, -1}; + G2PDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = 1; + std::int32_t result[4] = {-1, -1, -1, -1}; Require(sceG2PDialogTerminate() == kErrNotInitialized); Require(sceG2PDialogOpen(¶m) == kErrNotInitialized); Require(sceG2PDialogClose() == kErrNotInitialized); Require(sceG2PDialogGetResult(result) == kErrNotInitialized); + Require(sceG2PDialogOpen(nullptr) == static_cast(0x80B8000Du)); + Require(sceG2PDialogGetResult(nullptr) == static_cast(0x80B8000Du)); Require(sceG2PDialogGetStatus() == kStatusNone); Require(sceG2PDialogUpdateStatus() == kStatusNone); @@ -46,13 +53,29 @@ int main() { Require(sceG2PDialogOpen(nullptr) == kErrArgNull); Require(sceG2PDialogGetResult(result) == kErrNotFinished); + param.base.size = 0; + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.base.size = sizeof(param.base); + param.base.magic = 0; + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = 0; + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.size = sizeof(param); + param.reserved[63] = 1; + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.reserved[63] = 0; + param.userId = -1; + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.userId = 1; Require(sceG2PDialogOpen(¶m) == 0); Require(sceG2PDialogGetStatus() == kStatusFinished); Require(sceG2PDialogUpdateStatus() == kStatusFinished); Require(sceG2PDialogGetResult(nullptr) == kErrArgNull); Require(sceG2PDialogGetResult(result) == 0); - Require(result[0] == kResultUserCanceled); + Require(result[0] == -1 && result[1] == -1 && result[3] == -1); + Require(result[2] == kResultUserCanceled); Require(result[1] == -1); Require(sceG2PDialogClose() == 0); @@ -64,8 +87,9 @@ int main() { Require(sceG2PDialogGetStatus() == kStatusInitialized); Require(sceG2PDialogClose() == 0); Require(sceG2PDialogGetStatus() == kStatusFinished); - result[0] = -1; + result[2] = -1; Require(sceG2PDialogGetResult(result) == 0); - Require(result[0] == kResultUserCanceled); + Require(result[0] == -1 && result[1] == -1 && result[3] == -1); + Require(result[2] == kResultUserCanceled); Require(sceG2PDialogTerminate() == 0); } diff --git a/core/libs/tests/GuestPlayerReviewDialog.cpp b/core/libs/tests/GuestPlayerReviewDialog.cpp index c7917268a4..4c97608a99 100644 --- a/core/libs/tests/GuestPlayerReviewDialog.cpp +++ b/core/libs/tests/GuestPlayerReviewDialog.cpp @@ -1,5 +1,6 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include extern "C" { @@ -18,7 +19,6 @@ constexpr int COMMON_DIALOG_STATUS_NONE = 0; constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); @@ -28,11 +28,17 @@ void Require(bool value) { if (!value) std::abort(); } } int main() { - std::uint8_t param[64] = {}; - std::int32_t result[8] = {}; + PlayerReviewDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = 1; + std::int32_t result[4] = {-1, -1, -1, -1}; Require(scePlayerReviewDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); + Require(scePlayerReviewDialogOpen(nullptr) == static_cast(0x80B8000Du)); + Require(scePlayerReviewDialogGetResult(nullptr) == static_cast(0x80B8000Du)); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); - Require(scePlayerReviewDialogOpen(param) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); + Require(scePlayerReviewDialogOpen(¶m) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(scePlayerReviewDialogClose() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(scePlayerReviewDialogGetResult(result) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); @@ -48,12 +54,31 @@ int main() { Require(scePlayerReviewDialogInitialize() == 0); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_INITIALIZED); Require(scePlayerReviewDialogOpen(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); - Require(scePlayerReviewDialogOpen(param) == 0); + param.base.size = 0; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.base.size = sizeof(param.base); + param.base.magic = 0; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = 0; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.size = sizeof(param); + param.reserved[63] = 1; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.reserved[63] = 0; + param.userId = -1; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.userId = 1; + param.mode = 2; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.mode = 0; + Require(scePlayerReviewDialogOpen(¶m) == 0); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogGetResult(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); - Require(scePlayerReviewDialogGetResult(result) == 0); - Require(result[0] == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(scePlayerReviewDialogGetResult(result) == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result[0] == -1 && result[1] == -1 && result[3] == -1); + Require(result[2] == COMMON_DIALOG_RESULT_USER_CANCELED); Require(scePlayerReviewDialogClose() == 0); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); @@ -64,9 +89,10 @@ int main() { Require(scePlayerReviewDialogClose() == 0); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); - result[0] = -1; - Require(scePlayerReviewDialogGetResult(result) == 0); - Require(result[0] == COMMON_DIALOG_RESULT_USER_CANCELED); + result[2] = -1; + Require(scePlayerReviewDialogGetResult(result) == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result[0] == -1 && result[1] == -1 && result[3] == -1); + Require(result[2] == COMMON_DIALOG_RESULT_USER_CANCELED); Require(scePlayerReviewDialogTerminate() == 0); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_NONE); } diff --git a/docs/dev/debt/libSceCdlgG2P.md b/docs/dev/debt/libSceCdlgG2P.md index 2549e5ceee..f35f20d05d 100644 --- a/docs/dev/debt/libSceCdlgG2P.md +++ b/docs/dev/debt/libSceCdlgG2P.md @@ -2,4 +2,4 @@ ### Silent stubs -- [libSceCdlgG2P](../../../core/libs/prx/libSceCdlgG2P/Export.cpp) - the G2P dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. The signatures (`Open` and `GetResult` take a pointer, the others none) and the leading `int32` result are assumed from the other common dialogs, not verified for G2P; PPSA01289 imports it +- [libSceCdlgG2P](../../../core/libs/prx/libSceCdlgG2P/Export.cpp) - the G2P dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. The 128-byte open parameters, common-header validation and result field at byte 8 are recovered. Active-user verification and native service state transitions remain unimplemented; PPSA01289 imports it diff --git a/docs/dev/debt/libSceCdlgPlayerReview.md b/docs/dev/debt/libSceCdlgPlayerReview.md index 98ff1e48e6..2c193e11f2 100644 --- a/docs/dev/debt/libSceCdlgPlayerReview.md +++ b/docs/dev/debt/libSceCdlgPlayerReview.md @@ -2,7 +2,7 @@ ### Unknown function info -- [scePlayerReviewDialogOpen, scePlayerReviewDialogGetResult](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) (libSceCdlgPlayerReview) - the PS5-only dialog has no public reference: the parameter is not read and the result is assumed to start with the 32-bit common dialog result, as `sceWebBrowserDialogGetResult` assumes +- [scePlayerReviewDialogOpen](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) uses a recovered 168-byte parameter layout; the 40-byte target record at byte 60 and mode semantics remain unresolved. Active-user verification is not implemented. - [scePlayerReviewDialogClose](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) (libSceCdlgPlayerReview) - `scePlayerReviewDialogClose` moves an initialized dialog straight to `COMMON_DIALOG_STATUS_FINISHED` even when called before Open, as Signin, NetCtlAp and PlayerSelection do; MsgDialog.native, SaveDataDialog.native and ErrorDialog return an error there instead. Unverified against real hardware ### Functional From 5e43f87d5945693d48224933e85311e42dbca657 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:50:10 +0300 Subject: [PATCH 432/523] fix(dialog): reject closing inactive g2p and player review dialogs --- core/libs/prx/libSceCdlgG2P/Export.cpp | 5 ++++- core/libs/prx/libSceCdlgPlayerReview/Export.cpp | 4 +++- core/libs/tests/GuestG2PDialog.cpp | 10 +++++----- core/libs/tests/GuestPlayerReviewDialog.cpp | 12 ++++++------ docs/dev/debt/libSceCdlgPlayerReview.md | 1 - 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core/libs/prx/libSceCdlgG2P/Export.cpp b/core/libs/prx/libSceCdlgG2P/Export.cpp index 8a50061006..6f402b651d 100644 --- a/core/libs/prx/libSceCdlgG2P/Export.cpp +++ b/core/libs/prx/libSceCdlgG2P/Export.cpp @@ -16,6 +16,7 @@ constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); +constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; @@ -59,7 +60,9 @@ int APS5_VABI sceG2PDialogGetResult(CommonDialogResultPrefix* result) { } int APS5_VABI sceG2PDialogClose(void) { - if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/prx/libSceCdlgPlayerReview/Export.cpp b/core/libs/prx/libSceCdlgPlayerReview/Export.cpp index d63a6ad02b..2380fd4630 100644 --- a/core/libs/prx/libSceCdlgPlayerReview/Export.cpp +++ b/core/libs/prx/libSceCdlgPlayerReview/Export.cpp @@ -47,7 +47,9 @@ int APS5_VABI scePlayerReviewDialogOpen(const PlayerReviewDialogParam* param) { } int APS5_VABI scePlayerReviewDialogClose(void) { - if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestG2PDialog.cpp b/core/libs/tests/GuestG2PDialog.cpp index 0afa1a12e7..9a655706a9 100644 --- a/core/libs/tests/GuestG2PDialog.cpp +++ b/core/libs/tests/GuestG2PDialog.cpp @@ -78,18 +78,18 @@ int main() { Require(result[2] == kResultUserCanceled); Require(result[1] == -1); - Require(sceG2PDialogClose() == 0); + Require(sceG2PDialogClose() == static_cast(0x80B8000Bu)); Require(sceG2PDialogTerminate() == 0); Require(sceG2PDialogTerminate() == kErrNotInitialized); Require(sceG2PDialogGetStatus() == kStatusNone); Require(sceG2PDialogInitialize() == 0); Require(sceG2PDialogGetStatus() == kStatusInitialized); - Require(sceG2PDialogClose() == 0); - Require(sceG2PDialogGetStatus() == kStatusFinished); + Require(sceG2PDialogClose() == static_cast(0x80B8000Bu)); + Require(sceG2PDialogGetStatus() == kStatusInitialized); result[2] = -1; - Require(sceG2PDialogGetResult(result) == 0); + Require(sceG2PDialogGetResult(result) == kErrNotFinished); Require(result[0] == -1 && result[1] == -1 && result[3] == -1); - Require(result[2] == kResultUserCanceled); + Require(result[2] == -1); Require(sceG2PDialogTerminate() == 0); } diff --git a/core/libs/tests/GuestPlayerReviewDialog.cpp b/core/libs/tests/GuestPlayerReviewDialog.cpp index 4c97608a99..cb6c65a423 100644 --- a/core/libs/tests/GuestPlayerReviewDialog.cpp +++ b/core/libs/tests/GuestPlayerReviewDialog.cpp @@ -79,20 +79,20 @@ int main() { Require(scePlayerReviewDialogGetResult(result) == COMMON_DIALOG_RESULT_USER_CANCELED); Require(result[0] == -1 && result[1] == -1 && result[3] == -1); Require(result[2] == COMMON_DIALOG_RESULT_USER_CANCELED); - Require(scePlayerReviewDialogClose() == 0); + Require(scePlayerReviewDialogClose() == static_cast(0x80B8000Bu)); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogTerminate() == 0); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); Require(scePlayerReviewDialogInitialize() == 0); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_INITIALIZED); - Require(scePlayerReviewDialogClose() == 0); - Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); - Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); + Require(scePlayerReviewDialogClose() == static_cast(0x80B8000Bu)); + Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_INITIALIZED); + Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_INITIALIZED); result[2] = -1; - Require(scePlayerReviewDialogGetResult(result) == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(scePlayerReviewDialogGetResult(result) == COMMON_DIALOG_ERROR_NOT_FINISHED); Require(result[0] == -1 && result[1] == -1 && result[3] == -1); - Require(result[2] == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result[2] == -1); Require(scePlayerReviewDialogTerminate() == 0); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_NONE); } diff --git a/docs/dev/debt/libSceCdlgPlayerReview.md b/docs/dev/debt/libSceCdlgPlayerReview.md index 2c193e11f2..ae7ee87b57 100644 --- a/docs/dev/debt/libSceCdlgPlayerReview.md +++ b/docs/dev/debt/libSceCdlgPlayerReview.md @@ -3,7 +3,6 @@ ### Unknown function info - [scePlayerReviewDialogOpen](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) uses a recovered 168-byte parameter layout; the 40-byte target record at byte 60 and mode semantics remain unresolved. Active-user verification is not implemented. -- [scePlayerReviewDialogClose](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) (libSceCdlgPlayerReview) - `scePlayerReviewDialogClose` moves an initialized dialog straight to `COMMON_DIALOG_STATUS_FINISHED` even when called before Open, as Signin, NetCtlAp and PlayerSelection do; MsgDialog.native, SaveDataDialog.native and ErrorDialog return an error there instead. Unverified against real hardware ### Functional From 024dddc6f41729e54bb9964d89f9d347096c7a70 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:53:04 +0300 Subject: [PATCH 433/523] fix(avplayer): restore extended start position parameters --- core/libs/prx/libSceAvPlayer/Export.cpp | 7 +++---- .../libs/prx/libSceAvPlayer/include/AvPlayer.hpp | 13 ++++++++++--- core/libs/prx/libSceAvPlayer/src/Player.cpp | 8 ++++---- core/libs/prx/libSceAvPlayer/src/Source.cpp | 5 +++-- core/libs/tests/GuestAvPlayer.cpp | 16 ++++++++++++++-- docs/dev/debt/libSceAvPlayer.md | 1 - 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceAvPlayer/Export.cpp b/core/libs/prx/libSceAvPlayer/Export.cpp index 2cdb89887d..93d3d8fb0f 100644 --- a/core/libs/prx/libSceAvPlayer/Export.cpp +++ b/core/libs/prx/libSceAvPlayer/Export.cpp @@ -175,10 +175,9 @@ int APS5_VABI sceAvPlayerStart(AvPlayerInternal* h) { return ToPlayer(h)->Start(); } -int APS5_VABI sceAvPlayerStartEx(AvPlayerInternal* h, const void* start_info_ex) { - (void)start_info_ex; - if (!h) return SCE_AVPLAYER_ERROR_INVALID_PARAMS; - return ToPlayer(h)->Start(); +int APS5_VABI sceAvPlayerStartEx(AvPlayerInternal* handle, const StartInfo* startInfo) { + if (handle == nullptr || startInfo == nullptr || startInfo->size != sizeof(*startInfo)) return SCE_AVPLAYER_ERROR_INVALID_PARAMS; + return ToPlayer(handle)->Start(startInfo->startTime); } int APS5_VABI sceAvPlayerStop(AvPlayerInternal* h) { diff --git a/core/libs/prx/libSceAvPlayer/include/AvPlayer.hpp b/core/libs/prx/libSceAvPlayer/include/AvPlayer.hpp index 49bf029391..1c82453073 100644 --- a/core/libs/prx/libSceAvPlayer/include/AvPlayer.hpp +++ b/core/libs/prx/libSceAvPlayer/include/AvPlayer.hpp @@ -51,6 +51,13 @@ public: virtual void OnError() = 0; }; +struct StartInfo { + std::uint64_t size; + std::uint64_t startTime; +}; + +static_assert(sizeof(StartInfo) == 16); + struct SourceSettings { AvPlayerMemAllocator memory; AvPlayerFileReplacement file; @@ -66,7 +73,7 @@ public: virtual bool EnableStream(std::uint32_t index) = 0; virtual bool DisableStream(std::uint32_t index) = 0; virtual bool ChangeStream(std::uint32_t from, std::uint32_t to) = 0; - virtual int Start() = 0; + virtual int Start(std::uint64_t startTime) = 0; virtual void Stop() = 0; virtual void Pause() = 0; virtual void Resume() = 0; @@ -103,7 +110,7 @@ public: int EnableStream(std::uint32_t index); int DisableStream(std::uint32_t index); int ChangeStream(std::uint32_t from, std::uint32_t to); - int Start(); + int Start(std::uint64_t startTime = 0); int Stop(); int Pause(); int Resume(); @@ -136,7 +143,7 @@ private: void autoStart(); void checkEndOfFile(); bool readyLocked() const; - int startLocked(); + int startLocked(std::uint64_t startTime = 0); int stopLocked(); AvPlayerFileReplacement file{}; diff --git a/core/libs/prx/libSceAvPlayer/src/Player.cpp b/core/libs/prx/libSceAvPlayer/src/Player.cpp index 191a185847..90c3b57dba 100644 --- a/core/libs/prx/libSceAvPlayer/src/Player.cpp +++ b/core/libs/prx/libSceAvPlayer/src/Player.cpp @@ -265,15 +265,15 @@ int Player::ChangeStream(std::uint32_t from, std::uint32_t to) { return SCE_OK; } -int Player::Start() { +int Player::Start(std::uint64_t startTime) { std::lock_guard lock(mutex); - return startLocked(); + return startLocked(startTime); } -int Player::startLocked() { +int Player::startLocked(std::uint64_t startTime) { if (!source) return SCE_AVPLAYER_ERROR_OPERATION_FAILED; if (state != State::Ready && state != State::Stop && stopLocked() != SCE_OK) return SCE_AVPLAYER_ERROR_OPERATION_FAILED; - const int result = source->Start(); + const int result = source->Start(startTime); if (result != SCE_OK) { previous = state; state = State::Error; diff --git a/core/libs/prx/libSceAvPlayer/src/Source.cpp b/core/libs/prx/libSceAvPlayer/src/Source.cpp index 70f7e7fd31..4d31c38f1d 100644 --- a/core/libs/prx/libSceAvPlayer/src/Source.cpp +++ b/core/libs/prx/libSceAvPlayer/src/Source.cpp @@ -327,7 +327,8 @@ public: return true; } - int Start() override { + int Start(std::uint64_t startTime) override { + if (duration != 0 && startTime > duration) return SCE_AVPLAYER_ERROR_OPERATION_FAILED; if (video.stream < 0 && audio.stream < 0) return SCE_AVPLAYER_ERROR_OPERATION_FAILED; for (auto* decoder : {&video, &audio}) { if (decoder->stream < 0) continue; @@ -338,7 +339,7 @@ public: } } addVideoDecodeAheadBuffers(); - const auto start = startOffset; + const auto start = startTime != 0 ? startTime : startOffset; startOffset = 0; seek(start); { diff --git a/core/libs/tests/GuestAvPlayer.cpp b/core/libs/tests/GuestAvPlayer.cpp index efa5ff1e00..59250e3be1 100644 --- a/core/libs/tests/GuestAvPlayer.cpp +++ b/core/libs/tests/GuestAvPlayer.cpp @@ -21,6 +21,7 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceAvPlayer/include/AvPlayer.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libSceAvPlayer/include/AvPlayer.hpp" @@ -37,7 +38,7 @@ int APS5_VABI sceAvPlayerEnableStream(AvPlayerInternal*, std::uint32_t); int APS5_VABI sceAvPlayerChangeStream(AvPlayerInternal*, std::uint32_t, std::uint32_t); int APS5_VABI sceAvPlayerSetAvailableBandwidth(AvPlayerInternal*, std::uint32_t, std::uint32_t, std::uint32_t); int APS5_VABI sceAvPlayerStart(AvPlayerInternal*); -int APS5_VABI sceAvPlayerStartEx(AvPlayerInternal*, const void*); +int APS5_VABI sceAvPlayerStartEx(AvPlayerInternal*, const AvPlayer::StartInfo*); int APS5_VABI sceAvPlayerStop(AvPlayerInternal*); int APS5_VABI sceAvPlayerPause(AvPlayerInternal*); int APS5_VABI sceAvPlayerResume(AvPlayerInternal*); @@ -1072,11 +1073,22 @@ void TestExtendedExports() { CheckStreamInfoEx(player); Check(sceAvPlayerEnableStream(player, VideoStream) == 0, "enable video failed"); Check(sceAvPlayerStartEx(nullptr, nullptr) == InvalidParams, "start accepted a null player"); - Check(sceAvPlayerStartEx(player, nullptr) == 0, "extended start failed"); + Check(sceAvPlayerStartEx(player, nullptr) == InvalidParams, "start accepted null parameters"); + AvPlayer::StartInfo startInfo{0, 700}; + Check(sceAvPlayerStartEx(player, &startInfo) == InvalidParams, "start accepted an invalid parameter size"); + startInfo.size = sizeof(startInfo); + Check(sceAvPlayerStartEx(player, &startInfo) == 0, "extended start failed"); Check(WaitFor([&] { return events.Seen(EventPlay); }), "play event missing"); AvPlayerFrameInfoEx frame{}; Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame after the extended start"); CheckVideoFrame(frame); + Check(frame.timestamp >= 700 && frame.timestamp <= 767, "extended start ignored the requested position"); + Check(!events.Seen(EventWarning, JumpComplete), "extended start reported a jump"); + Check(sceAvPlayerStop(player) == 0, "extended stop failed"); + startInfo.startTime = 0; + Check(sceAvPlayerStartEx(player, &startInfo) == 0, "extended restart failed"); + Check(WaitFor([&] { return sceAvPlayerGetVideoDataEx(player, &frame) != 0; }), "no frame after extended restart"); + Check(frame.timestamp <= 67, "zero-position start did not restart playback"); Check(sceAvPlayerClose(player) == 0, "close failed"); } diff --git a/docs/dev/debt/libSceAvPlayer.md b/docs/dev/debt/libSceAvPlayer.md index 5a2f1941ed..fb24c0d166 100644 --- a/docs/dev/debt/libSceAvPlayer.md +++ b/docs/dev/debt/libSceAvPlayer.md @@ -10,7 +10,6 @@ - [AvPlayer loop warnings](../../../core/libs/prx/libSceAvPlayer/src/Source.cpp) are queued when the first audio or video frame of a loop is delivered, following [KytyPS5's playback boundary tracking](https://github.com/KytyPS5/KytyPS5/blob/a6f4e8e7f456b8697d3102823d3f16400c3e121e/src/libs/avPlayer.cpp#L1038-L1043). The console's exact callback timing is unverified. Decode-ahead does not announce a loop while the caller is consuming the previous pass. - [sceAvPlayerAddSource](../../../core/libs/prx/libSceAvPlayer/src/Source.cpp) (libSceAvPlayer) - requests up to 4 additional decode-ahead video framebuffers from the memory replacement beyond `num_output_video_framebuffers` when memory is available; the console's internal buffering behaviour is unknown -- [sceAvPlayerStartEx](../../../core/libs/prx/libSceAvPlayer/Export.cpp) (libSceAvPlayer) - start info layout unknown; it is ignored and playback starts as with `sceAvPlayerStart` - [sceAvPlayerInit / sceAvPlayerInitEx](../../../core/libs/prx/libSceAvPlayer/src/Player.cpp) (libSceAvPlayer) - behaviour without a memory replacement unknown; frame and sample buffers then come from the guest heap - [sceAvPlayerInitEx](../../../core/libs/prx/libSceAvPlayer/Export.cpp) (libSceAvPlayer) - the PS5 init data layout (`this_size` 0x230: auto start at 0x74, nine 0x30-byte thread parameter blocks from 0x78, output video frame buffer count at 0x228) follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/avPlayer.cpp), [prosper](https://github.com/mattias800/prosper/blob/main/prosper/src/hle/video/avplayer.cpp), [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Media/AvPlayer.cs) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/AvPlayer/AvPlayerExports.cs), which agree on it. PPSA25063 fills the same fields, writes auto start 0 and starts playback with `sceAvPlayerStart`; a title that sets auto start has not been seen. A smaller `this_size` is read with the PS4 offsets. The thread parameters are ignored - [AvPlayer auto start](../../../core/libs/prx/libSceAvPlayer/src/Player.cpp) (libSceAvPlayer) - the auto start that `sceAvPlayerAddSource` queues for a player with auto start or without an event callback starts the player only while it is still ready: a title that starts the player itself before the auto start runs keeps its playback and the frames it holds. Not confirmed on a title. From 3f3944a0ae7ccbed72692eb576bb1092fa018df1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:54:05 +0300 Subject: [PATCH 434/523] fix(ult): restore active object destruction error codes --- core/libs/prx/libSceUlt/Export.cpp | 10 +++++----- core/libs/tests/GuestUltQueue.cpp | 10 +++++----- core/libs/tests/GuestUltThread.cpp | 8 ++++---- docs/dev/debt/libSceUlt.md | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index ba2978be02..b2d575d929 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -258,7 +258,7 @@ int APS5_VABI sceUltMutexDestroy(void* mutex) { auto state = it->second; std::lock_guard stateLock(state->_mutex); if (state->_lockCount != 0 || state->_waiters != 0 || state->_conditions != 0 || state->_conditionWaiters != 0) { - return ULT_ERROR_BUSY; + return ULT_ERROR_STATE; } state->_alive = false; gMutexes.erase(it); @@ -483,7 +483,7 @@ int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void* pool) { if (entry.second->_waitingPool == pool) { std::lock_guard stateLock(entry.second->_mutex); if (entry.second->_waiters != 0) { - return ULT_ERROR_BUSY; + return ULT_ERROR_STATE; } } } @@ -491,7 +491,7 @@ int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void* pool) { if (entry.second->_waitingPool == pool) { std::lock_guard stateLock(entry.second->_mutex); if (entry.second->_waiters != 0) { - return ULT_ERROR_BUSY; + return ULT_ERROR_STATE; } } } @@ -499,7 +499,7 @@ int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void* pool) { if (entry.second->_pool && entry.second->_pool->_waitingPool == pool) { std::lock_guard poolLock(entry.second->_pool->_mutex); if (entry.second->_waiters != 0) { - return ULT_ERROR_BUSY; + return ULT_ERROR_STATE; } } } @@ -618,7 +618,7 @@ int APS5_VABI sceUltQueueDestroy(void* queue) { auto& pool = *state->_pool; { std::lock_guard stateLock(pool._mutex); - if (state->_waiters != 0) return ULT_ERROR_BUSY; + if (state->_waiters != 0) return ULT_ERROR_STATE; ReleaseQueue(*state); gQueues.erase(it); } diff --git a/core/libs/tests/GuestUltQueue.cpp b/core/libs/tests/GuestUltQueue.cpp index 91fba11ef9..ffce3c241f 100644 --- a/core/libs/tests/GuestUltQueue.cpp +++ b/core/libs/tests/GuestUltQueue.cpp @@ -156,13 +156,13 @@ static void Blocking() { CHECK(sceUltQueuePush(&fixture.first, &first) == 0); auto producer = std::async(std::launch::async, [&] { return sceUltQueuePush(&fixture.first, &second); }); WaitUntilBlocked(producer); - CHECK(sceUltQueueDestroy(&fixture.first) == Busy); + CHECK(sceUltQueueDestroy(&fixture.first) == State); CHECK(sceUltQueuePop(&fixture.first, &output) == 0 && output == first); CHECK(producer.get() == 0); CHECK(sceUltQueuePop(&fixture.first, &output) == 0 && output == second); auto consumer = std::async(std::launch::async, [&] { return sceUltQueuePop(&fixture.first, &output); }); WaitUntilBlocked(consumer); - CHECK(sceUltQueueDestroy(&fixture.first) == Busy); + CHECK(sceUltQueueDestroy(&fixture.first) == State); CHECK(sceUltQueuePush(&fixture.first, &first) == 0); CHECK(consumer.get() == 0 && output == first); fixture.Destroy(); @@ -180,7 +180,7 @@ static void SharedPool() { CHECK(sceUltQueueTryPop(&fixture.second, &output) == Again); auto producer = std::async(std::launch::async, [&] { return sceUltQueuePush(&fixture.second, &second); }); WaitUntilBlocked(producer); - CHECK(sceUltQueueDestroy(&fixture.second) == Busy); + CHECK(sceUltQueueDestroy(&fixture.second) == State); CHECK(sceUltQueueDestroy(&fixture.first) == 0); CHECK(producer.get() == 0); CHECK(consumer.get() == 0 && output == second); @@ -201,8 +201,8 @@ static void Finalize() { auto consumer = std::async(std::launch::async, [&] { return sceUltQueuePop(&fixture.second, &output); }); WaitUntilBlocked(producer); WaitUntilBlocked(consumer); - CHECK(sceUltQueueDestroy(&fixture.first) == Busy); - CHECK(sceUltQueueDestroy(&fixture.second) == Busy); + CHECK(sceUltQueueDestroy(&fixture.first) == State); + CHECK(sceUltQueueDestroy(&fixture.second) == State); CHECK(sceUltFinalize() == 0); CHECK(producer.get() == State); CHECK(consumer.get() == State && output == 123); diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 0057ef4d0e..70df002c12 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -227,7 +227,7 @@ static void UnderscoredCreate() { CHECK(sceUltMutexLock(mutex) == Ok); CHECK(sceUltMutexLock(mutex) == Ok); CHECK(sceUltMutexUnlock(mutex) == Ok); - CHECK(sceUltMutexDestroy(mutex) == Busy); + CHECK(sceUltMutexDestroy(mutex) == State); CHECK(sceUltMutexUnlock(mutex) == Ok); alignas(8) static std::uint8_t dataPool[512]; alignas(8) static std::uint8_t queue[512]; @@ -242,7 +242,7 @@ static void UnderscoredCreate() { CHECK(sceUltWaitingQueueResourcePoolDestroy(unknownPool) == State); CHECK(sceUltMutexDestroy(nullptr) == Null); CHECK(sceUltMutexLock(mutex) == Ok); - CHECK(sceUltMutexDestroy(mutex) == Busy); + CHECK(sceUltMutexDestroy(mutex) == State); std::atomic waiterStarted{false}; std::atomic waiterAcquired{false}; std::thread waiter([&] { @@ -254,8 +254,8 @@ static void UnderscoredCreate() { while (!waiterStarted.load(std::memory_order_acquire)) std::this_thread::yield(); std::this_thread::sleep_for(std::chrono::milliseconds(20)); CHECK(!waiterAcquired.load(std::memory_order_acquire)); - CHECK(sceUltMutexDestroy(mutex) == Busy); - CHECK(sceUltWaitingQueueResourcePoolDestroy(pool) == Busy); + CHECK(sceUltMutexDestroy(mutex) == State); + CHECK(sceUltWaitingQueueResourcePoolDestroy(pool) == State); CHECK(sceUltMutexUnlock(mutex) == Ok); waiter.join(); CHECK(waiterAcquired.load(std::memory_order_acquire)); diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index 3e8b94f62a..e569ec3ab6 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -5,13 +5,13 @@ - [Bound condition variables](../../../core/libs/prx/libSceUlt/Export.cpp) use the creation-time mutex, inferred from PPSA28061 callers of `_sceUltConditionVariableCreate`; the wait caller reloads only the condition argument. Error precedence, busy/state errors, FIFO signal selection and unsupported recursive waits have not been measured on PS5 hardware. Non-null options and waits at recursion depth above one throw. A live condition prevents destroying its bound mutex. Finalization follows the existing host mutex lifecycle: it invalidates conditions and wakes queued or reacquiring waiters with a state error. Scheduling priority and build-version validation are not implemented. - [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - Runtime option defaults still omit the native header, stack size, worker affinity and priority fields; affinity depends on unresolved process configuration queries. Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. -- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - the added `sceUltQueuePop`, `sceUltQueueTryPush`, `sceUltQueueDestroy` and `sceUltQueueDataResourcePoolDestroy` signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with `ULT_ERROR_BUSY` and otherwise discard queued data; pool exhaustion returns `ULT_ERROR_AGAIN`. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. -- [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) - a non-null option parameter is accepted and ignored, as the other Ult create calls already do. [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/00d087b2323bf377fd39b7163a256977383cb232/include/orbis/Ult.h) declares `sceUltQueueOptParamInitialize` and `sceUltQueueDataResourcePoolOptParamInitialize` without their structures; the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.h#L25-L78) defines both as a 16-byte reserved header followed by reserved fields, 128 bytes, with no settings, and [fpPS4](https://github.com/red-prig/fpPS4/blob/5ffbdbbd76b4da6e4b8721743ea491f494b28913/src/ps4_libsceult.pas#L142-L172) keeps them as 128 opaque bytes. That the PS5 structures carry no settings either is assumed from these, not verified +- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - Queue-data pool destruction, allocation failure codes, alignment checks and shared slot limits still require comparison with the native node allocator. +- [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) accept and ignore option parameters. Native options contain a 16-byte header and 112 zero reserved bytes; header, alignment and reserved-field validation are not implemented. - [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. The nested context in `UltUlthreadInfo` still requires reconstruction. - [sceUltMutexTryLock, the reader-writer lock and sceUltUlthreadGetSelf, sceUltUlthreadYield and sceUltUlthreadExit](../../../core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp) (libSceUlt) - the signatures follow the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.cpp) (a 256-byte lock and 128-byte option structures), not a PS5 binary. The rules are assumed: readers may take the lock while a writer waits, a thread that holds the write lock gets `ULT_ERROR_STATE` for any further lock instead of waiting on itself, a reader asking for the write lock waits on its own read lock, and the try forms return `ULT_ERROR_BUSY`. Outside an ulthread, `sceUltUlthreadGetSelf`, `sceUltUlthreadYield` and `sceUltUlthreadExit` return `ULT_ERROR_STATE`; inside one they act on the guest thread it runs on, since ulthreads are not scheduled cooperatively here ### Functional - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; the guest object layouts remain incomplete. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. -- [sceUltWaitingQueueResourcePoolDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) refuses with `ULT_ERROR_BUSY` only when a thread is currently waiting on a pool-backed mutex, semaphore or queue, matching the PS5 libSceUlt disassembly (0x473a in or55417wcDk); bound but idle objects do not block pool destruction. Waiting-queue pool guest work area node accounting is not implemented. +- [sceUltWaitingQueueResourcePoolDestroy](../../../core/libs/prx/libSceUlt/Export.cpp) refuses with `ULT_ERROR_STATE` only when a thread is currently waiting on a pool-backed mutex, semaphore or queue, matching the PS5 libSceUlt disassembly (0x473a in or55417wcDk); bound but idle objects do not block pool destruction. Waiting-queue pool guest work area node accounting is not implemented. - The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`_sceUltConditionVariableOptParamInitialize`](../../../core/libs/prx/libSceUlt/Export.cpp). From c86b797e9eaa93f431659286a0e4df016c1eb7b2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:57:08 +0300 Subject: [PATCH 435/523] fix(webbrowserdialog): restore cookie parameter layouts --- .../prx/libSceWebBrowserDialog/Export.cpp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceWebBrowserDialog/Export.cpp b/core/libs/prx/libSceWebBrowserDialog/Export.cpp index 8ccf318370..1d0dfbc4d4 100644 --- a/core/libs/prx/libSceWebBrowserDialog/Export.cpp +++ b/core/libs/prx/libSceWebBrowserDialog/Export.cpp @@ -4,6 +4,23 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct WebBrowserDialogCookieParam { + std::uint64_t size; + const char* address; + const char* cookie; + std::uint8_t reserved[256]; +}; + +struct WebBrowserDialogResetCookieParam { + std::uint64_t size; + std::uint8_t reserved[256]; +}; + +static_assert(sizeof(WebBrowserDialogCookieParam) == 280); +static_assert(offsetof(WebBrowserDialogCookieParam, address) == 8); +static_assert(offsetof(WebBrowserDialogCookieParam, cookie) == 16); +static_assert(sizeof(WebBrowserDialogResetCookieParam) == 264); + namespace { std::atomic g_status{0}; @@ -68,7 +85,7 @@ int APS5_VABI sceWebBrowserDialogUpdateStatus(void) { } -int APS5_VABI sceWebBrowserDialogSetCookie(void) { +int APS5_VABI sceWebBrowserDialogSetCookie(const WebBrowserDialogCookieParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -82,7 +99,7 @@ int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param) return 0; } -int APS5_VABI sceWebBrowserDialogResetCookie() { +int APS5_VABI sceWebBrowserDialogResetCookie(const WebBrowserDialogResetCookieParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } From 964fe1116862dfc743a804e8982620dc9662f9cf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:57:09 +0300 Subject: [PATCH 436/523] fix(appinstutil): restore package installation argument slots --- core/libs/prx/libSceAppInstUtil/Export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAppInstUtil/Export.cpp b/core/libs/prx/libSceAppInstUtil/Export.cpp index c3485fbe05..5925f41ddd 100644 --- a/core/libs/prx/libSceAppInstUtil/Export.cpp +++ b/core/libs/prx/libSceAppInstUtil/Export.cpp @@ -39,7 +39,7 @@ int APS5_VABI sceAppInstUtilAppGetSize(const char* titleId, std::uint64_t* size) NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAppInstUtilAppInstallPkg() { +int APS5_VABI sceAppInstUtilAppInstallPkg(const char* packagePath, std::uint64_t serviceArgument) { NotImplemented_nid_no_patch(__func__); return 0; } From 6d318d168ae3e3b1dd29c651d4a9ff7bc80b6672 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:57:34 +0300 Subject: [PATCH 437/523] fix(font): restore graphics and word creation argument slots --- core/libs/prx/libSceFont/src/Unimplemented.cpp | 4 ++-- docs/dev/debt/libSceFont.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Unimplemented.cpp b/core/libs/prx/libSceFont/src/Unimplemented.cpp index 49bdec210f..c98c47a93e 100644 --- a/core/libs/prx/libSceFont/src/Unimplemented.cpp +++ b/core/libs/prx/libSceFont/src/Unimplemented.cpp @@ -153,12 +153,12 @@ int APS5_VABI sceFontCreateGraphicsService(const FontMemory* memory, const FontG return 0; } -int APS5_VABI sceFontCreateGraphicsServiceWithEdition() { +int APS5_VABI sceFontCreateGraphicsServiceWithEdition(const FontMemory* memory, const FontGraphicsServiceParam* param, std::uint64_t unusedSlot, FontGraphicsService** service) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceFontCreateWords() { +int APS5_VABI sceFontCreateWords(const FontMemory* memory, FontTextSource* source, std::uint64_t unusedSlot, FontWords** words) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index 585003d2a6..b387ddb5d3 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -36,3 +36,5 @@ Canvas, design, surface texture and fill-pattern construction signatures in [Uni [Unimplemented.cpp](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) now defines the 32-byte fill method, 64-byte fill plot/rates and 96-byte device usage layouts, with size and field-offset assertions. Uninterpreted descriptor tails remain reserved; pool statistics expose capacity, current usage, available bytes, peak and signed change. The [glyph support registration and texture-image fill signatures](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) are recovered from native argument use. The image constructor takes a byte span and four scalar coordinates; the decompiler omits its successful path. `CreateWords` and `CreateGraphicsServiceWithEdition` consume the output pointer in the fourth general-purpose argument slot but do not read the third slot, whose source type is not established. + +- [sceFontCreateGraphicsServiceWithEdition and sceFontCreateWords](../../../core/libs/prx/libSceFont/src/Unimplemented.cpp) take their handle output in the fourth integer argument slot. The third slot is not read by the supplied module and is represented as an opaque 64-bit value; its declared source type remains unknown. From 96ae2b5d5d294f5ac4796180f239383cada83354 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 10:58:34 +0300 Subject: [PATCH 438/523] fix(syscore): restore application info query arguments --- core/libs/prx/libSceSysCore/Export.cpp | 8 +++++++- docs/dev/debt/libSceSysCore.md | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceSysCore/Export.cpp b/core/libs/prx/libSceSysCore/Export.cpp index 7c5cd4bbd6..f06a188fd8 100644 --- a/core/libs/prx/libSceSysCore/Export.cpp +++ b/core/libs/prx/libSceSysCore/Export.cpp @@ -3,9 +3,15 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct ApplicationInfo { + std::byte data[152]; +}; + +static_assert(sizeof(ApplicationInfo) == 152); + extern "C" { -int APS5_VABI sceApplicationGetAppInfoByAppId() { +int APS5_VABI sceApplicationGetAppInfoByAppId(std::uint32_t applicationId, ApplicationInfo* info) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSysCore.md b/docs/dev/debt/libSceSysCore.md index 0ebd352ebd..969d61b1fe 100644 --- a/docs/dev/debt/libSceSysCore.md +++ b/docs/dev/debt/libSceSysCore.md @@ -2,4 +2,4 @@ ### Unknown function info -- [sceApplicationGetAppInfoByAppId](../../../core/libs/prx/libSceSysCore/Export.cpp) (libSceSysCore) - the name and NID are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSysCore.c), but no credible prototype was found. The declaration has no parameters solely to resolve a known import and makes no ABI claim +- [sceApplicationGetAppInfoByAppId](../../../core/libs/prx/libSceSysCore/Export.cpp) takes a 32-bit application ID and a writable 152-byte record. The service fills the record through IPC; its complete field layout remains unresolved. The SystemService caller consumes a subrecord beginning at byte 48. From 991efec08663d5a3436d9527bd3ce15ea9a9173d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:03:05 +0300 Subject: [PATCH 439/523] fix(systemservice): restore tournament and daemon argument slots --- core/libs/prx/libSceSystemService/Export.cpp | 15 ++++++++++++++- .../prx/libSceSystemService/PendingExports.cpp | 8 ++++---- docs/dev/debt/libSceSystemService.md | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceSystemService/Export.cpp b/core/libs/prx/libSceSystemService/Export.cpp index d0b5a7623b..08e4239f19 100644 --- a/core/libs/prx/libSceSystemService/Export.cpp +++ b/core/libs/prx/libSceSystemService/Export.cpp @@ -393,7 +393,20 @@ int APS5_VABI sceSystemServiceOpenChallengeActivity(void) { return SYSTEM_SERVICE_OK; } -int APS5_VABI sceSystemServiceOpenTournamentOccurrence(void) { +struct SystemServiceTournamentOccurrenceParam { + std::uint64_t size; + const char* occurrenceId; + std::int32_t userId; + std::uint32_t padding; + const char* context; +}; + +static_assert(sizeof(SystemServiceTournamentOccurrenceParam) == 32); +static_assert(offsetof(SystemServiceTournamentOccurrenceParam, occurrenceId) == 8); +static_assert(offsetof(SystemServiceTournamentOccurrenceParam, userId) == 16); +static_assert(offsetof(SystemServiceTournamentOccurrenceParam, context) == 24); + +int APS5_VABI sceSystemServiceOpenTournamentOccurrence(const SystemServiceTournamentOccurrenceParam* param) { return SYSTEM_SERVICE_OK; } diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 9841ee91cf..1c37a7a66c 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -286,7 +286,7 @@ int APS5_VABI sceLncUtilBlockAppSuspend(const char* reason) { return 0; } -int APS5_VABI sceLncUtilBlockingGetEventForDaemon() { +int APS5_VABI sceLncUtilBlockingGetEventForDaemon(LncDaemonEvent* event, std::uint64_t receiveArgument) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2157,7 +2157,7 @@ int APS5_VABI sceSystemServiceKillLocalProcessForPs2Emu(std::int32_t processId) return 0; } -int APS5_VABI sceSystemServiceLaunchEventDetails() { +int APS5_VABI sceSystemServiceLaunchEventDetails(std::uint64_t unusedSlot, const char* eventId) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2172,7 +2172,7 @@ int APS5_VABI sceSystemServiceLaunchTournamentList() { return 0; } -int APS5_VABI sceSystemServiceLaunchTournamentsTeamProfile() { +int APS5_VABI sceSystemServiceLaunchTournamentsTeamProfile(std::uint64_t unusedSlot, const char* profile, const char* tournament, const char* context) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -2315,7 +2315,7 @@ int APS5_VABI sceSystemServiceShowEyeToEyeDistanceSetting() { return 0; } -int APS5_VABI sceSystemServiceShowImposeMenuForPs2Emu() { +int APS5_VABI sceSystemServiceShowImposeMenuForPs2Emu(std::uint8_t enabled, std::uint64_t reserved) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 8d9548f41d..8abccbf202 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -148,3 +148,5 @@ - [Assembly-only shell and clock queries](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): format/fsck progress returns a float in XMM0. Impose-menu, accessibility-zoom and suspend-confirmation queries take an application ID and return a boolean. Suspend-blacklist lookup forwards a 32-bit ID to SysCore. Clock changes take a string and 32-bit value; CPU pstate takes a 32-byte input structure whose fields remain unresolved, and memory pstate takes one 32-bit value. - [Integer system parameters](../../../core/libs/prx/libSceSystemService/Export.cpp): registry-backed parameters 100–111, 200–213 and 300 throw because their settings are not modeled. Language results omit the console SDK-version compatibility remapping, and parameters 7, 400 and 1000 do not reproduce platform-dependent availability. + +- [Tournament launch parameters](../../../core/libs/prx/libSceSystemService/Export.cpp): occurrence parameters occupy 32 bytes and contain two string pointers and a user ID. The final string semantics are unresolved. Event-details and team-profile launches retain an unused first argument slot of unknown declared type. The daemon blocking-event receive argument is passed unchanged as 64 bits to IPC; its meaning remains unresolved. Protected bodies in [PendingExports.cpp](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) remain unchanged. From a9642f82419313a0a8c337060c2e1c39f298ec08 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:03:55 +0300 Subject: [PATCH 440/523] fix(vrtracker): restore processing and initialization contracts --- core/libs/prx/libSceVrTracker/Export.cpp | 69 +++++++++++++++++++++--- docs/dev/debt/libSceVrTracker.md | 2 +- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceVrTracker/Export.cpp b/core/libs/prx/libSceVrTracker/Export.cpp index b68062f19b..6ca29bfe49 100644 --- a/core/libs/prx/libSceVrTracker/Export.cpp +++ b/core/libs/prx/libSceVrTracker/Export.cpp @@ -3,29 +3,86 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +struct VrTrackerMemoryBlock { + void* address; + std::uint32_t size; + std::uint32_t alignment; +}; + +struct VrTrackerInitConfig { + std::uint32_t type; + std::uint32_t option; + std::uint32_t mode; + std::uint32_t parameter; + std::uint32_t reserved[4]; +}; + +struct VrTrackerInitParam { + std::uint32_t size; + std::int32_t mode; + std::uint32_t reserved[6]; + VrTrackerInitConfig config; + VrTrackerMemoryBlock memory[3]; + std::uint32_t option112; + std::uint32_t option116; + std::uint32_t reservedTail[6]; +}; + +static_assert(sizeof(VrTrackerMemoryBlock) == 16); +static_assert(sizeof(VrTrackerInitConfig) == 32); +static_assert(sizeof(VrTrackerInitParam) == 144); +static_assert(offsetof(VrTrackerInitParam, config) == 32); +static_assert(offsetof(VrTrackerInitParam, memory) == 64); +static_assert(offsetof(VrTrackerInitParam, option112) == 112); +struct VrTrackerGpuSubmitParam; +struct VrTrackerResult; + +struct VrTrackerCpuProcessParam { + std::uint32_t size; + std::uint32_t mode; + std::uint32_t argument; + std::uint32_t reserved[5]; +}; + +struct VrTrackerGpuWaitParam { + std::uint32_t size; + std::byte payload[28]; +}; + +struct VrTrackerGetResultParam { + std::uint32_t size; + std::byte payload[52]; +}; + +static_assert(sizeof(VrTrackerCpuProcessParam) == 32); +static_assert(offsetof(VrTrackerCpuProcessParam, mode) == 4); +static_assert(offsetof(VrTrackerCpuProcessParam, argument) == 8); +static_assert(sizeof(VrTrackerGpuWaitParam) == 32); +static_assert(sizeof(VrTrackerGetResultParam) == 56); + extern "C" { -int APS5_VABI sceVrTrackerCpuProcess() { +int APS5_VABI sceVrTrackerCpuProcess(const VrTrackerCpuProcessParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVrTrackerGetResult() { +int APS5_VABI sceVrTrackerGetResult(const VrTrackerGetResultParam* param, VrTrackerResult* result) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVrTrackerGetTime() { +int APS5_VABI sceVrTrackerGetTime(std::uint64_t* time) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVrTrackerGpuSubmit() { +int APS5_VABI sceVrTrackerGpuSubmit(const VrTrackerGpuSubmitParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVrTrackerGpuWait() { +int APS5_VABI sceVrTrackerGpuWait(const VrTrackerGpuWaitParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVrTrackerInit() { +int APS5_VABI sceVrTrackerInit(const VrTrackerInitParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceVrTracker.md b/docs/dev/debt/libSceVrTracker.md index 7180a455a7..da28022685 100644 --- a/docs/dev/debt/libSceVrTracker.md +++ b/docs/dev/debt/libSceVrTracker.md @@ -2,7 +2,7 @@ ### Unknown function info -- [sceVrTrackerCpuProcess, sceVrTrackerGetResult, sceVrTrackerGetTime, sceVrTrackerGpuSubmit, sceVrTrackerGpuWait and sceVrTrackerInit](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [VrTracker parameters](../../../core/libs/prx/libSceVrTracker/Export.cpp): argument slots are recovered. CPU processing and GPU waiting consume 32-byte records, result queries consume 56-byte records, and time queries write 64 bits. Initialization accepts 128-byte and 144-byte versions; the 144-byte layout includes three address/size/alignment records, but configuration selector semantics and the older field layout remain unresolved. GPU submission accepts 552-byte and 648-byte versions; submission and result records still require field recovery; result storage has 1520-byte and 3640-byte variants. CPU mode/argument semantics and the remaining query fields are unresolved. Protected bodies remain unchanged. - [sceVrTrackerCpuPopMarker, sceVrTrackerCpuPushMarker, sceVrTrackerGetLiveCaptureId, sceVrTrackerGpuWaitAndCpuProcess, sceVrTrackerResetAll, sceVrTrackerSaveInternalBuffers and sceVrTrackerSetDeviceRejection](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. - [sceVrTrackerSetExtendedMode, sceVrTrackerSetLEDBrightness, sceVrTrackerSetRestingMode, sceVrTrackerStartLiveCapture, sceVrTrackerStopLiveCapture and sceVrTrackerTerm](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. - [sceVrTrackerGetPlayAreaWarningInfo, sceVrTrackerNotifyEndOfCpuProcess, sceVrTrackerQueryMemory, sceVrTrackerRecalibrate, sceVrTrackerRegisterDevice and sceVrTrackerRegisterDevice2](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Parameter shapes follow the [pinned shadPS4 declarations](https://github.com/shadps4-emu/shadPS4/blob/f9d07487dd85bb7dc522df6de109b8ad4b9b7175/src/core/libraries/vr_tracker/vr_tracker.h), not measured on a PS5. From 7daeb8059ff27eba6c3119c00bc846a130356639 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:05:13 +0300 Subject: [PATCH 441/523] fix(movetracker): restore initialization argument slots --- core/libs/prx/libSceMoveTracker/Export.cpp | 2 +- docs/dev/debt/libSceMoveTracker.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceMoveTracker/Export.cpp b/core/libs/prx/libSceMoveTracker/Export.cpp index fb002273b5..ef780c7000 100644 --- a/core/libs/prx/libSceMoveTracker/Export.cpp +++ b/core/libs/prx/libSceMoveTracker/Export.cpp @@ -54,7 +54,7 @@ int APS5_VABI sceMoveTrackerGetWorkingMemorySize(std::uint32_t* totalSize, std:: return 0; } -int APS5_VABI sceMoveTrackerInit() { +int APS5_VABI sceMoveTrackerInit(void* workMemory, std::uint64_t requiredArgument, std::uint32_t pipeId, std::uint32_t queueId) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMoveTracker.md b/docs/dev/debt/libSceMoveTracker.md index b6ddbd43a0..c261f9fcd9 100644 --- a/docs/dev/debt/libSceMoveTracker.md +++ b/docs/dev/debt/libSceMoveTracker.md @@ -2,4 +2,4 @@ ### Unknown function info -- [libSceMoveTracker](../../../core/libs/prx/libSceMoveTracker/Export.cpp) remains unimplemented. Calibration direction, camera/controller input, capture initialization, state query, memory-size query and playback output signatures are recovered. Camera and controller records occupy 24 bytes, state records 160 bytes, and playback metadata 16 bytes; their complete field meanings still require reconstruction. Initialization arguments and capture/playback helper signatures need further checking against caller register use. +- [libSceMoveTracker](../../../core/libs/prx/libSceMoveTracker/Export.cpp) remains unimplemented. Calibration direction, camera/controller input, capture initialization, state query, memory-size query and playback output signatures are recovered. Camera and controller records occupy 24 bytes, state records 160 bytes, and playback metadata 16 bytes; their complete field meanings still require reconstruction. Initialization consumes a work-memory pointer, a required nonzero 64-bit argument and two 32-bit GPU queue selectors below eight. The second argument is only checked for zero; its declared type remains unresolved. Capture/playback helper signatures need further checking against caller register use. From 35e3c134c4f31d5bac847be4a4ee0d70b7fe0577 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:09:42 +0300 Subject: [PATCH 442/523] fix(mbus): restore by-value device state arguments --- core/libs/prx/libSceMbus/PendingExports.cpp | 9 +++++++-- docs/dev/debt/libSceMbus.md | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceMbus/PendingExports.cpp b/core/libs/prx/libSceMbus/PendingExports.cpp index 593546e9b6..76313b1a67 100644 --- a/core/libs/prx/libSceMbus/PendingExports.cpp +++ b/core/libs/prx/libSceMbus/PendingExports.cpp @@ -2,6 +2,11 @@ #include +struct MbusDeviceFunctionState { + std::byte data[180]; +}; + +static_assert(sizeof(MbusDeviceFunctionState) == 180); struct MbusDeviceDescription; struct MbusDeviceInfo; struct MbusSimulatedHandleStatus; @@ -340,7 +345,7 @@ int APS5_VABI sceMbusEventBusStatusChangeSubscribe(std::int32_t eventHandle, std return 0; } -int APS5_VABI sceMbusEventBusStatusChangeUnsubscribe() { +int APS5_VABI sceMbusEventBusStatusChangeUnsubscribe(std::uint64_t unusedSlot, std::int32_t subscription) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -505,7 +510,7 @@ int APS5_VABI sceMbusSetCameraAppModuleFocus(std::int32_t moduleId) { return 0; } -int APS5_VABI sceMbusSetDeviceFunctionState() { +int APS5_VABI sceMbusSetDeviceFunctionState(std::uint64_t deviceId, MbusDeviceFunctionState state) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceMbus.md b/docs/dev/debt/libSceMbus.md index b1bb624f14..9078d4ebc6 100644 --- a/docs/dev/debt/libSceMbus.md +++ b/docs/dev/debt/libSceMbus.md @@ -8,7 +8,7 @@ - [sceDeviceServiceQueryDeviceInfo_](../../../core/libs/prx/libSceMbus/Export.cpp): the 112-byte device-info layout and the third argument's meaning remain unresolved. The second argument is a six-byte HID usage filter pointer, not an integer. Initialization takes an optional 16-byte parameter structure with an optional thread configuration; custom scheduling throws because the host has no event worker. `sceDeviceServiceGetEventState` takes a clear flag, not a device type. -- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): SetDeviceFunctionState still requires its by-value aggregate layout. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. +- [sceMbus remaining declarations](../../../core/libs/prx/libSceMbus/PendingExports.cpp): SetDeviceFunctionState takes a 64-bit device identifier and a 180-byte aggregate by value on the stack. Its service-side fields and source alignment remain unresolved; the declaration preserves the observed stack ABI with opaque storage. AcquireControlList and Term return constants without reading incoming arguments, so their original parameter lists cannot be established from the callees. All protected stub bodies remain unchanged. - [sceMbusAddHandleByDeviceId](../../../core/libs/prx/libSceMbus/PendingExports.cpp): the six-argument ABI is confirmed, including the 64-bit device ID. The meanings of the last three 32-bit fields require the device-service backend. From 07a181649c4e34574920b87202d491807aea0a1e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:11:32 +0300 Subject: [PATCH 443/523] fix(savedata): restore cancellation and transfer preparation arguments --- core/libs/prx/libSceSaveData.native/Export.cpp | 4 ++-- core/libs/tests/GuestSaveDataCancel.cpp | 5 +++-- docs/dev/debt/libSceSaveData.native.md | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceSaveData.native/Export.cpp b/core/libs/prx/libSceSaveData.native/Export.cpp index a95498f7d9..afe320f439 100644 --- a/core/libs/prx/libSceSaveData.native/Export.cpp +++ b/core/libs/prx/libSceSaveData.native/Export.cpp @@ -978,7 +978,7 @@ int APS5_VABI sceSaveDataGetConvertProgress(float* progress) { return rc; } -int APS5_VABI sceSaveDataCancel() { +int APS5_VABI sceSaveDataCancel(std::uint32_t cancelArgument) { NotImplemented_nid_no_patch(__func__); return 0; } @@ -989,7 +989,7 @@ int APS5_VABI sceSaveDataDirNameSearch2(const SaveDataDirNameSearchCond* cond, S return rc; } -int APS5_VABI sceSaveDataPrepareForTransferring() { +int APS5_VABI sceSaveDataPrepareForTransferring(const SaveDataMountPoint* mountPoint, const SaveDataPrepareParam* param) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/tests/GuestSaveDataCancel.cpp b/core/libs/tests/GuestSaveDataCancel.cpp index 9b8087c031..ce9c2e8c20 100644 --- a/core/libs/tests/GuestSaveDataCancel.cpp +++ b/core/libs/tests/GuestSaveDataCancel.cpp @@ -1,13 +1,14 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include #include #include -extern "C" int APS5_VABI sceSaveDataCancel(); +extern "C" int APS5_VABI sceSaveDataCancel(std::uint32_t cancelArgument); int main() { try { - sceSaveDataCancel(); + sceSaveDataCancel(0); } catch (const std::runtime_error& error) { if (std::string(error.what()) == "sceSaveDataCancel not implemented") return 0; std::fprintf(stderr, "sceSaveDataCancel threw: %s\n", error.what()); diff --git a/docs/dev/debt/libSceSaveData.native.md b/docs/dev/debt/libSceSaveData.native.md index db7aeb7660..ba886396d2 100644 --- a/docs/dev/debt/libSceSaveData.native.md +++ b/docs/dev/debt/libSceSaveData.native.md @@ -3,11 +3,11 @@ ### Unknown function info - [sceSaveDataCreateTransactionResource, sceSaveDataDeleteTransactionResource](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - transaction resources return synthetic positive identifiers and validate initialization state; resource parameter validation and transactional rollback semantics on host filesystem storage are unknown -- [sceSaveDataPrepareForTransferring](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native, NID `RvID8BVeg+g`) - declared without parameters as a throwing stub; Minecraft (PPSA17221) imports it beside `sceSaveDataTransferringMountPs4` and `sceSaveDataDirNameSearchPs4` and fails to load without it. The name hashes to the NID but is not in aerolib; it was found by combining words from the libSceSaveData names in aerolib. Its arguments and behaviour are unknown. +- [sceSaveDataPrepareForTransferring](../../../core/libs/prx/libSceSaveData.native/Export.cpp): the native wrapper confirms the same 16-byte mount point and 40-byte preparation parameter transport as `sceSaveDataPrepare`. Transfer preparation still requires the save-data service backend; its protected stub is unchanged. - [sceSaveDataTransferringMountPs4, sceSaveDataDirNameSearchPs4](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - arguments assumed to be those of `sceSaveDataTransferringMount` and `sceSaveDataDirNameSearch`: KytyPS5 binds both NIDs to the PS5 functions, and [prosper](https://github.com/mattias800/prosper) captured the same result layout on a title for the search. There is no PS4 save area, so the mount returns `NOT_FOUND` and the search reports zero hits - [sceSaveDataConvert](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - the parameter block (user id at `+0x00`, `SceSaveDataDirName` pointer at `+0x08`, 48 opaque bytes after it) is read from PPSA28416's single call, not confirmed by KytyPS5, shadPS4, fpPS4 or SharpProspero, which have no body for it. Host saves have no size limit, so an existing save returns `SCE_OK` without touching its files and no event is queued; what the console returns for a save that is mounted, already converted or needs no conversion is unknown. The directory name is validated as `sceSaveDataDelete` does - [sceSaveDataGetConvertProgress](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - imported by PPSA16064; signature and error codes follow the [SDK 12.000 reference](https://github.com/peterpuppy/bm/blob/3f9d1ba6b896a53080460c903f5c15af8affb80d/features/ps5/document/output/psn_12/System/Save_Data/SaveData-Reference/sce-save-data-get-convert-progress.md) (`float* progress`). Save data conversion on host filesystems completes synchronously, so the progress query returns `SAVE_DATA_OK` with the last conversion's progress (`0.0f` before any conversion or when conversion fails, `1.0f` after successful conversion), `SAVE_DATA_ERROR_PARAMETER` if `progress` is null, or `SAVE_DATA_ERROR_NOT_INITIALIZED` when the library is uninitialized; intermediate asynchronous conversion progress on console is unmeasured -- [sceSaveDataCancel](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - imported by PPSA23126, which fails to load without it; added without an implementation because its signature is unknown. Every save data call here finishes before it returns, so nothing is ever in flight to cancel, but what the console returns then is unknown +- [sceSaveDataCancel](../../../core/libs/prx/libSceSaveData.native/Export.cpp): the native wrapper forwards one 32-bit argument to the save-data service. Its meaning and cancellation results require the service handler; the protected stub is unchanged. - [sceSaveDataDirNameSearch](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - the sort keys (0 directory name, 1 user parameter, 2 blocks, 3 modification time, 5 free blocks) and orders (0 ascending, 1 descending) follow shadPS4, not a hardware run; saves with equal keys keep directory-name order before a descending reverse. The search infos (blocks, free blocks and 32 reserved bytes per returned name) follow shadPS4's `OrbisSaveDataSearchInfo`. Keys other than 0, 1, 2, 3 and 5 (including 4) and orders above 1 return `SCE_SAVE_DATA_ERROR_PARAMETER`; shadPS4 and KytyPS5 only reject keys above 5, so key 4 is rejected here because no source names it. In the directory name pattern `%` matches any run of characters, none included, and `_` exactly one, as the comments of shadPS4's matcher describe; also not confirmed on hardware - [sceSaveDataDirNameSearch, sceSaveDataGetMountInfo, sceSaveDataMount3](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - the 64 KiB block of [SharpEmu](https://github.com/sharpemu/sharpemu/blob/c6d304cc3328bad62cb671327ace181110cf2ddb/src/SharpEmu.Libs/SaveData/SaveDataExports.cs#L82-L84) and the 48 to 16384-block range of SharpEmu and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/569ec04d1a77127c601eed522ad5bba187173f9d/src/libs/libSaveData.cpp#L30-L31) are not measured on a console, and file system overhead is not counted. What the console does with a `blocks` outside that range is unknown: KytyPS5 rejects the creating mount and SharpEmu clamps it; here a larger one is kept as 16384 and a smaller non-zero one as given. A save's size is the `blocks` of the mount that created it, kept in `.blocks`, else that of its next mount that names one, else 16384; its free blocks are that size less its files, each rounded up to 64 KiB blocks - [sceSaveDataInitialize3](../../../core/libs/prx/libSceSaveData.native/Export.cpp) (libSceSaveData.native) - a repeated initialize succeeds: PPSA12544's executable and its Unity SaveData plugin both initialize, and the plugin fails on any error. Initializations are counted and `sceSaveDataTerminate` ends the session at the last one; how the console pairs them is unknown From f80c7e047dd99ded6c968bf328d14d949c8d3b6e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:12:55 +0300 Subject: [PATCH 444/523] fix(webbrowserdialog): restore predetermined content allow-list validation --- .../prx/libSceCommonDialog/CommonDialog.hpp | 8 ++++++ .../prx/libSceWebBrowserDialog/Export.cpp | 18 ++++++++++-- core/libs/tests/GuestWebBrowserDialog.cpp | 28 +++++++++++++++---- docs/dev/debt/libSceWebBrowserDialog.md | 2 +- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index b4d577c0db..e0a19fe9e8 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,14 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct WebBrowserDialogAllowList { + std::uint64_t size; + const char* addresses[20]; + std::uint8_t reserved[256]; +}; + +static_assert(sizeof(WebBrowserDialogAllowList) == 424); + struct CommonDialogResultPrefix { std::uint8_t untouched[8]; std::int32_t result; diff --git a/core/libs/prx/libSceWebBrowserDialog/Export.cpp b/core/libs/prx/libSceWebBrowserDialog/Export.cpp index 1d0dfbc4d4..97434d16e1 100644 --- a/core/libs/prx/libSceWebBrowserDialog/Export.cpp +++ b/core/libs/prx/libSceWebBrowserDialog/Export.cpp @@ -2,6 +2,7 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" struct WebBrowserDialogCookieParam { @@ -29,9 +30,9 @@ constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); +constexpr int COMMON_DIALOG_ERROR_PARAMETER = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); } @@ -90,11 +91,22 @@ int APS5_VABI sceWebBrowserDialogSetCookie(const WebBrowserDialogCookieParam* pa return 0; } -int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param) { +int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param, const WebBrowserDialogAllowList* allowList) { const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; + if (allowList == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (allowList->size != sizeof(*allowList)) return COMMON_DIALOG_ERROR_PARAMETER; + bool hasAddress = false; + for (const char* address : allowList->addresses) { + if (address == nullptr) continue; + hasAddress = true; + std::size_t length = 0; + while (length < 256 && address[length] != '\0') ++length; + if (length == 0 || length == 256) return COMMON_DIALOG_ERROR_PARAMETER; + } + if (!hasAddress || std::any_of(std::begin(allowList->reserved), std::end(allowList->reserved), [](std::uint8_t value) { return value != 0; })) return COMMON_DIALOG_ERROR_PARAMETER; if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestWebBrowserDialog.cpp b/core/libs/tests/GuestWebBrowserDialog.cpp index ea5d58c2cc..5f4802bef8 100644 --- a/core/libs/tests/GuestWebBrowserDialog.cpp +++ b/core/libs/tests/GuestWebBrowserDialog.cpp @@ -1,4 +1,5 @@ #include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include #include @@ -6,7 +7,7 @@ extern "C" { int APS5_VABI sceWebBrowserDialogInitialize(void); int APS5_VABI sceWebBrowserDialogTerminate(void); int APS5_VABI sceWebBrowserDialogOpen(const void* param); -int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param); +int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param, const WebBrowserDialogAllowList* allowList); int APS5_VABI sceWebBrowserDialogClose(void); int APS5_VABI sceWebBrowserDialogGetStatus(void); int APS5_VABI sceWebBrowserDialogUpdateStatus(void); @@ -20,7 +21,6 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); @@ -30,6 +30,9 @@ void Require(bool value) { if (!value) std::abort(); } int main() { std::uint8_t param[64] = {}; + WebBrowserDialogAllowList allowList{}; + allowList.size = sizeof(allowList); + allowList.addresses[0] = "test.invalid"; std::int32_t result = 0x5a5a5a5a; Require(sceWebBrowserDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); @@ -65,10 +68,25 @@ int main() { Require(sceWebBrowserDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); - Require(sceWebBrowserDialogOpenForPredeterminedContent(param) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(sceWebBrowserDialogInitialize() == 0); - Require(sceWebBrowserDialogOpenForPredeterminedContent(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); - Require(sceWebBrowserDialogOpenForPredeterminedContent(param) == 0); + Require(sceWebBrowserDialogOpenForPredeterminedContent(nullptr, &allowList) == COMMON_DIALOG_ERROR_ARG_NULL); + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); + allowList.addresses[0] = nullptr; + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == static_cast(0x80B8000Au)); + allowList.addresses[19] = "test.invalid"; + allowList.reserved[255] = 1; + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == static_cast(0x80B8000Au)); + allowList.reserved[255] = 0; + allowList.addresses[19] = ""; + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == static_cast(0x80B8000Au)); + char longAddress[257]; + std::fill(std::begin(longAddress), std::end(longAddress), 'a'); + longAddress[256] = '\0'; + allowList.addresses[19] = longAddress; + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == static_cast(0x80B8000Au)); + longAddress[255] = '\0'; + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &allowList) == 0); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(sceWebBrowserDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); diff --git a/docs/dev/debt/libSceWebBrowserDialog.md b/docs/dev/debt/libSceWebBrowserDialog.md index 3d44927711..8cd180fe3b 100644 --- a/docs/dev/debt/libSceWebBrowserDialog.md +++ b/docs/dev/debt/libSceWebBrowserDialog.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libSceWebBrowserDialog](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) - `sceWebBrowserDialogOpen` and `sceWebBrowserDialogOpenForPredeterminedContent` run the common-dialog state machine without displaying an interactive browser, and `sceWebBrowserDialogGetResult` reports that the user cancelled. Minecraft Bedrock (PPSA17221) imports `sceWebBrowserDialogOpenForPredeterminedContent` (#5744). The real export takes a second argument, the allow-list of up to twenty addresses (SharpProspero `docs/dialogs.md`); it is not read here, so no address filtering is modelled, and the exact validation order and codes with a null second argument are unverified. +- [libSceWebBrowserDialog](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) - `sceWebBrowserDialogOpen` and `sceWebBrowserDialogOpenForPredeterminedContent` run the common-dialog state machine without displaying an interactive browser, and `sceWebBrowserDialogGetResult` reports that the user cancelled. Minecraft Bedrock (PPSA17221) imports `sceWebBrowserDialogOpenForPredeterminedContent` (#5744). The 424-byte allow-list parameter and its twenty optional address pointers are validated, but address filtering is not modelled without an interactive browser. Main open-parameter validation remains incomplete. - [libSceWebBrowserDialog](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) - `sceWebBrowserDialogClose` transitions an initialized dialog directly to `COMMON_DIALOG_STATUS_FINISHED` even when called before `Open`, following the Signin / NetCtlAp / PlayerSelection convention; this behavior is unverified against real hardware. ### Unknown function info From fd6372e4f6295710a514ed276b040db664ea2c2b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:13:51 +0300 Subject: [PATCH 445/523] fix(webbrowserdialog): validate result buffers and inactive close --- .../prx/libSceCommonDialog/CommonDialog.hpp | 21 ++++++++ .../prx/libSceWebBrowserDialog/Export.cpp | 30 +++++++---- core/libs/tests/GuestWebBrowserDialog.cpp | 51 +++++++++++++------ docs/dev/debt/libSceWebBrowserDialog.md | 2 - 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index e0a19fe9e8..d16c6ad73d 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,27 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct WebBrowserDialogExtendedResult { + std::uint64_t size; + std::uint32_t type; + char content[2048]; + std::uint8_t reserved0[4]; + char* extendedContent; + std::uint32_t extendedCapacity; + std::uint8_t reserved1[20]; +}; + +struct WebBrowserDialogResult { + std::int32_t result; + std::uint32_t padding; + WebBrowserDialogExtendedResult* extended; + std::uint8_t reserved[240]; +}; + +static_assert(sizeof(WebBrowserDialogExtendedResult) == 2096); +static_assert(offsetof(WebBrowserDialogExtendedResult, extendedContent) == 2064); +static_assert(sizeof(WebBrowserDialogResult) == 256); + struct WebBrowserDialogAllowList { std::uint64_t size; const char* addresses[20]; diff --git a/core/libs/prx/libSceWebBrowserDialog/Export.cpp b/core/libs/prx/libSceWebBrowserDialog/Export.cpp index 97434d16e1..8c720fbab1 100644 --- a/core/libs/prx/libSceWebBrowserDialog/Export.cpp +++ b/core/libs/prx/libSceWebBrowserDialog/Export.cpp @@ -32,6 +32,7 @@ constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); +constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); constexpr int COMMON_DIALOG_ERROR_PARAMETER = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); } @@ -54,18 +55,27 @@ int APS5_VABI sceWebBrowserDialogTerminate(void) { } int APS5_VABI sceWebBrowserDialogClose(void) { - if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - g_status = COMMON_DIALOG_STATUS_FINISHED; - return 0; + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; + g_status = COMMON_DIALOG_STATUS_FINISHED; + return 0; } -int APS5_VABI sceWebBrowserDialogGetResult(void* result) { - const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; - if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - *static_cast(result) = COMMON_DIALOG_RESULT_USER_CANCELED; - return 0; +int APS5_VABI sceWebBrowserDialogGetResult(WebBrowserDialogResult* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (std::any_of(std::begin(result->reserved), std::end(result->reserved), [](std::uint8_t value) { return value != 0; })) return COMMON_DIALOG_ERROR_PARAMETER; + if (result->extended != nullptr) { + const auto& extended = *result->extended; + if (extended.size != sizeof(extended)) return COMMON_DIALOG_ERROR_PARAMETER; + if (std::any_of(std::begin(extended.reserved0), std::end(extended.reserved0), [](std::uint8_t value) { return value != 0; })) return COMMON_DIALOG_ERROR_PARAMETER; + if (std::any_of(std::begin(extended.reserved1), std::end(extended.reserved1), [](std::uint8_t value) { return value != 0; })) return COMMON_DIALOG_ERROR_PARAMETER; + } + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + return 0; } int APS5_VABI sceWebBrowserDialogGetStatus(void) { diff --git a/core/libs/tests/GuestWebBrowserDialog.cpp b/core/libs/tests/GuestWebBrowserDialog.cpp index 5f4802bef8..bb391077a8 100644 --- a/core/libs/tests/GuestWebBrowserDialog.cpp +++ b/core/libs/tests/GuestWebBrowserDialog.cpp @@ -11,7 +11,7 @@ int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param, int APS5_VABI sceWebBrowserDialogClose(void); int APS5_VABI sceWebBrowserDialogGetStatus(void); int APS5_VABI sceWebBrowserDialogUpdateStatus(void); -int APS5_VABI sceWebBrowserDialogGetResult(void* result); +int APS5_VABI sceWebBrowserDialogGetResult(WebBrowserDialogResult* result); } namespace { @@ -33,21 +33,22 @@ int main() { WebBrowserDialogAllowList allowList{}; allowList.size = sizeof(allowList); allowList.addresses[0] = "test.invalid"; - std::int32_t result = 0x5a5a5a5a; + WebBrowserDialogResult result{}; + result.result = 0x5a5a5a5a; Require(sceWebBrowserDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); Require(sceWebBrowserDialogClose() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(sceWebBrowserDialogOpen(param) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(sceWebBrowserDialogGetResult(&result) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); - Require(result == 0x5a5a5a5a); + Require(result.result == 0x5a5a5a5a); Require(sceWebBrowserDialogInitialize() == 0); Require(sceWebBrowserDialogInitialize() == COMMON_DIALOG_ERROR_ALREADY_INITIALIZED); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_INITIALIZED); Require(sceWebBrowserDialogUpdateStatus() == COMMON_DIALOG_STATUS_INITIALIZED); Require(sceWebBrowserDialogGetResult(&result) == COMMON_DIALOG_ERROR_NOT_FINISHED); - Require(result == 0x5a5a5a5a); + Require(result.result == 0x5a5a5a5a); Require(sceWebBrowserDialogTerminate() == 0); Require(sceWebBrowserDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); @@ -61,9 +62,9 @@ int main() { Require(sceWebBrowserDialogGetResult(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); Require(sceWebBrowserDialogGetResult(&result) == 0); - Require(result == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result.result == COMMON_DIALOG_RESULT_USER_CANCELED); - Require(sceWebBrowserDialogClose() == 0); + Require(sceWebBrowserDialogClose() == static_cast(0x80B8000Bu)); Require(sceWebBrowserDialogTerminate() == 0); Require(sceWebBrowserDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); @@ -90,23 +91,43 @@ int main() { Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(sceWebBrowserDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); - result = 0x5a5a5a5a; + result.result = 0x5a5a5a5a; Require(sceWebBrowserDialogGetResult(&result) == 0); - Require(result == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result.result == COMMON_DIALOG_RESULT_USER_CANCELED); - Require(sceWebBrowserDialogClose() == 0); + Require(sceWebBrowserDialogClose() == static_cast(0x80B8000Bu)); Require(sceWebBrowserDialogTerminate() == 0); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); Require(sceWebBrowserDialogInitialize() == 0); + Require(sceWebBrowserDialogClose() == static_cast(0x80B8000Bu)); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_INITIALIZED); - Require(sceWebBrowserDialogClose() == 0); - Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); - result = 0x5a5a5a5a; + Require(sceWebBrowserDialogGetResult(&result) == COMMON_DIALOG_ERROR_NOT_FINISHED); + Require(sceWebBrowserDialogTerminate() == 0); + Require(sceWebBrowserDialogGetResult(nullptr) == COMMON_DIALOG_ERROR_ARG_NULL); + result.reserved[239] = 1; + Require(sceWebBrowserDialogGetResult(&result) == static_cast(0x80B8000Au)); + result.reserved[239] = 0; + WebBrowserDialogExtendedResult extended{}; + result.extended = &extended; + Require(sceWebBrowserDialogGetResult(&result) == static_cast(0x80B8000Au)); + extended.size = sizeof(extended); + Require(sceWebBrowserDialogGetResult(&result) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); + Require(sceWebBrowserDialogInitialize() == 0); + Require(sceWebBrowserDialogOpen(param) == 0); + extended.reserved0[3] = 1; + Require(sceWebBrowserDialogGetResult(&result) == static_cast(0x80B8000Au)); + extended.reserved0[3] = 0; + extended.reserved1[19] = 1; + Require(sceWebBrowserDialogGetResult(&result) == static_cast(0x80B8000Au)); + extended.reserved1[19] = 0; + extended.type = 0x5a5a5a5a; + extended.content[2047] = 'x'; + result.padding = 0x5a5a5a5a; Require(sceWebBrowserDialogGetResult(&result) == 0); - Require(result == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result.result == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(result.padding == 0x5a5a5a5a && result.extended == &extended); + Require(extended.type == 0x5a5a5a5a && extended.content[2047] == 'x'); Require(sceWebBrowserDialogTerminate() == 0); - Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); - return 0; } diff --git a/docs/dev/debt/libSceWebBrowserDialog.md b/docs/dev/debt/libSceWebBrowserDialog.md index 8cd180fe3b..f86ba5ae8f 100644 --- a/docs/dev/debt/libSceWebBrowserDialog.md +++ b/docs/dev/debt/libSceWebBrowserDialog.md @@ -3,8 +3,6 @@ ### Silent stubs - [libSceWebBrowserDialog](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) - `sceWebBrowserDialogOpen` and `sceWebBrowserDialogOpenForPredeterminedContent` run the common-dialog state machine without displaying an interactive browser, and `sceWebBrowserDialogGetResult` reports that the user cancelled. Minecraft Bedrock (PPSA17221) imports `sceWebBrowserDialogOpenForPredeterminedContent` (#5744). The 424-byte allow-list parameter and its twenty optional address pointers are validated, but address filtering is not modelled without an interactive browser. Main open-parameter validation remains incomplete. -- [libSceWebBrowserDialog](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) - `sceWebBrowserDialogClose` transitions an initialized dialog directly to `COMMON_DIALOG_STATUS_FINISHED` even when called before `Open`, following the Signin / NetCtlAp / PlayerSelection convention; this behavior is unverified against real hardware. - ### Unknown function info - [sceWebBrowserDialogGetEvent, sceWebBrowserDialogNavigate, sceWebBrowserDialogSetZoom](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) (libSceWebBrowserDialog) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceWebBrowserDialog.c); the no-parameter prototypes follow [shadPS4 `webbrowserdialog.cpp`](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/web_browser_dialog/webbrowserdialog.cpp#L23-L85) and are not measured on a console. They are declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. From 218be372ea5b94dc81ad110a7a878a7ce6f7adb8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:16:42 +0300 Subject: [PATCH 446/523] fix(agc): restore protected export argument and return types --- core/libs/prx/libSceAgc/Unimplemented.cpp | 16 ++++++++-------- docs/dev/debt/libSceAgc.md | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceAgc/Unimplemented.cpp b/core/libs/prx/libSceAgc/Unimplemented.cpp index 7a7d7c52cb..02dcc73aca 100644 --- a/core/libs/prx/libSceAgc/Unimplemented.cpp +++ b/core/libs/prx/libSceAgc/Unimplemented.cpp @@ -5,42 +5,42 @@ extern "C" { -int APS5_VABI sceAgcAcbPushMarkerSpan() { +std::uint32_t* APS5_VABI sceAgcAcbPushMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcAcbSetMarkerSpan() { +std::uint32_t* APS5_VABI sceAgcAcbSetMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcAcquireMemSetEngine() { +int APS5_VABI sceAgcAcquireMemSetEngine(std::uint32_t* packet, std::uint8_t engine) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcDcbPushMarkerSpan() { +std::uint32_t* APS5_VABI sceAgcDcbPushMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcDcbSetMarkerSpan() { +std::uint32_t* APS5_VABI sceAgcDcbSetMarkerSpan(CommandBuffer* buffer, const char* text, std::uint32_t length, std::uint32_t color) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcSetSemaphoreMemory() { +int APS5_VABI sceAgcSetSemaphoreMemory(void* memory, std::uint64_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcCbMemsetExclusive() { +std::uint32_t* APS5_VABI sceAgcCbMemsetExclusive(CommandBuffer* buffer, void* destination, const void* pattern, std::uint64_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceAgcSetShaderInstrumentation() { +int APS5_VABI sceAgcSetShaderInstrumentation(std::uint32_t flags) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceAgc.md b/docs/dev/debt/libSceAgc.md index 0bd4347123..eb2c126c2d 100644 --- a/docs/dev/debt/libSceAgc.md +++ b/docs/dev/debt/libSceAgc.md @@ -4,7 +4,6 @@ - [GS shader-half fusion](../../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - `SPI_SHADER_PGM_RSRC2_GS.OC_LDS_EN` is preserved if either half requests it, while user SGPR allocation comes from the front half and other state stays with the back half. Bit 18 is `OC_LDS_EN` in AMD's [GFX10 register definitions](https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_1_0_sh_mask.h). Mesa's radeonsi sets it on a merged ES+GS program exactly when the ES stage is a tessellation evaluation shader ([`si_shader_gs_legacy`](https://gitlab.freedesktop.org/mesa/mesa/-/blob/45ea3ed8ce649d6b72d1843307a29d9cc3b7704e/src/gallium/drivers/radeonsi/gfx/si_state_shaders.cpp#L631), [`gfx10_shader_ngg`](https://gitlab.freedesktop.org/mesa/mesa/-/blob/45ea3ed8ce649d6b72d1843307a29d9cc3b7704e/src/gallium/drivers/radeonsi/gfx/si_state_shaders.cpp#L1144)), so it belongs to the front half; console fusion output has not been measured. Cocoon (PPSA08766) requests front `0x70004` and back `0xab0020`. Other unmatched hardware state still throws. -- PPSA01341 imports declared without parameters, signatures unknown: [sceAgcSetSemaphoreMemory](../../../core/libs/prx/libSceAgc/Unimplemented.cpp). Name from the shadPS4 aerolib NID list - [sceAgcWaitRegMemPatchMask](../../../core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp) (libSceAgc) - follows `sceAgcWaitRegMemPatchReference`: the mask is taken as a 32-bit value and written to the low mask word of a 32- or 64-bit wait, as [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) does; [sharpemu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/Agc/AgcExports.PacketPatching.cs) writes both words of a 64-bit wait's mask. A mask above 32 bits throws - [sceAgcDcbSetCfRegisterDirect, sceAgcDcbSetCfRegisterRangeDirect](../../../core/libs/prx/libSceAgc/DcbState/src/Registers.cpp) (libSceAgc) - signatures follow [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Agc/SceAgc.cs), which agree; the SET_CONFIG_REG (0x68) packet is OpenAGC's. The driver does not execute SET_CONFIG_REG, so a submitted packet throws - [Command buffer cursors](../../../core/libs/prx/libSceAgc/Command/src/Packet.cpp) (libSceAgc) - a null `cursor_down` is taken as a buffer without downward allocations, whose limit is `top`. PPSA16064 builds one-packet buffers of 8 dwords that way; the console's handling is unconfirmed From 2053461e93b414f7b1aa2473161ab4371806d546 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:19:22 +0300 Subject: [PATCH 447/523] fix(ult): validate creation options and work area contracts --- core/libs/prx/libSceUlt/Export.cpp | 38 ++++++++++++++++++++++++- core/libs/prx/libSceUlt/SceUltTypes.hpp | 8 ++++++ core/libs/tests/GuestUltQueue.cpp | 2 +- core/libs/tests/GuestUltThread.cpp | 29 ++++++++++++++++--- docs/dev/debt/libSceUlt.md | 3 +- 5 files changed, 72 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index b2d575d929..5580ec5b6f 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -36,6 +36,16 @@ std::unordered_map gRuntimes; std::unordered_map> gUlthreads; +int validateCommonOptParam(const void* optParam) { + if (optParam == nullptr) return ULT_OK; + if ((reinterpret_cast(optParam) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + UltCommonOptParam value; + std::memcpy(&value, optParam, sizeof(value)); + if (value.header[0] != 0x6C696275 || value.header[1] != 0x6C746D61 || value.header[2] != 0x67696300) return ULT_ERROR_INVALID; + if (std::any_of(std::begin(value.reserved), std::end(value.reserved), [](std::uint8_t byte) { return byte != 0; })) return ULT_ERROR_INVALID; + return ULT_OK; +} + int semaphoreGetState(void* semaphore, std::shared_ptr* out) { std::lock_guard lock(gMutex); auto it = gSemaphores.find(semaphore); @@ -586,7 +596,7 @@ int APS5_VABI sceUltQueueCreate(void* queue, const char* name, std::uint64_t dat auto& pool = *state->_pool; std::lock_guard stateLock(pool._mutex); if (dataSize > pool._dataSize || waitingQueueResourcePool != pool._waitingPool) return ULT_ERROR_INVALID; - if (pool._queues == pool._numQueueObject) return ULT_ERROR_AGAIN; + if (pool._queues == pool._numQueueObject) return ULT_ERROR_BUSY; gQueues.emplace(queue, std::move(state)); ++pool._queues; std::memset(queue, 0, 512); @@ -888,10 +898,20 @@ int APS5_VABI sceUltUlthreadRuntimeDestroy(void* runtime) { } int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name, std::uint32_t numThreads, std::uint32_t numSyncObjects, void* workArea, const void* optParam) { + if (pool == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(pool) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + if (name == nullptr) return ULT_ERROR_NULL; + if (const int result = validateCommonOptParam(optParam); result != ULT_OK) return result; + if (numThreads == 0 || numSyncObjects == 0) return ULT_ERROR_INVALID; + if ((reinterpret_cast(workArea) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + if (workArea == nullptr) return ULT_ERROR_NULL; return sceUltWaitingQueueResourcePoolCreate(pool, name, numThreads, numSyncObjects, workArea, optParam, 0); } int APS5_VABI _sceUltSemaphoreCreate(void* semaphore, const char* name, std::int32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam) { + if (semaphore == nullptr || name == nullptr) return ULT_ERROR_NULL; + if (((reinterpret_cast(semaphore) | reinterpret_cast(waitingQueueResourcePool)) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + if (const int result = validateCommonOptParam(optParam); result != ULT_OK) return result; return sceUltSemaphoreCreate(semaphore, name, numInitialResource, waitingQueueResourcePool, optParam, 0); } @@ -913,10 +933,26 @@ int APS5_VABI _sceUltMutexCreate(void* mutex, const char* name, void* waitingQue } int APS5_VABI _sceUltQueueDataResourcePoolCreate(void* pool, const char* name, std::uint32_t numData, std::uint64_t dataSize, std::uint32_t numQueueObject, void* waitingQueueResourcePool, void* workArea, const void* optParam) { + if (pool == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(pool) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + if (const int result = validateCommonOptParam(optParam); result != ULT_OK) return result; + if (numData == 0 || dataSize == 0 || numQueueObject == 0) return ULT_ERROR_INVALID; + if ((reinterpret_cast(workArea) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + if (workArea == nullptr || name == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(waitingQueueResourcePool) & 7u) != 0) return ULT_ERROR_ALIGNMENT; return sceUltQueueDataResourcePoolCreate(pool, name, numData, dataSize, numQueueObject, waitingQueueResourcePool, workArea, optParam, 0); } int APS5_VABI _sceUltQueueCreate(void* queue, const char* name, std::uint64_t dataSize, void* waitingQueueResourcePool, void* queueDataResourcePool, const void* optParam) { + if (queue == nullptr || name == nullptr || queueDataResourcePool == nullptr) return ULT_ERROR_NULL; + if (((reinterpret_cast(queue) | reinterpret_cast(waitingQueueResourcePool) | reinterpret_cast(queueDataResourcePool)) & 7u) != 0) return ULT_ERROR_ALIGNMENT; + { + std::lock_guard lock(gMutex); + const auto it = gQueueDataPools.find(queueDataResourcePool); + if (it == gQueueDataPools.end()) return ULT_ERROR_INVALID; + if (dataSize == 0 || dataSize > it->second->_dataSize) return ULT_ERROR_INVALID; + } + if (const int result = validateCommonOptParam(optParam); result != ULT_OK) return result; return sceUltQueueCreate(queue, name, dataSize, waitingQueueResourcePool, queueDataResourcePool, optParam, 0); } diff --git a/core/libs/prx/libSceUlt/SceUltTypes.hpp b/core/libs/prx/libSceUlt/SceUltTypes.hpp index 35fa6b302c..d8bff75850 100644 --- a/core/libs/prx/libSceUlt/SceUltTypes.hpp +++ b/core/libs/prx/libSceUlt/SceUltTypes.hpp @@ -36,6 +36,14 @@ inline int InitializeOptParam(UltOptParam* optParam) { return ULT_OK; } +struct alignas(8) UltCommonOptParam { + std::uint32_t header[3]; + std::uint32_t buildVersion; + std::uint8_t reserved[112]; +}; + +static_assert(sizeof(UltCommonOptParam) == 128); + struct UltWaitingThreadInfo { std::uint64_t size; void* thread; diff --git a/core/libs/tests/GuestUltQueue.cpp b/core/libs/tests/GuestUltQueue.cpp index ffce3c241f..4cfcf13881 100644 --- a/core/libs/tests/GuestUltQueue.cpp +++ b/core/libs/tests/GuestUltQueue.cpp @@ -82,7 +82,7 @@ static void Validation() { CHECK(sceUltQueueTryPop(&fixture.first, &saved) == 0 && saved == value); CHECK(sceUltQueueCreate(&extra, nullptr, 9, nullptr, &fixture.pool, nullptr, 0) == Invalid); CHECK(sceUltQueueCreate(&extra, nullptr, 8, &extra, &fixture.pool, nullptr, 0) == Invalid); - CHECK(sceUltQueueCreate(&extra, nullptr, 8, nullptr, &fixture.pool, nullptr, 0) == Again); + CHECK(sceUltQueueCreate(&extra, nullptr, 8, nullptr, &fixture.pool, nullptr, 0) == Busy); CHECK(sceUltQueueCreate(nullptr, nullptr, 8, nullptr, &fixture.pool, nullptr, 0) == Null); CHECK(sceUltQueueCreate(extra.bytes.data() + 1, nullptr, 8, nullptr, &fixture.pool, nullptr, 0) == Alignment); CHECK(sceUltQueueCreate(&extra, nullptr, 0, nullptr, &fixture.pool, nullptr, 0) == Range); diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 70df002c12..41d47b2622 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -180,11 +180,24 @@ static void UnderscoredCreate() { alignas(8) static std::uint8_t semaphore[256]; alignas(8) static std::uint8_t unknownPool[256]; CHECK(_sceUltWaitingQueueResourcePoolCreate(nullptr, "pool", 1, 1, nullptr, nullptr) == Null); - CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, nullptr, nullptr) == Ok); + alignas(8) std::uint8_t workArea[1024]{}; + alignas(8) std::uint32_t commonOptions[32]{}; + commonOptions[0] = 0x6C696275; + commonOptions[1] = 0x6C746D61; + commonOptions[2] = 0x67696300; + CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, nullptr, nullptr) == Null); + CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, workArea + 1, nullptr) == Alignment); + commonOptions[31] = 1; + CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, workArea, commonOptions) == Invalid); + commonOptions[31] = 0; + CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, workArea, commonOptions) == Ok); CHECK(_sceUltSemaphoreCreate(nullptr, "semaphore", 1, pool, nullptr) == Null); CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", -1, pool, nullptr) != Ok); CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, unknownPool, nullptr) != Ok); - CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr) == Ok); + commonOptions[0] = 0; + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, commonOptions) == Invalid); + commonOptions[0] = 0x6C696275; + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, commonOptions) == Ok); CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, nullptr) == State); CHECK(sceUltSemaphoreAcquire(semaphore, 2) == Ok); CHECK(sceUltSemaphoreRelease(semaphore, 2) == Ok); @@ -232,9 +245,17 @@ static void UnderscoredCreate() { alignas(8) static std::uint8_t dataPool[512]; alignas(8) static std::uint8_t queue[512]; CHECK(_sceUltQueueDataResourcePoolCreate(nullptr, "data", 1, 8, 1, pool, nullptr, nullptr) == Null); - CHECK(_sceUltQueueDataResourcePoolCreate(dataPool, "data", 1, 8, 1, pool, nullptr, nullptr) == Ok); + CHECK(_sceUltQueueDataResourcePoolCreate(dataPool, "data", 1, 8, 1, pool, nullptr, nullptr) == Null); + commonOptions[4] = 1; + CHECK(_sceUltQueueDataResourcePoolCreate(dataPool, "data", 1, 8, 1, pool, workArea, commonOptions) == Invalid); + commonOptions[4] = 0; + CHECK(_sceUltQueueDataResourcePoolCreate(dataPool, "data", 1, 8, 1, pool, workArea, commonOptions) == Ok); CHECK(_sceUltQueueCreate(nullptr, "queue", 8, pool, dataPool, nullptr) == Null); - CHECK(_sceUltQueueCreate(queue, "queue", 8, pool, dataPool, nullptr) == Ok); + commonOptions[31] = 1; + CHECK(_sceUltQueueCreate(queue, "queue", 8, pool, dataPool, commonOptions) == Invalid); + commonOptions[31] = 0; + CHECK(_sceUltQueueCreate(queue, "queue", 8, pool, dataPool, reinterpret_cast(commonOptions) + 1) == Alignment); + CHECK(_sceUltQueueCreate(queue, "queue", 8, pool, dataPool, commonOptions) == Ok); std::uint64_t value = 0x1122334455667788ull, output = 0; CHECK(sceUltQueuePush(queue, &value) == Ok); CHECK(sceUltQueuePop(queue, &output) == Ok && output == value); diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index e569ec3ab6..31ae1d7025 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -5,8 +5,7 @@ - [Bound condition variables](../../../core/libs/prx/libSceUlt/Export.cpp) use the creation-time mutex, inferred from PPSA28061 callers of `_sceUltConditionVariableCreate`; the wait caller reloads only the condition argument. Error precedence, busy/state errors, FIFO signal selection and unsupported recursive waits have not been measured on PS5 hardware. Non-null options and waits at recursion depth above one throw. A live condition prevents destroying its bound mutex. Finalization follows the existing host mutex lifecycle: it invalidates conditions and wakes queued or reacquiring waiters with a state error. Scheduling priority and build-version validation are not implemented. - [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - Runtime option defaults still omit the native header, stack size, worker affinity and priority fields; affinity depends on unresolved process configuration queries. Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. -- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - Queue-data pool destruction, allocation failure codes, alignment checks and shared slot limits still require comparison with the native node allocator. -- [sceUltQueueCreate, sceUltQueueDataResourcePoolCreate](../../../core/libs/prx/libSceUlt/Export.cpp) accept and ignore option parameters. Native options contain a 16-byte header and 112 zero reserved bytes; header, alignment and reserved-field validation are not implemented. +- [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - Queue-data pool destruction and shared slot limits still require comparison with the native node allocator. - [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. The nested context in `UltUlthreadInfo` still requires reconstruction. - [sceUltMutexTryLock, the reader-writer lock and sceUltUlthreadGetSelf, sceUltUlthreadYield and sceUltUlthreadExit](../../../core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp) (libSceUlt) - the signatures follow the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.cpp) (a 256-byte lock and 128-byte option structures), not a PS5 binary. The rules are assumed: readers may take the lock while a writer waits, a thread that holds the write lock gets `ULT_ERROR_STATE` for any further lock instead of waiting on itself, a reader asking for the write lock waits on its own read lock, and the try forms return `ULT_ERROR_BUSY`. Outside an ulthread, `sceUltUlthreadGetSelf`, `sceUltUlthreadYield` and `sceUltUlthreadExit` return `ULT_ERROR_STATE`; inside one they act on the guest thread it runs on, since ulthreads are not scheduled cooperatively here From 1196f48d74abf11ec68e363eac470b645ee362e7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:20:44 +0300 Subject: [PATCH 448/523] fix(ult): restore semaphore count bits and validation order --- core/libs/prx/libSceUlt/Export.cpp | 23 +++++++++++++---------- core/libs/tests/GuestUltThread.cpp | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceUlt/Export.cpp b/core/libs/prx/libSceUlt/Export.cpp index 5580ec5b6f..63344b81f8 100644 --- a/core/libs/prx/libSceUlt/Export.cpp +++ b/core/libs/prx/libSceUlt/Export.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -636,7 +637,7 @@ int APS5_VABI sceUltQueueDestroy(void* queue) { return ULT_OK; } -int APS5_VABI sceUltSemaphoreCreate(void* semaphore, const char* name, std::int32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam, std::uint32_t buildVersion) { +int APS5_VABI sceUltSemaphoreCreate(void* semaphore, const char* name, std::uint32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam, std::uint32_t buildVersion) { (void)name; (void)optParam; (void)buildVersion; @@ -646,11 +647,8 @@ int APS5_VABI sceUltSemaphoreCreate(void* semaphore, const char* name, std::int3 if ((reinterpret_cast(semaphore) & 7u) != 0) { return ULT_ERROR_ALIGNMENT; } - if (numInitialResource < 0) { - return ULT_ERROR_RANGE; - } auto state = std::make_shared(); - state->_resources = numInitialResource; + state->_resources = std::bit_cast(numInitialResource); state->_waitingPool = waitingQueueResourcePool; std::lock_guard lock(gMutex); if (waitingQueueResourcePool != nullptr && gResourcePools.find(waitingQueueResourcePool) == gResourcePools.end()) { @@ -665,6 +663,8 @@ int APS5_VABI sceUltSemaphoreCreate(void* semaphore, const char* name, std::int3 } int APS5_VABI sceUltSemaphoreAcquire(void* semaphore, std::int32_t numResource) { + if (semaphore == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(semaphore) & 7u) != 0) return ULT_ERROR_ALIGNMENT; if (numResource <= 0) { return ULT_ERROR_RANGE; } @@ -687,6 +687,8 @@ int APS5_VABI sceUltSemaphoreAcquire(void* semaphore, std::int32_t numResource) } int APS5_VABI sceUltSemaphoreTryAcquire(void* semaphore, std::int32_t numResource) { + if (semaphore == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(semaphore) & 7u) != 0) return ULT_ERROR_ALIGNMENT; if (numResource <= 0) { return ULT_ERROR_RANGE; } @@ -706,6 +708,8 @@ int APS5_VABI sceUltSemaphoreTryAcquire(void* semaphore, std::int32_t numResourc } int APS5_VABI sceUltSemaphoreRelease(void* semaphore, std::int32_t numResource) { + if (semaphore == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(semaphore) & 7u) != 0) return ULT_ERROR_ALIGNMENT; if (numResource <= 0) { return ULT_ERROR_RANGE; } @@ -728,9 +732,8 @@ int APS5_VABI sceUltSemaphoreRelease(void* semaphore, std::int32_t numResource) } int APS5_VABI sceUltSemaphoreDestroy(void* semaphore) { - if (semaphore == nullptr) { - return ULT_ERROR_NULL; - } + if (semaphore == nullptr) return ULT_ERROR_NULL; + if ((reinterpret_cast(semaphore) & 7u) != 0) return ULT_ERROR_ALIGNMENT; std::lock_guard globalLock(gMutex); auto it = gSemaphores.find(semaphore); if (it == gSemaphores.end()) { @@ -739,7 +742,7 @@ int APS5_VABI sceUltSemaphoreDestroy(void* semaphore) { auto state = it->second; std::lock_guard stateLock(state->_mutex); if (state->_waiters != 0) { - return ULT_ERROR_BUSY; + return ULT_ERROR_STATE; } state->_alive = false; gSemaphores.erase(it); @@ -908,7 +911,7 @@ int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void* pool, const char* name return sceUltWaitingQueueResourcePoolCreate(pool, name, numThreads, numSyncObjects, workArea, optParam, 0); } -int APS5_VABI _sceUltSemaphoreCreate(void* semaphore, const char* name, std::int32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam) { +int APS5_VABI _sceUltSemaphoreCreate(void* semaphore, const char* name, std::uint32_t numInitialResource, void* waitingQueueResourcePool, const void* optParam) { if (semaphore == nullptr || name == nullptr) return ULT_ERROR_NULL; if (((reinterpret_cast(semaphore) | reinterpret_cast(waitingQueueResourcePool)) & 7u) != 0) return ULT_ERROR_ALIGNMENT; if (const int result = validateCommonOptParam(optParam); result != ULT_OK) return result; diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index 41d47b2622..e1fb8dbbba 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -24,7 +24,8 @@ int APS5_VABI _sceUltUlthreadRuntimeOptParamInitialize(void*, std::uint32_t); int APS5_VABI _sceUltUlthreadRuntimeCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*); int APS5_VABI _sceUltUlthreadCreate(void*, const char*, UltUlthreadEntry, std::uint64_t, void*, std::uint64_t, void*, const void*); int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*); -int APS5_VABI _sceUltSemaphoreCreate(void*, const char*, std::int32_t, void*, const void*); +int APS5_VABI _sceUltSemaphoreCreate(void*, const char*, std::uint32_t, void*, const void*); +int APS5_VABI sceUltSemaphoreTryAcquire(void*, std::int32_t); int APS5_VABI sceUltSemaphoreAcquire(void*, std::int32_t); int APS5_VABI sceUltSemaphoreRelease(void*, std::int32_t); int APS5_VABI _sceUltMutexOptParamInitialize(void*, std::uint32_t); @@ -192,7 +193,18 @@ static void UnderscoredCreate() { commonOptions[31] = 0; CHECK(_sceUltWaitingQueueResourcePoolCreate(pool, "pool", 1, 1, workArea, commonOptions) == Ok); CHECK(_sceUltSemaphoreCreate(nullptr, "semaphore", 1, pool, nullptr) == Null); - CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", -1, pool, nullptr) != Ok); + CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", UINT32_MAX, pool, nullptr) == Ok); + CHECK(sceUltSemaphoreTryAcquire(semaphore, 1) == Again); + CHECK(sceUltSemaphoreRelease(semaphore, 1) == Ok); + CHECK(sceUltSemaphoreTryAcquire(semaphore, 1) == Again); + CHECK(sceUltSemaphoreDestroy(semaphore) == Ok); + CHECK(sceUltSemaphoreAcquire(nullptr, 0) == Null); + CHECK(sceUltSemaphoreTryAcquire(nullptr, 0) == Null); + CHECK(sceUltSemaphoreRelease(nullptr, 0) == Null); + CHECK(sceUltSemaphoreAcquire(semaphore + 1, 0) == Alignment); + CHECK(sceUltSemaphoreTryAcquire(semaphore + 1, 0) == Alignment); + CHECK(sceUltSemaphoreRelease(semaphore + 1, 0) == Alignment); + CHECK(sceUltSemaphoreDestroy(semaphore + 1) == Alignment); CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, unknownPool, nullptr) != Ok); commonOptions[0] = 0; CHECK(_sceUltSemaphoreCreate(semaphore, "semaphore", 2, pool, commonOptions) == Invalid); From 61a44c79e22377308181abdc4d9b08291cdc2afd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:22:01 +0300 Subject: [PATCH 449/523] fix(videoout): restore flip master handle arguments --- core/libs/prx/libSceVideoOut/Unimplemented.cpp | 4 ++-- docs/dev/debt/libSceVideoOut.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceVideoOut/Unimplemented.cpp b/core/libs/prx/libSceVideoOut/Unimplemented.cpp index dabea7e04a..91b75fbb31 100644 --- a/core/libs/prx/libSceVideoOut/Unimplemented.cpp +++ b/core/libs/prx/libSceVideoOut/Unimplemented.cpp @@ -7,12 +7,12 @@ extern "C" { -int APS5_VABI sceVideoOutSetFlipMaster() { +int APS5_VABI sceVideoOutSetFlipMaster(std::int32_t handle, std::int32_t masterHandle) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVideoOutClearFlipMaster() { +int APS5_VABI sceVideoOutClearFlipMaster(std::int32_t handle) { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/docs/dev/debt/libSceVideoOut.md b/docs/dev/debt/libSceVideoOut.md index 8b62870d99..56dce0b93f 100644 --- a/docs/dev/debt/libSceVideoOut.md +++ b/docs/dev/debt/libSceVideoOut.md @@ -9,7 +9,6 @@ ### Unknown function info -- PPSA29866 imports declared without parameters, signatures unknown, added without an implementation because the title fails to load without them: [sceVideoOutSetFlipMaster and sceVideoOutClearFlipMaster](../../../core/libs/prx/libSceVideoOut/Unimplemented.cpp) (libSceVideoOut) - [sceVideoOutColorSpaceConversionSettingsSetDefaultSdrToHdrConversion_, sceVideoOutColorSpaceConversionSettingsSetSdrToHdrConversion_, sceVideoOutAdjustColorSpaceConversion_, sceVideoOutSetGlobalBlendSpace](../../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - PPSA30630 imports these to configure color space conversion and global blend space. Color space conversion settings are accepted with size validation (32 bytes) but conversion blocks and blend space parameters are not applied to the presentation swapchain - [sceVideoOutDeleteVrrActiveStatusEvent](../../../core/libs/prx/libSceVideoOut/src/Event.cpp) (libSceVideoOut, NID `-q-vbO859Tw`) - the name hashes to the NID PPSA02530 imports and is listed in [sce_symbols](https://github.com/zecoxao/sce_symbols) `aerolib.csv` at `fe2cc2948e`. The signature `(eq, handle)` and the behaviour, removing the handle's VRR status registration from the queue as the other `Delete*Event` calls do, follow sceVideoOutAddVrrActiveStatusEvent; no title has been observed calling it - [sceVideoOutCursorEnable, sceVideoOutCursorDisable, sceVideoOutCursorSetImageAddress, sceVideoOutCursorSetPosition](../../../core/libs/prx/libSceVideoOut/Unimplemented.cpp) (libSceVideoOut, NIDs `UPZWCH8qTM4`, `fEPEwROdCwM`, `u-9bhWQApq8`, `N17AK88NdD0`) - the names hash to the NIDs Prospero Win (PPSA99995) imports, and the parameter lists `(handle, index, address)`, `(handle, index)`, `(handle, index, address)` and `(handle, index, x, y)` follow its [declarations](https://github.com/mpereiraesaa/prospero-win/blob/644e32e400c555e340d507383398c5d21b39b0b0/wine/ps5/pw_vulkan_radv.c#L65-L68). The cursor image's format and size and the console's return values are not known; the functions report that they are not implemented. From fdcf961f5c059eafb2979999409d06cfc208d443 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:23:27 +0300 Subject: [PATCH 450/523] fix(pad): restore output report argument registers --- core/libs/prx/libScePad/Export.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index d66adfa338..34ff03cd2b 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -254,7 +254,7 @@ int APS5_VABI scePadGetFeatureReport(std::int32_t handle, std::uint8_t reportId, int APS5_VABI scePadSetFeatureReport(std::int32_t handle, std::uint8_t reportId, const void* data, std::size_t size) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI scePadOutputReport() { NotImplemented_nid_no_patch(__func__); return 0; } +int APS5_VABI scePadOutputReport(std::int32_t handle, std::uint8_t reportId, const void* data, std::uint64_t size) { NotImplemented_nid_no_patch(__func__); return 0; } int APS5_VABI scePadRead_nid_postfix(int handle, PadData* data, int num) { if (handle != PAD_HANDLE) return PAD_ERROR_INVALID_HANDLE; From 8de11f73eecd5398f3d276c3a41e2c9c182e1fd8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:27:03 +0300 Subject: [PATCH 451/523] fix(audiodec): validate aac sizes and extended mode ranges --- core/libs/prx/libSceAudiodec/Export.cpp | 18 ++++++++++--- core/libs/tests/GuestAudiodec.cpp | 34 +++++++++++++++++++++++++ docs/dev/debt/libSceAudiodec.md | 2 ++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceAudiodec/Export.cpp b/core/libs/prx/libSceAudiodec/Export.cpp index ffc1d6e1f7..5e2b911800 100644 --- a/core/libs/prx/libSceAudiodec/Export.cpp +++ b/core/libs/prx/libSceAudiodec/Export.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include "SceTypes.hpp" @@ -88,7 +90,7 @@ std::int32_t validateCtrl(const AudiodecCtrl* ctrl, std::uint32_t codecType) { if (infoSize != sizeof(AudiodecMp3Info)) return ERROR_INVALID_BSI_INFO_SIZE; break; default: - if (paramSize < sizeof(AudiodecParamM4aac)) return ERROR_INVALID_PARAM_SIZE; + if (paramSize != sizeof(AudiodecParamM4aac) && paramSize != 28 && paramSize != 32) return ERROR_INVALID_PARAM_SIZE; if (infoSize != sizeof(AudiodecM4aacInfo)) return ERROR_INVALID_BSI_INFO_SIZE; break; } @@ -133,12 +135,20 @@ void fillMp3Info(AudiodecMp3Info* info, const Audiodec::Mp3Header& header) { } std::int32_t createAac(const AudiodecParamM4aac* param, AudiodecM4aacInfo* info, Instance& instance) { + std::uint32_t extensionMode = 0; + std::uint32_t outputMode = 0; + const auto* bytes = reinterpret_cast(param); + if (param->ui_size >= 28) std::memcpy(&extensionMode, bytes + 24, sizeof(extensionMode)); + if (extensionMode > 1) return static_cast(0x807F0305); + if (param->ui_size == 32) std::memcpy(&outputMode, bytes + 28, sizeof(outputMode)); + if (outputMode > 2) return static_cast(0x807F0306); + if (extensionMode != 0 || outputMode != 0) throw std::runtime_error("M4AAC extended decoding modes are not implemented"); if (param->ui_config_number != M4AAC_CONFIG_ADTS && param->ui_config_number != M4AAC_CONFIG_RAW) return ERROR_M4AAC_INVALID_CONFIG_NUMBER; const std::uint32_t sampleRate = Audiodec::AacSampleRate(param->ui_sampling_freq_index); if (param->ui_config_number == M4AAC_CONFIG_RAW && sampleRate == 0) return ERROR_M4AAC_INVALID_SAMPLING_FREQ; - if (param->ui_max_channels > 8) return ERROR_M4AAC_INVALID_MAX_CHANNELS; - if (param->ui_enable_heaac > 1) return ERROR_M4AAC_INVALID_ENABLE_HEAAC; - const std::uint32_t channels = param->ui_max_channels == 0 ? 2 : param->ui_max_channels; + if (param->ui_max_channels != 1 && param->ui_max_channels != 2 && param->ui_max_channels != 6 && param->ui_max_channels != 8) return ERROR_M4AAC_INVALID_MAX_CHANNELS; + if (param->ui_enable_heaac > 1 || (param->ui_enable_heaac == 1 && param->ui_max_channels > 2)) return ERROR_M4AAC_INVALID_ENABLE_HEAAC; + const std::uint32_t channels = param->ui_max_channels; instance.decoder = Audiodec::CreateAac(param->i_bw_pcm, param->ui_config_number == M4AAC_CONFIG_ADTS, param->ui_sampling_freq_index, channels, param->ui_enable_heaac == 1); instance.channels = channels; instance.sampleRate = sampleRate; diff --git a/core/libs/tests/GuestAudiodec.cpp b/core/libs/tests/GuestAudiodec.cpp index 404e7fce05..3eb7a370db 100644 --- a/core/libs/tests/GuestAudiodec.cpp +++ b/core/libs/tests/GuestAudiodec.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include extern "C" { @@ -410,6 +411,39 @@ int main() { aac.i_bw_pcm = 2; const std::int32_t floatAac = sceAudiodecCreateDecoder(&aacCtrl, 3); Require(floatAac > 0 && sceAudiodecDeleteDecoder(floatAac) == 0); + for (const std::uint32_t channels : {0u, 3u, 4u, 5u, 7u, 9u}) { + aac.ui_max_channels = channels; + Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0304)); + } + aac.ui_max_channels = 6; + aac.ui_enable_heaac = 1; + Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0302)); + struct ExtendedParam { + AudiodecParamM4aac base; + std::uint32_t extensionMode; + std::uint32_t outputMode; + } extended{{32, 1, 1, 3, 2, 0}, 0, 0}; + aacCtrl.pParam = &extended; + for (const std::uint32_t size : {25u, 27u, 29u, 31u, 33u}) { + extended.base.ui_size = size; + Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == errorInvalidParamSize); + } + for (const std::uint32_t size : {28u, 32u}) { + extended.base.ui_size = size; + extended.extensionMode = 2; + Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0305)); + extended.extensionMode = 1; + bool threw = false; + try { sceAudiodecCreateDecoder(&aacCtrl, 3); } + catch (const std::runtime_error&) { threw = true; } + Require(threw); + extended.extensionMode = 0; + const auto extendedHandle = sceAudiodecCreateDecoder(&aacCtrl, 3); + Require(extendedHandle > 0); + Require(sceAudiodecDeleteDecoder(extendedHandle) == 0); + } + extended.outputMode = 3; + Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0306)); const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 2); Require(handle > 0); diff --git a/docs/dev/debt/libSceAudiodec.md b/docs/dev/debt/libSceAudiodec.md index 15c8c1cbe1..ac9b492ea4 100644 --- a/docs/dev/debt/libSceAudiodec.md +++ b/docs/dev/debt/libSceAudiodec.md @@ -2,6 +2,8 @@ ### Unknown function info +- [M4AAC extended parameters](../../../core/libs/prx/libSceAudiodec/Export.cpp): 28-byte and 32-byte parameter variants add fields at offsets 24 and 28. Their accepted ranges are 0..1 and 0..2; nonzero modes throw until the corresponding decoder configuration is implemented. Zero extensions use the base decoding configuration. + - [sceAudiodecDecode](../../../core/libs/prx/libSceAudiodec/src/Codecs.cpp) (libSceAudiodec) - a raw M4AAC stream carries no channel configuration, so the decoder tries the mono/stereo configuration and then configurations 3 to 7 within `ui_max_channels` until one decodes the first access unit, and keeps that one. No reference documents how the library picks the layout (shadPS4 maps the channel count straight to the configuration; SharpProspero only describes the maximum). With the stereo configuration a mono raw stream decodes as 2 channels, so 2 is reported, not 1 - [sceAudiodecDecode](../../../core/libs/prx/libSceAudiodec/src/Codecs.cpp) (libSceAudiodec) - the HE-AAC handling follows one console measurement of an HE-AAC v2 ADTS stream: SBR output at 48 kHz in one channel with `ui_enable_heaac` 1, the 24 kHz mono core with 0. Not measured: whether the console, like FFmpeg, decodes a stream without SBR data in its first access unit as AAC-LC with `ui_enable_heaac` 1, and what it reports in `ui_sampling_freq` and `ui_heaac` with `ui_enable_heaac` 0 (here the rate of the returned PCM and 0) From 5a2274e2412e9ea5bef23d0d3a1810f4ccfd3871 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:41:45 +0300 Subject: [PATCH 452/523] fix(dialog): validate active users before opening --- core/libs/prx/libSceCdlgG2P/CMakeLists.txt | 3 ++- core/libs/prx/libSceCdlgG2P/Export.cpp | 5 +++++ .../prx/libSceCdlgPlayerReview/CMakeLists.txt | 3 ++- core/libs/prx/libSceCdlgPlayerReview/Export.cpp | 5 +++++ core/libs/tests/GuestG2PDialog.cpp | 14 ++++++++++++-- core/libs/tests/GuestPlayerReviewDialog.cpp | 15 +++++++++++++-- docs/dev/debt/libSceCdlgG2P.md | 2 +- docs/dev/debt/libSceCdlgPlayerReview.md | 2 +- 8 files changed, 41 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceCdlgG2P/CMakeLists.txt b/core/libs/prx/libSceCdlgG2P/CMakeLists.txt index 9809109f69..f15414c733 100644 --- a/core/libs/prx/libSceCdlgG2P/CMakeLists.txt +++ b/core/libs/prx/libSceCdlgG2P/CMakeLists.txt @@ -1,11 +1,12 @@ add_library(libSceCdlgG2P SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceCdlgG2P PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceCdlgG2P) +target_link_libraries(libSceCdlgG2P PRIVATE libSceUserService) if(BUILD_TESTING) add_test_executable(guest_g2p_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestG2PDialog.cpp) target_include_directories(guest_g2p_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_g2p_dialog_tests PRIVATE libSceCdlgG2P) + target_link_libraries(guest_g2p_dialog_tests PRIVATE libSceCdlgG2P libSceUserService) add_test(NAME guest_g2p_dialog COMMAND guest_g2p_dialog_tests) if(WIN32) set_tests_properties(guest_g2p_dialog PROPERTIES ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceCdlgG2P/Export.cpp b/core/libs/prx/libSceCdlgG2P/Export.cpp index 6f402b651d..43abff189e 100644 --- a/core/libs/prx/libSceCdlgG2P/Export.cpp +++ b/core/libs/prx/libSceCdlgG2P/Export.cpp @@ -1,3 +1,4 @@ +#include "SceTypes.hpp" #include #include #include @@ -6,6 +7,8 @@ #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +extern "C" int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* users); + namespace { constexpr int COMMON_DIALOG_STATUS_NONE = 0; @@ -38,6 +41,8 @@ int APS5_VABI sceG2PDialogOpen(const G2PDialogParam* param) { if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; if (!ValidateCommonDialogBaseParam(param->base) || param->size != sizeof(*param) || param->userId == -1 || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + UserServiceLoginUserIdList users{}; + if (sceUserServiceGetLoginUserIdList(&users) != 0 || std::none_of(std::begin(users.user_id), std::end(users.user_id), [param](std::int32_t userId) { return userId != -1 && userId == param->userId; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/prx/libSceCdlgPlayerReview/CMakeLists.txt b/core/libs/prx/libSceCdlgPlayerReview/CMakeLists.txt index 7b4466af1a..f3aa2fabdc 100644 --- a/core/libs/prx/libSceCdlgPlayerReview/CMakeLists.txt +++ b/core/libs/prx/libSceCdlgPlayerReview/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(libSceCdlgPlayerReview SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceCdlgPlayerReview PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceCdlgPlayerReview) +target_link_libraries(libSceCdlgPlayerReview PRIVATE libSceUserService) if(MINGW) foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static) @@ -11,7 +12,7 @@ endif() if(BUILD_TESTING) add_test_executable(guest_player_review_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestPlayerReviewDialog.cpp) target_include_directories(guest_player_review_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_player_review_dialog_tests PRIVATE libSceCdlgPlayerReview) + target_link_libraries(guest_player_review_dialog_tests PRIVATE libSceCdlgPlayerReview libSceUserService) add_test(NAME guest_player_review_dialog COMMAND guest_player_review_dialog_tests) if(WIN32) set_tests_properties(guest_player_review_dialog PROPERTIES ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceCdlgPlayerReview/Export.cpp b/core/libs/prx/libSceCdlgPlayerReview/Export.cpp index 2380fd4630..4db4e1760e 100644 --- a/core/libs/prx/libSceCdlgPlayerReview/Export.cpp +++ b/core/libs/prx/libSceCdlgPlayerReview/Export.cpp @@ -1,9 +1,12 @@ +#include "SceTypes.hpp" #include #include #include "prx/libc/include/general/VabiMacros.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +extern "C" int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* users); + namespace { std::atomic g_status{0}; @@ -42,6 +45,8 @@ int APS5_VABI scePlayerReviewDialogOpen(const PlayerReviewDialogParam* param) { if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; if (!ValidateCommonDialogBaseParam(param->base) || param->size != sizeof(*param) || param->userId == -1 || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; }) || param->mode > 1) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + UserServiceLoginUserIdList users{}; + if (sceUserServiceGetLoginUserIdList(&users) != 0 || std::none_of(std::begin(users.user_id), std::end(users.user_id), [param](std::int32_t userId) { return userId != -1 && userId == param->userId; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestG2PDialog.cpp b/core/libs/tests/GuestG2PDialog.cpp index 9a655706a9..a318e77f68 100644 --- a/core/libs/tests/GuestG2PDialog.cpp +++ b/core/libs/tests/GuestG2PDialog.cpp @@ -1,9 +1,12 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include "prx/libSceUserService/UserService.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include extern "C" { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* param); +int APS5_VABI sceUserServiceTerminate(); int APS5_VABI sceG2PDialogInitialize(void); int APS5_VABI sceG2PDialogOpen(const void* param); int APS5_VABI sceG2PDialogUpdateStatus(void); @@ -29,11 +32,12 @@ constexpr int kResultUserCanceled = 1; } int main() { + Require(sceUserServiceInitialize(nullptr) == 0); G2PDialogParam param{}; param.base.size = sizeof(param.base); param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; param.size = sizeof(param); - param.userId = 1; + param.userId = USER_SERVICE_INITIAL_USER_ID; std::int32_t result[4] = {-1, -1, -1, -1}; Require(sceG2PDialogTerminate() == kErrNotInitialized); @@ -67,7 +71,12 @@ int main() { param.reserved[63] = 0; param.userId = -1; Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); - param.userId = 1; + param.userId = 42; + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.userId = USER_SERVICE_INITIAL_USER_ID; + Require(sceUserServiceTerminate() == 0); + Require(sceG2PDialogOpen(¶m) == static_cast(0x80B8000Au)); + Require(sceUserServiceInitialize(nullptr) == 0); Require(sceG2PDialogOpen(¶m) == 0); Require(sceG2PDialogGetStatus() == kStatusFinished); Require(sceG2PDialogUpdateStatus() == kStatusFinished); @@ -92,4 +101,5 @@ int main() { Require(result[0] == -1 && result[1] == -1 && result[3] == -1); Require(result[2] == -1); Require(sceG2PDialogTerminate() == 0); + Require(sceUserServiceTerminate() == 0); } diff --git a/core/libs/tests/GuestPlayerReviewDialog.cpp b/core/libs/tests/GuestPlayerReviewDialog.cpp index cb6c65a423..45621694d4 100644 --- a/core/libs/tests/GuestPlayerReviewDialog.cpp +++ b/core/libs/tests/GuestPlayerReviewDialog.cpp @@ -1,9 +1,12 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include +#include "prx/libSceUserService/UserService.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include extern "C" { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* param); +int APS5_VABI sceUserServiceTerminate(); int APS5_VABI scePlayerReviewDialogInitialize(void); int APS5_VABI scePlayerReviewDialogTerminate(void); int APS5_VABI scePlayerReviewDialogOpen(const void* param); @@ -28,11 +31,12 @@ void Require(bool value) { if (!value) std::abort(); } } int main() { + Require(sceUserServiceInitialize(nullptr) == 0); PlayerReviewDialogParam param{}; param.base.size = sizeof(param.base); param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; param.size = sizeof(param); - param.userId = 1; + param.userId = USER_SERVICE_INITIAL_USER_ID; std::int32_t result[4] = {-1, -1, -1, -1}; Require(scePlayerReviewDialogTerminate() == COMMON_DIALOG_ERROR_NOT_INITIALIZED); Require(scePlayerReviewDialogOpen(nullptr) == static_cast(0x80B8000Du)); @@ -68,10 +72,16 @@ int main() { param.reserved[63] = 0; param.userId = -1; Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); - param.userId = 1; + param.userId = USER_SERVICE_INITIAL_USER_ID; param.mode = 2; Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); param.mode = 0; + param.userId = 42; + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.userId = USER_SERVICE_INITIAL_USER_ID; + Require(sceUserServiceTerminate() == 0); + Require(scePlayerReviewDialogOpen(¶m) == static_cast(0x80B8000Au)); + Require(sceUserServiceInitialize(nullptr) == 0); Require(scePlayerReviewDialogOpen(¶m) == 0); Require(scePlayerReviewDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_FINISHED); @@ -95,4 +105,5 @@ int main() { Require(result[2] == -1); Require(scePlayerReviewDialogTerminate() == 0); Require(scePlayerReviewDialogUpdateStatus() == COMMON_DIALOG_STATUS_NONE); + Require(sceUserServiceTerminate() == 0); } diff --git a/docs/dev/debt/libSceCdlgG2P.md b/docs/dev/debt/libSceCdlgG2P.md index f35f20d05d..ad18657b3d 100644 --- a/docs/dev/debt/libSceCdlgG2P.md +++ b/docs/dev/debt/libSceCdlgG2P.md @@ -2,4 +2,4 @@ ### Silent stubs -- [libSceCdlgG2P](../../../core/libs/prx/libSceCdlgG2P/Export.cpp) - the G2P dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. The 128-byte open parameters, common-header validation and result field at byte 8 are recovered. Active-user verification and native service state transitions remain unimplemented; PPSA01289 imports it +- [libSceCdlgG2P](../../../core/libs/prx/libSceCdlgG2P/Export.cpp) - the G2P dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. The 128-byte open parameters, common-header validation and result field at byte 8 are recovered. Native service state transitions remain unimplemented; PPSA01289 imports it diff --git a/docs/dev/debt/libSceCdlgPlayerReview.md b/docs/dev/debt/libSceCdlgPlayerReview.md index ae7ee87b57..0e97a3d72d 100644 --- a/docs/dev/debt/libSceCdlgPlayerReview.md +++ b/docs/dev/debt/libSceCdlgPlayerReview.md @@ -2,7 +2,7 @@ ### Unknown function info -- [scePlayerReviewDialogOpen](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) uses a recovered 168-byte parameter layout; the 40-byte target record at byte 60 and mode semantics remain unresolved. Active-user verification is not implemented. +- [scePlayerReviewDialogOpen](../../../core/libs/prx/libSceCdlgPlayerReview/Export.cpp) uses a recovered 168-byte parameter layout; the 40-byte target record at byte 60 and mode semantics remain unresolved. ### Functional From 266764fba740cf3ef302feb8e9043acf34856360 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:41:46 +0300 Subject: [PATCH 453/523] fix(vrtracker): restore result query parameter fields --- core/libs/prx/libSceVrTracker/Export.cpp | 16 +++++++++++++++- docs/dev/debt/libSceVrTracker.md | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceVrTracker/Export.cpp b/core/libs/prx/libSceVrTracker/Export.cpp index 6ca29bfe49..14776d3483 100644 --- a/core/libs/prx/libSceVrTracker/Export.cpp +++ b/core/libs/prx/libSceVrTracker/Export.cpp @@ -51,7 +51,17 @@ struct VrTrackerGpuWaitParam { struct VrTrackerGetResultParam { std::uint32_t size; - std::byte payload[52]; + std::int32_t handle; + std::uint32_t mode; + std::uint32_t extendedResult; + std::uint64_t time; + std::uint32_t statusMode; + std::uint32_t reserved0; + std::uint32_t option32; + std::byte outputValue[4]; + std::uint32_t option40; + std::uint32_t reserved1[2]; + std::byte unused[4]; }; static_assert(sizeof(VrTrackerCpuProcessParam) == 32); @@ -59,6 +69,10 @@ static_assert(offsetof(VrTrackerCpuProcessParam, mode) == 4); static_assert(offsetof(VrTrackerCpuProcessParam, argument) == 8); static_assert(sizeof(VrTrackerGpuWaitParam) == 32); static_assert(sizeof(VrTrackerGetResultParam) == 56); +static_assert(offsetof(VrTrackerGetResultParam, time) == 16); +static_assert(offsetof(VrTrackerGetResultParam, statusMode) == 24); +static_assert(offsetof(VrTrackerGetResultParam, outputValue) == 36); +static_assert(offsetof(VrTrackerGetResultParam, reserved1) == 44); extern "C" { diff --git a/docs/dev/debt/libSceVrTracker.md b/docs/dev/debt/libSceVrTracker.md index da28022685..138b88a370 100644 --- a/docs/dev/debt/libSceVrTracker.md +++ b/docs/dev/debt/libSceVrTracker.md @@ -2,7 +2,7 @@ ### Unknown function info -- [VrTracker parameters](../../../core/libs/prx/libSceVrTracker/Export.cpp): argument slots are recovered. CPU processing and GPU waiting consume 32-byte records, result queries consume 56-byte records, and time queries write 64 bits. Initialization accepts 128-byte and 144-byte versions; the 144-byte layout includes three address/size/alignment records, but configuration selector semantics and the older field layout remain unresolved. GPU submission accepts 552-byte and 648-byte versions; submission and result records still require field recovery; result storage has 1520-byte and 3640-byte variants. CPU mode/argument semantics and the remaining query fields are unresolved. Protected bodies remain unchanged. +- [VrTracker parameters](../../../core/libs/prx/libSceVrTracker/Export.cpp): argument slots are recovered. CPU processing and GPU waiting consume 32-byte records, result queries consume 56-byte records, and time queries write 64 bits. Initialization accepts 128-byte and 144-byte versions; the 144-byte layout includes three address/size/alignment records, but configuration selector semantics and the older field layout remain unresolved. GPU submission accepts 552-byte and 648-byte versions; submission and result records still require field recovery; result storage has 1520-byte and 3640-byte variants. Result-query records have a signed handle, 64-bit time, 32-bit selectors and an extended-result flag; selector meanings and the four-byte value copied unchanged to result offset 448 remain unresolved. CPU mode/argument semantics are unresolved. Protected bodies remain unchanged. - [sceVrTrackerCpuPopMarker, sceVrTrackerCpuPushMarker, sceVrTrackerGetLiveCaptureId, sceVrTrackerGpuWaitAndCpuProcess, sceVrTrackerResetAll, sceVrTrackerSaveInternalBuffers and sceVrTrackerSetDeviceRejection](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. - [sceVrTrackerSetExtendedMode, sceVrTrackerSetLEDBrightness, sceVrTrackerSetRestingMode, sceVrTrackerStartLiveCapture, sceVrTrackerStopLiveCapture and sceVrTrackerTerm](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). No credible prototypes were found; these no-parameter declarations only resolve imports, make no ABI claim and must not be called until signatures are verified. - [sceVrTrackerGetPlayAreaWarningInfo, sceVrTrackerNotifyEndOfCpuProcess, sceVrTrackerQueryMemory, sceVrTrackerRecalibrate, sceVrTrackerRegisterDevice and sceVrTrackerRegisterDevice2](../../../core/libs/prx/libSceVrTracker/Export.cpp) (libSceVrTracker) - the list is the intersection of the export table of the console's libSceVrTracker (function symbols only), pinned aerolib names and functions not yet defined anywhere in the repo. [Inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceVrTracker.c); [aerolib](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Parameter shapes follow the [pinned shadPS4 declarations](https://github.com/shadps4-emu/shadPS4/blob/f9d07487dd85bb7dc522df6de109b8ad4b9b7175/src/core/libraries/vr_tracker/vr_tracker.h), not measured on a PS5. From 464470f7ae1ea431955ec513f66bf66b7fcdbf45 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:41:47 +0300 Subject: [PATCH 454/523] fix(npcommerce): restore extended dialog parameter layout --- .../prx/libSceCommonDialog/CommonDialog.hpp | 20 +++++++++++++++++++ core/libs/prx/libSceNpCommerce/Export.cpp | 4 ++-- core/libs/tests/GuestNpCommerceDialog.cpp | 12 ++++++++--- docs/dev/debt/libSceNpCommerce.md | 2 +- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index d16c6ad73d..c2f5d66d63 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,26 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct NpCommerceDialogParam2 { + CommonDialogBaseParam base; + std::uint32_t size; + std::int32_t userId; + std::uint32_t mode; + std::uint32_t option; + std::uint32_t contentMode; + std::uint32_t padding0; + const char* const* products; + std::uint32_t productCount; + std::uint32_t padding1; + std::uint64_t argument; + std::uint64_t userData; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(NpCommerceDialogParam2) == 136); +static_assert(offsetof(NpCommerceDialogParam2, products) == 72); +static_assert(offsetof(NpCommerceDialogParam2, userData) == 96); + struct WebBrowserDialogExtendedResult { std::uint64_t size; std::uint32_t type; diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index 2fd73aeb90..60f96cb34c 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -2,6 +2,7 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" namespace { @@ -11,7 +12,6 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80006u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B80009u); @@ -79,7 +79,7 @@ int APS5_VABI sceNpCommerceSetPsStoreIconLayout(int layout) { return 0; } -int APS5_VABI sceNpCommerceDialogOpen2(void) { +int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param) { g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 7b7239959f..298022419c 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -1,4 +1,5 @@ #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include #include @@ -7,7 +8,7 @@ extern "C" { int APS5_VABI sceNpCommerceDialogInitialize(); int APS5_VABI sceNpCommerceDialogOpen(const void* param); -int APS5_VABI sceNpCommerceDialogOpen2(void); +int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param); int APS5_VABI sceNpCommerceDialogUpdateStatus(void); int APS5_VABI sceNpCommerceDialogGetStatus(void); int APS5_VABI sceNpCommerceDialogGetResult(void* result); @@ -38,11 +39,16 @@ void RequireCanceledResult() { } int main() { + NpCommerceDialogParam2 param2{}; + param2.base.size = sizeof(param2.base); + param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; + param2.size = sizeof(param2); + param2.userId = 1; Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceNpCommerceDialogInitialize() == 0); Require(sceNpCommerceDialogGetStatus() == statusInitialized); - Require(sceNpCommerceDialogOpen2() == 0); + Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); Require(sceNpCommerceDialogGetStatus() == statusFinished); RequireCanceledResult(); @@ -55,7 +61,7 @@ int main() { alignas(8) std::uint8_t param[0x40] = {}; Require(sceNpCommerceDialogInitialize() == 0); Require(sceNpCommerceDialogOpen(param) == 0); - Require(sceNpCommerceDialogOpen2() == 0); + Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); RequireCanceledResult(); Require(sceNpCommerceDialogTerminate() == 0); diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index d9ac7be185..b918bd1717 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -7,4 +7,4 @@ ### Unknown function info - [sceNpCommerceDialogInitializeInternal](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpCommerce.c); the no-parameter prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_commerce/np_commerce.cpp#L61) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. -- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - declared without parameters; follows [prosper](https://github.com/mattias800/prosper/blob/73413cb445b0364bb49c4d2478e9ee1614b9f7d9/prosper/src/hle/np/np.cpp#L500-L504), which serves it with its `sceNpCommerceDialogOpen` handler: every argument is ignored, nothing is validated, it returns 0 and the dialog finishes at once, so `sceNpCommerceDialogGetResult` reports it cancelled. The parameters and the console's error results are unknown. PPSA34501 calls it at Zombies > Online +- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): the single pointer argument and 136-byte layout are recovered, including the product pointer array, count and result user-data value. User checks, mode validation, SDK-dependent restrictions and native error results remain unimplemented; the host dialog still finishes immediately as cancelled. Mode-specific meanings of the option and 64-bit argument remain unresolved. From 8ff0554ff4dc6431deae6fc2ab2a6c299225c9ad Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:42:45 +0300 Subject: [PATCH 455/523] fix(npcommerce): restore dialog lifecycle errors --- core/libs/prx/libSceNpCommerce/Export.cpp | 10 ++++++++-- core/libs/tests/GuestNpCommerceDialog.cpp | 10 +++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index 60f96cb34c..245233008e 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -9,11 +9,14 @@ namespace { constexpr int COMMON_DIALOG_STATUS_NONE = 0; constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; +constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80006u); -constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B80009u); +constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); +constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); + +constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); int g_status = COMMON_DIALOG_STATUS_NONE; @@ -45,6 +48,7 @@ int APS5_VABI sceNpCommerceDialogGetStatus(void) { int APS5_VABI sceNpCommerceDialogGetResult(void* result) { if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (g_status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; // SceNpCommerceDialogResult: int32 result, bool authorized, then reserved bytes. std::int32_t value = COMMON_DIALOG_RESULT_USER_CANCELED; @@ -56,11 +60,13 @@ int APS5_VABI sceNpCommerceDialogGetResult(void* result) { int APS5_VABI sceNpCommerceDialogClose(void) { if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } int APS5_VABI sceNpCommerceDialogTerminate() { + if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; g_status = COMMON_DIALOG_STATUS_NONE; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 298022419c..687a158fa1 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -45,7 +45,15 @@ int main() { param2.size = sizeof(param2); param2.userId = 1; Require(sceNpCommerceDialogGetStatus() == statusNone); + Require(sceNpCommerceDialogTerminate() == static_cast(0x80B80003u)); + Require(sceNpCommerceDialogClose() == static_cast(0x80B80003u)); + Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); + std::uint8_t result[48]{}; + Require(sceNpCommerceDialogGetResult(result) == static_cast(0x80B80003u)); Require(sceNpCommerceDialogInitialize() == 0); + Require(sceNpCommerceDialogClose() == static_cast(0x80B8000Bu)); + Require(sceNpCommerceDialogGetResult(result) == static_cast(0x80B80005u)); + Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); Require(sceNpCommerceDialogGetStatus() == statusInitialized); Require(sceNpCommerceDialogOpen2(¶m2) == 0); @@ -53,7 +61,7 @@ int main() { Require(sceNpCommerceDialogGetStatus() == statusFinished); RequireCanceledResult(); - Require(sceNpCommerceDialogClose() == 0); + Require(sceNpCommerceDialogClose() == static_cast(0x80B8000Bu)); Require(sceNpCommerceDialogGetStatus() == statusFinished); Require(sceNpCommerceDialogTerminate() == 0); Require(sceNpCommerceDialogGetStatus() == statusNone); From 878e5bdd5d549ca2b1a1dbdb6bdd794d219e4fbe Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:44:08 +0300 Subject: [PATCH 456/523] fix(npcommerce): validate dialog base parameters --- .../prx/libSceCommonDialog/CommonDialog.hpp | 18 +++++++++++++++ core/libs/prx/libSceNpCommerce/Export.cpp | 9 ++++++-- core/libs/tests/GuestNpCommerceDialog.cpp | 22 ++++++++++++++++--- docs/dev/debt/libSceNpCommerce.md | 2 +- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index c2f5d66d63..a9bea03571 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,24 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct NpCommerceDialogParam { + CommonDialogBaseParam base; + std::uint32_t size; + std::int32_t userId; + std::uint32_t mode; + std::uint32_t option; + const char* const* products; + std::uint32_t productCount; + std::uint32_t padding; + std::uint64_t argument; + std::uint64_t userData; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(NpCommerceDialogParam) == 128); +static_assert(offsetof(NpCommerceDialogParam, products) == 64); +static_assert(offsetof(NpCommerceDialogParam, userData) == 88); + struct NpCommerceDialogParam2 { CommonDialogBaseParam base; std::uint32_t size; diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index 245233008e..23b6481670 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -16,6 +16,7 @@ constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); int g_status = COMMON_DIALOG_STATUS_NONE; @@ -31,9 +32,10 @@ int APS5_VABI sceNpCommerceDialogInitialize() { return 0; } -int APS5_VABI sceNpCommerceDialogOpen(const void* param) { - if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; +int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param) { if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -86,6 +88,9 @@ int APS5_VABI sceNpCommerceSetPsStoreIconLayout(int layout) { } int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 687a158fa1..16f5bf3065 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -7,7 +7,7 @@ extern "C" { int APS5_VABI sceNpCommerceDialogInitialize(); -int APS5_VABI sceNpCommerceDialogOpen(const void* param); +int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param); int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param); int APS5_VABI sceNpCommerceDialogUpdateStatus(void); int APS5_VABI sceNpCommerceDialogGetStatus(void); @@ -45,6 +45,15 @@ int main() { param2.size = sizeof(param2); param2.userId = 1; Require(sceNpCommerceDialogGetStatus() == statusNone); + Require(sceNpCommerceDialogOpen(nullptr) == static_cast(0x80B8000Du)); + Require(sceNpCommerceDialogOpen2(nullptr) == static_cast(0x80B8000Du)); + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B80003u)); + param2.base.magic = 0; + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); + param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; + param2.base.reserved[35] = 1; + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); + param2.base.reserved[35] = 0; Require(sceNpCommerceDialogTerminate() == static_cast(0x80B80003u)); Require(sceNpCommerceDialogClose() == static_cast(0x80B80003u)); Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); @@ -66,9 +75,16 @@ int main() { Require(sceNpCommerceDialogTerminate() == 0); Require(sceNpCommerceDialogGetStatus() == statusNone); - alignas(8) std::uint8_t param[0x40] = {}; + NpCommerceDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = 1; Require(sceNpCommerceDialogInitialize() == 0); - Require(sceNpCommerceDialogOpen(param) == 0); + param.base.size = 0; + Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.base.size = sizeof(param.base); + Require(sceNpCommerceDialogOpen(¶m) == 0); Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); RequireCanceledResult(); diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index b918bd1717..ca3ca354d6 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -7,4 +7,4 @@ ### Unknown function info - [sceNpCommerceDialogInitializeInternal](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpCommerce.c); the no-parameter prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_commerce/np_commerce.cpp#L61) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. -- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): the single pointer argument and 136-byte layout are recovered, including the product pointer array, count and result user-data value. User checks, mode validation, SDK-dependent restrictions and native error results remain unimplemented; the host dialog still finishes immediately as cancelled. Mode-specific meanings of the option and 64-bit argument remain unresolved. +- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): the single pointer argument and 136-byte layout are recovered, including the product pointer array, count and result user-data value. User checks, mode validation, process-version-dependent restrictions and result-buffer semantics remain unimplemented; the host dialog still finishes immediately as cancelled. Mode-specific meanings of the option and 64-bit argument remain unresolved. From eeabc8d176892f2374c821f1f6527851e9557cb2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:45:44 +0300 Subject: [PATCH 457/523] fix(npcommerce): validate product modes and sdk constraints --- core/libs/prx/libSceNpCommerce/CMakeLists.txt | 3 +- core/libs/prx/libSceNpCommerce/Export.cpp | 37 +++++++++++ core/libs/tests/GuestNpCommerceDialog.cpp | 66 ++++++++++++++++++- docs/dev/debt/libSceNpCommerce.md | 2 + 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpCommerce/CMakeLists.txt b/core/libs/prx/libSceNpCommerce/CMakeLists.txt index ec34bf1230..1c59d83752 100644 --- a/core/libs/prx/libSceNpCommerce/CMakeLists.txt +++ b/core/libs/prx/libSceNpCommerce/CMakeLists.txt @@ -1,11 +1,12 @@ add_library(libSceNpCommerce SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceNpCommerce PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceNpCommerce) +target_link_libraries(libSceNpCommerce PRIVATE libc libSceUserService) if(BUILD_TESTING) add_test_executable(guest_np_commerce_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpCommerceDialog.cpp) target_include_directories(guest_np_commerce_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_commerce_dialog_tests PRIVATE libSceNpCommerce libc) + target_link_libraries(guest_np_commerce_dialog_tests PRIVATE libSceNpCommerce libc libSceUserService) configure_windows_unwind(guest_np_commerce_dialog_tests) add_test(NAME guest_np_commerce_dialog COMMAND guest_np_commerce_dialog_tests) if(WIN32) diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index 23b6481670..213732b38a 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -1,10 +1,13 @@ #include #include #include +#include "prx/libc/include/ApplicationHeap.hpp" #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +extern "C" int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* users); + namespace { constexpr int COMMON_DIALOG_STATUS_NONE = 0; @@ -21,6 +24,39 @@ constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); int g_status = COMMON_DIALOG_STATUS_NONE; +std::uint32_t compiledSdkVersion() { + const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t version; + std::memcpy(&version, process + 16, sizeof(version)); + return version; +} + +bool isLoggedIn(std::int32_t userId) { + UserServiceLoginUserIdList users{}; + return sceUserServiceGetLoginUserIdList(&users) == 0 && userId != -1 && std::find(std::begin(users.user_id), std::end(users.user_id), userId) != std::end(users.user_id); +} + +bool validateParam(const NpCommerceDialogParam& param) { + const auto sdkVersion = compiledSdkVersion(); + if (sdkVersion >= 0x01700000 && (param.size != sizeof(param) || !std::all_of(std::begin(param.reserved), std::end(param.reserved), [](std::uint8_t value) { return value == 0; }) || !isLoggedIn(param.userId) || param.option > 7)) return false; + switch (param.mode) { + case 0: + return param.productCount <= 1 && (param.products != nullptr) == (param.productCount != 0); + case 1: + return param.productCount == 1 && param.products != nullptr; + case 2: + return param.productCount <= 1 && (param.productCount != 0 ? param.products != nullptr : sdkVersion < 0x01700000 || param.products == nullptr); + case 3: + return param.productCount >= 1 && param.productCount <= 10 && param.products != nullptr; + case 4: + return param.productCount <= 10 && (param.productCount != 0 ? param.products != nullptr : sdkVersion < 0x01700000 || param.products == nullptr); + case 5: + return param.productCount == 0 && (sdkVersion >= 0x03500000 ? param.argument == 1 : (param.argument & 3) != 0 && (sdkVersion < 0x01700000 || param.argument < 4)); + default: + return false; + } +} + } extern "C" { @@ -36,6 +72,7 @@ int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param) { if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (!validateParam(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 16f5bf3065..bc0b204659 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -1,3 +1,6 @@ +#include "ProcessParametersFixture.hpp" +#include "prx/libSceUserService/UserService.hpp" +#include #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" @@ -6,6 +9,8 @@ #include extern "C" { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* param); +int APS5_VABI sceUserServiceTerminate(); int APS5_VABI sceNpCommerceDialogInitialize(); int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param); int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param); @@ -20,6 +25,8 @@ namespace { void Require(bool value) { if (!value) std::abort(); } +alignas(16) std::array process{}; + constexpr int statusNone = 0; constexpr int statusInitialized = 1; constexpr int statusFinished = 3; @@ -39,6 +46,10 @@ void RequireCanceledResult() { } int main() { + Require(ProcessParametersFixture::Install(process.data(), process.size())); + std::uint32_t sdkVersion = 0x03500000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(sceUserServiceInitialize(nullptr) == 0); NpCommerceDialogParam2 param2{}; param2.base.size = sizeof(param2.base); param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; @@ -79,16 +90,69 @@ int main() { param.base.size = sizeof(param.base); param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; param.size = sizeof(param); - param.userId = 1; + param.userId = USER_SERVICE_INITIAL_USER_ID; Require(sceNpCommerceDialogInitialize() == 0); param.base.size = 0; Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); param.base.size = sizeof(param.base); + param.size = 0; + Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.size = sizeof(param); + param.option = 8; + Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.option = 0; + param.reserved[31] = 1; + Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.reserved[31] = 0; + param.userId = -1; + Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.userId = USER_SERVICE_INITIAL_USER_ID; + const char* products[11]{}; + for (std::uint32_t mode = 0; mode <= 6; ++mode) { + param.mode = mode; + for (std::uint32_t count = 0; count <= 11; ++count) { + param.productCount = count; + for (bool hasProducts : {false, true}) { + param.products = hasProducts ? products : nullptr; + bool valid = false; + if (mode == 0 || mode == 2) valid = count <= 1 && hasProducts == (count != 0); + if (mode == 1) valid = count == 1 && hasProducts; + if (mode == 3) valid = count >= 1 && count <= 10 && hasProducts; + if (mode == 4) valid = count <= 10 && hasProducts == (count != 0); + Require(sceNpCommerceDialogOpen(¶m) == (valid ? 0 : static_cast(0x80B8000Au))); + } + } + } + for (std::uint32_t mode : {1000u, 1001u}) { + param.mode = mode; + param.productCount = 0; + param.argument = 1; + Require(sceNpCommerceDialogOpen(¶m) == static_cast(0x80B8000Au)); + } + param.mode = 5; + param.productCount = 0; + param.products = nullptr; + for (std::uint64_t argument = 0; argument <= 4; ++argument) { + param.argument = argument; + Require(sceNpCommerceDialogOpen(¶m) == (argument == 1 ? 0 : static_cast(0x80B8000Au))); + } + sdkVersion = 0x01700000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + param.argument = 3; + Require(sceNpCommerceDialogOpen(¶m) == 0); + sdkVersion = 0x01600000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + param.size = 0; + param.option = 100; + param.userId = -1; + param.reserved[31] = 1; + param.argument = 7; Require(sceNpCommerceDialogOpen(¶m) == 0); Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); RequireCanceledResult(); Require(sceNpCommerceDialogTerminate() == 0); Require(sceNpCommerceDialogGetStatus() == statusNone); + Require(sceUserServiceTerminate() == 0); return 0; } diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index ca3ca354d6..9e423c84b4 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -8,3 +8,5 @@ - [sceNpCommerceDialogInitializeInternal](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpCommerce.c); the no-parameter prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_commerce/np_commerce.cpp#L61) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. - [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): the single pointer argument and 136-byte layout are recovered, including the product pointer array, count and result user-data value. User checks, mode validation, process-version-dependent restrictions and result-buffer semantics remain unimplemented; the host dialog still finishes immediately as cancelled. Mode-specific meanings of the option and 64-bit argument remain unresolved. + +- [sceNpCommerceDialogOpen](../../../core/libs/prx/libSceNpCommerce/Export.cpp): Product copying and service submission remain part of the cancelled UI stub. From 4e67d2380d41544587e3f0a3e95f8828daf983bc Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:47:44 +0300 Subject: [PATCH 458/523] fix(npcommerce): validate extended dialog modes --- core/libs/prx/libSceNpCommerce/Export.cpp | 34 +++++++++++++++++++++ core/libs/tests/GuestNpCommerceDialog.cpp | 37 ++++++++++++++++++++++- docs/dev/debt/libSceNpCommerce.md | 2 +- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index 213732b38a..b77de4d0b5 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "prx/libc/include/ApplicationHeap.hpp" #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" @@ -57,6 +58,38 @@ bool validateParam(const NpCommerceDialogParam& param) { } } +bool validateParam2(const NpCommerceDialogParam2& param) { + if (!isLoggedIn(param.userId) || param.size != sizeof(param)) return false; + if (param.option >= 8 && (param.contentMode == 0 || (param.contentMode == 1 && param.option >= 100))) return false; + if (!std::all_of(std::begin(param.reserved), std::end(param.reserved), [](std::uint8_t value) { return value == 0; })) return false; + bool valid = false; + switch (param.mode) { + case 0: + valid = param.contentMode < 2 && param.productCount <= 1 && (param.products != nullptr) == (param.productCount != 0); + break; + case 1: + valid = param.contentMode < 2 && param.productCount == 1 && param.products != nullptr; + break; + case 2: + valid = param.productCount <= 1 && (param.products != nullptr) == (param.productCount != 0); + break; + case 3: + valid = param.contentMode < 2 && param.productCount >= 1 && param.productCount <= 10 && param.products != nullptr; + break; + case 4: + valid = param.contentMode < 2 && param.productCount <= 10 && (param.products != nullptr) == (param.productCount != 0); + break; + case 5: + valid = param.productCount == 0 && param.argument == 1; + break; + default: + return false; + } + if (!valid) return false; + if (param.contentMode != 0 && (param.contentMode != 1 || param.mode != 1)) throw std::runtime_error("commerce dialog: extended content process version is not implemented"); + return true; +} + } extern "C" { @@ -128,6 +161,7 @@ int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param) { if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (!validateParam2(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index bc0b204659..1b2a6c9ed6 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -1,6 +1,7 @@ #include "ProcessParametersFixture.hpp" #include "prx/libSceUserService/UserService.hpp" #include +#include #include "SceTypes.hpp" #include "prx/libSceCommonDialog/CommonDialog.hpp" @@ -54,7 +55,7 @@ int main() { param2.base.size = sizeof(param2.base); param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; param2.size = sizeof(param2); - param2.userId = 1; + param2.userId = USER_SERVICE_INITIAL_USER_ID; Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceNpCommerceDialogOpen(nullptr) == static_cast(0x80B8000Du)); Require(sceNpCommerceDialogOpen2(nullptr) == static_cast(0x80B8000Du)); @@ -120,9 +121,43 @@ int main() { if (mode == 3) valid = count >= 1 && count <= 10 && hasProducts; if (mode == 4) valid = count <= 10 && hasProducts == (count != 0); Require(sceNpCommerceDialogOpen(¶m) == (valid ? 0 : static_cast(0x80B8000Au))); + param2.mode = mode; + param2.productCount = count; + param2.products = param.products; + Require(sceNpCommerceDialogOpen2(¶m2) == (valid ? 0 : static_cast(0x80B8000Au))); } } } + param2.mode = 1; + param2.contentMode = 1; + param2.productCount = 1; + param2.products = products; + param2.option = 99; + Require(sceNpCommerceDialogOpen2(¶m2) == 0); + param2.option = 100; + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); + param2.option = 0; + param2.mode = 2; + bool threw = false; + try { + sceNpCommerceDialogOpen2(¶m2); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw); + param2.contentMode = 0; + param2.mode = 0; + param2.productCount = 0; + param2.products = nullptr; + param2.reserved[31] = 1; + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); + param2.reserved[31] = 0; + param2.size = 0; + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); + param2.size = sizeof(param2); + param2.userId = -1; + Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); + param2.userId = USER_SERVICE_INITIAL_USER_ID; for (std::uint32_t mode : {1000u, 1001u}) { param.mode = mode; param.productCount = 0; diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index 9e423c84b4..33da178d0b 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -7,6 +7,6 @@ ### Unknown function info - [sceNpCommerceDialogInitializeInternal](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpCommerce.c); the no-parameter prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_commerce/np_commerce.cpp#L61) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. -- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): the single pointer argument and 136-byte layout are recovered, including the product pointer array, count and result user-data value. User checks, mode validation, process-version-dependent restrictions and result-buffer semantics remain unimplemented; the host dialog still finishes immediately as cancelled. Mode-specific meanings of the option and 64-bit argument remain unresolved. +- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): extended content combinations outside content mode 0 and content mode 1 with dialog mode 1 throw when otherwise valid. The native restriction reads process sysctl field 68; its relation to host process metadata is unresolved. Result-buffer semantics and mode-specific meanings of the option and 64-bit argument remain unresolved. - [sceNpCommerceDialogOpen](../../../core/libs/prx/libSceNpCommerce/Export.cpp): Product copying and service submission remain part of the cancelled UI stub. From cb8ad7c7776bcfcb16b8271e91f6baefb96616ee Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:48:29 +0300 Subject: [PATCH 459/523] fix(npcommerce): restore dialog result data and return value --- .../prx/libSceCommonDialog/CommonDialog.hpp | 11 ++++++ core/libs/prx/libSceNpCommerce/Export.cpp | 17 +++++---- core/libs/tests/GuestNpCommerceDialog.cpp | 35 +++++++++++-------- docs/dev/debt/libSceNpCommerce.md | 2 +- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index a9bea03571..b430b8ec87 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,17 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct NpCommerceDialogResult { + std::int32_t result; + std::uint8_t authorized; + std::uint8_t padding[3]; + std::uint64_t userData; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(NpCommerceDialogResult) == 48); +static_assert(offsetof(NpCommerceDialogResult, userData) == 8); + struct NpCommerceDialogParam { CommonDialogBaseParam base; std::uint32_t size; diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index b77de4d0b5..e224383c46 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -24,6 +24,7 @@ constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000A constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); int g_status = COMMON_DIALOG_STATUS_NONE; +std::uint64_t g_userData = 0; std::uint32_t compiledSdkVersion() { const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); @@ -106,6 +107,7 @@ int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param) { if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (!validateParam(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + g_userData = param->userData; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -118,16 +120,16 @@ int APS5_VABI sceNpCommerceDialogGetStatus(void) { return g_status; } -int APS5_VABI sceNpCommerceDialogGetResult(void* result) { +int APS5_VABI sceNpCommerceDialogGetResult(NpCommerceDialogResult* result) { if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (g_status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - // SceNpCommerceDialogResult: int32 result, bool authorized, then reserved bytes. - std::int32_t value = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &value, sizeof(value)); - std::uint8_t authorized = 0; - std::memcpy(static_cast(result) + sizeof(value), &authorized, sizeof(authorized)); - return 0; + const auto sdkVersion = compiledSdkVersion(); + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + result->authorized = 0; + result->userData = g_userData; + if (sdkVersion >= 0x01700000) std::fill(std::begin(result->reserved), std::end(result->reserved), 0); + return COMMON_DIALOG_RESULT_USER_CANCELED; } int APS5_VABI sceNpCommerceDialogClose(void) { @@ -162,6 +164,7 @@ int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param) { if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (!validateParam2(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + g_userData = param->userData; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 1b2a6c9ed6..9cb602b2b0 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -17,7 +17,7 @@ int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param); int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param); int APS5_VABI sceNpCommerceDialogUpdateStatus(void); int APS5_VABI sceNpCommerceDialogGetStatus(void); -int APS5_VABI sceNpCommerceDialogGetResult(void* result); +int APS5_VABI sceNpCommerceDialogGetResult(NpCommerceDialogResult* result); int APS5_VABI sceNpCommerceDialogClose(void); int APS5_VABI sceNpCommerceDialogTerminate(); } @@ -33,15 +33,15 @@ constexpr int statusInitialized = 1; constexpr int statusFinished = 3; constexpr std::int32_t resultUserCanceled = 1; -void RequireCanceledResult() { - std::uint8_t result[0x20]; - std::memset(result, 0x33, sizeof(result)); - Require(sceNpCommerceDialogGetResult(result) == 0); - std::int32_t value = 0; - std::memcpy(&value, result, sizeof(value)); - Require(value == resultUserCanceled); - Require(result[4] == 0); - Require(result[5] == 0x33); +void RequireCanceledResult(std::uint64_t userData, bool clearsReserved) { + NpCommerceDialogResult result; + std::memset(&result, 0x33, sizeof(result)); + Require(sceNpCommerceDialogGetResult(&result) == resultUserCanceled); + Require(result.result == resultUserCanceled); + Require(result.authorized == 0); + Require(result.userData == userData); + for (const auto value : result.padding) Require(value == 0x33); + for (const auto value : result.reserved) Require(value == (clearsReserved ? 0 : 0x33)); } } @@ -56,6 +56,7 @@ int main() { param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; param2.size = sizeof(param2); param2.userId = USER_SERVICE_INITIAL_USER_ID; + param2.userData = 0x123456789abcdef0; Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceNpCommerceDialogOpen(nullptr) == static_cast(0x80B8000Du)); Require(sceNpCommerceDialogOpen2(nullptr) == static_cast(0x80B8000Du)); @@ -69,18 +70,18 @@ int main() { Require(sceNpCommerceDialogTerminate() == static_cast(0x80B80003u)); Require(sceNpCommerceDialogClose() == static_cast(0x80B80003u)); Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); - std::uint8_t result[48]{}; - Require(sceNpCommerceDialogGetResult(result) == static_cast(0x80B80003u)); + NpCommerceDialogResult result{}; + Require(sceNpCommerceDialogGetResult(&result) == static_cast(0x80B80003u)); Require(sceNpCommerceDialogInitialize() == 0); Require(sceNpCommerceDialogClose() == static_cast(0x80B8000Bu)); - Require(sceNpCommerceDialogGetResult(result) == static_cast(0x80B80005u)); + Require(sceNpCommerceDialogGetResult(&result) == static_cast(0x80B80005u)); Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); Require(sceNpCommerceDialogGetStatus() == statusInitialized); Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); Require(sceNpCommerceDialogGetStatus() == statusFinished); - RequireCanceledResult(); + RequireCanceledResult(param2.userData, true); Require(sceNpCommerceDialogClose() == static_cast(0x80B8000Bu)); Require(sceNpCommerceDialogGetStatus() == statusFinished); @@ -158,6 +159,7 @@ int main() { param2.userId = -1; Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); param2.userId = USER_SERVICE_INITIAL_USER_ID; + param2.userData = 0x123456789abcdef0; for (std::uint32_t mode : {1000u, 1001u}) { param.mode = mode; param.productCount = 0; @@ -174,7 +176,9 @@ int main() { sdkVersion = 0x01700000; std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); param.argument = 3; + param.userData = 0xfedcba9876543210; Require(sceNpCommerceDialogOpen(¶m) == 0); + RequireCanceledResult(param.userData, true); sdkVersion = 0x01600000; std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); param.size = 0; @@ -183,9 +187,10 @@ int main() { param.reserved[31] = 1; param.argument = 7; Require(sceNpCommerceDialogOpen(¶m) == 0); + RequireCanceledResult(param.userData, false); Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); - RequireCanceledResult(); + RequireCanceledResult(param2.userData, false); Require(sceNpCommerceDialogTerminate() == 0); Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceUserServiceTerminate() == 0); diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index 33da178d0b..02559d2c86 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -7,6 +7,6 @@ ### Unknown function info - [sceNpCommerceDialogInitializeInternal](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpCommerce.c); the no-parameter prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_commerce/np_commerce.cpp#L61) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. -- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): extended content combinations outside content mode 0 and content mode 1 with dialog mode 1 throw when otherwise valid. The native restriction reads process sysctl field 68; its relation to host process metadata is unresolved. Result-buffer semantics and mode-specific meanings of the option and 64-bit argument remain unresolved. +- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): extended content combinations outside content mode 0 and content mode 1 with dialog mode 1 throw when otherwise valid. The native restriction reads process sysctl field 68; its relation to host process metadata is unresolved. Mode-specific meanings of the option and 64-bit argument remain unresolved. - [sceNpCommerceDialogOpen](../../../core/libs/prx/libSceNpCommerce/Export.cpp): Product copying and service submission remain part of the cancelled UI stub. From 51d788e87c506d4d82357fe49c962397912bb499 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:50:06 +0300 Subject: [PATCH 460/523] fix(agc): restore wait patch scalar argument widths --- core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp | 6 ++---- core/libs/prx/libSceAgc/tests/Commands.cpp | 9 ++------- docs/dev/debt/libSceAgc.md | 1 - 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp b/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp index 4da4af5783..2946f29377 100644 --- a/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp +++ b/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp @@ -55,16 +55,14 @@ int APS5_VABI sceAgcWaitRegMemPatchAddress(std::uint32_t* cmd, const volatile vo return 0; } -int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint64_t reference) { +int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint32_t reference) { auto* wait = Agc::Command::ValidateWait(cmd, __func__); - Agc::Command::CheckBits(reference, 0xffffffffu, __func__); wait[4] = static_cast(reference); return 0; } -int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint64_t mask) { +int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint32_t mask) { auto* wait = Agc::Command::ValidateWait(cmd, __func__); - Agc::Command::CheckBits(mask, 0xffffffffu, __func__); wait[((wait[0] >> 8u) & 0xffu) == 0x3cu ? 5 : 6] = static_cast(mask); return 0; } diff --git a/core/libs/prx/libSceAgc/tests/Commands.cpp b/core/libs/prx/libSceAgc/tests/Commands.cpp index c5ac16fa9c..c6fc01ae9c 100644 --- a/core/libs/prx/libSceAgc/tests/Commands.cpp +++ b/core/libs/prx/libSceAgc/tests/Commands.cpp @@ -28,8 +28,8 @@ extern "C" int APS5_VABI sceAgcInit_0090(std::uint32_t* state, std::uint32_t ver extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier); extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint64_t modifier); extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint32_t countIndirect, std::uint32_t maxCountOrCount, const volatile void* countAddress, std::uint32_t strideInBytes, std::uint64_t modifier); -extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint64_t reference); -extern "C" int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint64_t mask); +extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint32_t reference); +extern "C" int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint32_t mask); extern "C" int APS5_VABI sceAgcGetDataPacketPayloadAddress_0090(std::uint32_t** addr, std::uint32_t* cmd, int type); extern "C" std::uint32_t* APS5_VABI sceAgcCbSetShRegisterRangeDirect(CommandBuffer* buf, std::uint32_t offset, const std::uint32_t* values, std::uint32_t numValues); extern "C" std::uint32_t* APS5_VABI sceAgcDcbContextStateOp_0100(CommandBuffer* buf, std::uint32_t operation); @@ -531,9 +531,6 @@ void testMemory() { check(packet[8] == 0x55667788u && packet[9] == 0x11223344u && packet[12] == 2u, "wait reference or poll interval mismatch"); sceAgcWaitRegMemPatchReference(packet, 7); check(packet[8] == 7 && packet[9] == 0x11223344u, "reference patch changed the high word"); - const auto before = storage.words; - expectFailure([&] { sceAgcWaitRegMemPatchReference(packet, 0x100000000ull); }); - check(storage.words == before, "invalid memory operation modified packet memory"); auto* truncated = Agc::Command::WriteWait(&storage.buffer, 0, 3, 0, 0, &value, 0x100000000ull, 0xffffffff00000001ull, 32, __func__); check(truncated[8] == 0u && truncated[9] == 1u, "32-bit wait did not keep the low halves of the reference and mask"); sceAgcWaitRegMemPatchMask(packet, 0x0f0f0f0fu); @@ -541,8 +538,6 @@ void testMemory() { sceAgcWaitRegMemPatchMask(truncated, 0xff00u); check(truncated[9] == 0xff00u && truncated[8] == 0u && truncated[10] == 2u, "32-bit mask patch changed the wrong word"); const auto beforeMask = storage.words; - expectFailure([&] { sceAgcWaitRegMemPatchMask(packet, 0x100000000ull); }); - expectFailure([&] { sceAgcWaitRegMemPatchMask(truncated, 0x100000000ull); }); expectFailure([&] { sceAgcWaitRegMemPatchMask(packet + 4, 1); }); check(storage.words == beforeMask, "invalid mask patch modified packet memory"); } diff --git a/docs/dev/debt/libSceAgc.md b/docs/dev/debt/libSceAgc.md index eb2c126c2d..4cc94d9004 100644 --- a/docs/dev/debt/libSceAgc.md +++ b/docs/dev/debt/libSceAgc.md @@ -4,7 +4,6 @@ - [GS shader-half fusion](../../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - `SPI_SHADER_PGM_RSRC2_GS.OC_LDS_EN` is preserved if either half requests it, while user SGPR allocation comes from the front half and other state stays with the back half. Bit 18 is `OC_LDS_EN` in AMD's [GFX10 register definitions](https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_1_0_sh_mask.h). Mesa's radeonsi sets it on a merged ES+GS program exactly when the ES stage is a tessellation evaluation shader ([`si_shader_gs_legacy`](https://gitlab.freedesktop.org/mesa/mesa/-/blob/45ea3ed8ce649d6b72d1843307a29d9cc3b7704e/src/gallium/drivers/radeonsi/gfx/si_state_shaders.cpp#L631), [`gfx10_shader_ngg`](https://gitlab.freedesktop.org/mesa/mesa/-/blob/45ea3ed8ce649d6b72d1843307a29d9cc3b7704e/src/gallium/drivers/radeonsi/gfx/si_state_shaders.cpp#L1144)), so it belongs to the front half; console fusion output has not been measured. Cocoon (PPSA08766) requests front `0x70004` and back `0xab0020`. Other unmatched hardware state still throws. -- [sceAgcWaitRegMemPatchMask](../../../core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp) (libSceAgc) - follows `sceAgcWaitRegMemPatchReference`: the mask is taken as a 32-bit value and written to the low mask word of a 32- or 64-bit wait, as [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) does; [sharpemu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/Agc/AgcExports.PacketPatching.cs) writes both words of a 64-bit wait's mask. A mask above 32 bits throws - [sceAgcDcbSetCfRegisterDirect, sceAgcDcbSetCfRegisterRangeDirect](../../../core/libs/prx/libSceAgc/DcbState/src/Registers.cpp) (libSceAgc) - signatures follow [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Agc/SceAgc.cs), which agree; the SET_CONFIG_REG (0x68) packet is OpenAGC's. The driver does not execute SET_CONFIG_REG, so a submitted packet throws - [Command buffer cursors](../../../core/libs/prx/libSceAgc/Command/src/Packet.cpp) (libSceAgc) - a null `cursor_down` is taken as a buffer without downward allocations, whose limit is `top`. PPSA16064 builds one-packet buffers of 8 dwords that way; the console's handling is unconfirmed - [sceAgcGetRegisterDefaultsInternal](../../../core/libs/prx/libSceAgc/Misc/src/RegisterDefaults.cpp) (libSceAgc) - returns the version 0 internal table while `sceAgcGetRegisterDefaults` now returns the version 13 public one; which internal table a console returns is not known. [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) returns the table of the version passed to `sceAgcInit` instead, and null before it From d6966f9792ef300216c0f94d0ff6c44b54e31796 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:51:12 +0300 Subject: [PATCH 461/523] fix(agc): preserve wait packet control bits during patching --- .../libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp | 13 +++++-------- core/libs/prx/libSceAgc/tests/Commands.cpp | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp b/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp index 2946f29377..34ee4fd902 100644 --- a/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp +++ b/core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp @@ -45,13 +45,11 @@ extern "C" { int APS5_VABI sceAgcWaitRegMemPatchAddress(std::uint32_t* cmd, const volatile void* address) { auto* wait = Agc::Command::ValidateWait(cmd, __func__); const auto guestAddress = reinterpret_cast(address); - const auto alignment = ((wait[0] >> 8u) & 0xffu) == 0x3cu ? 4u : 8u; - Agc::Command::CheckAddress(guestAddress, alignment, __func__); - Agc::Command::CheckBits(guestAddress, 0xffffffffffffull, __func__); - cmd[2] = (cmd[2] & 0xffff0000u) | static_cast(guestAddress >> 32u); + const auto lowMask = ((wait[0] >> 8u) & 0xffu) == 0x3cu ? 3u : 7u; + cmd[2] = (cmd[2] & 0xffff0000u) | (static_cast(guestAddress >> 32u) & 0xffffu); cmd[3] = static_cast(guestAddress); - wait[2] = static_cast(guestAddress); - wait[3] = static_cast(guestAddress >> 32u); + wait[2] = (static_cast(guestAddress) & ~lowMask) | (wait[2] & lowMask); + wait[3] = (static_cast(guestAddress >> 32u) & 0x3ffffu) | (wait[3] & 0xfffc0000u); return 0; } @@ -69,8 +67,7 @@ int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint32_t mask) int APS5_VABI sceAgcWaitRegMemPatchCompareFunction(std::uint32_t* cmd, std::uint8_t compareFunction) { auto* wait = Agc::Command::ValidateWait(cmd, __func__); - Agc::Command::Require(compareFunction <= 6, __func__, "invalid wait comparison"); - wait[1] = (wait[1] & ~7u) | compareFunction; + wait[1] = (wait[1] & ~7u) | (compareFunction & 7u); return 0; } diff --git a/core/libs/prx/libSceAgc/tests/Commands.cpp b/core/libs/prx/libSceAgc/tests/Commands.cpp index c6fc01ae9c..88badb2189 100644 --- a/core/libs/prx/libSceAgc/tests/Commands.cpp +++ b/core/libs/prx/libSceAgc/tests/Commands.cpp @@ -28,6 +28,8 @@ extern "C" int APS5_VABI sceAgcInit_0090(std::uint32_t* state, std::uint32_t ver extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier); extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint64_t modifier); extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, std::uint32_t dataOffsetInBytes, std::uint32_t countIndirect, std::uint32_t maxCountOrCount, const volatile void* countAddress, std::uint32_t strideInBytes, std::uint64_t modifier); +extern "C" int APS5_VABI sceAgcWaitRegMemPatchAddress(std::uint32_t* cmd, const volatile void* address); +extern "C" int APS5_VABI sceAgcWaitRegMemPatchCompareFunction(std::uint32_t* cmd, std::uint8_t compareFunction); extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint32_t reference); extern "C" int APS5_VABI sceAgcWaitRegMemPatchMask(std::uint32_t* cmd, std::uint32_t mask); extern "C" int APS5_VABI sceAgcGetDataPacketPayloadAddress_0090(std::uint32_t** addr, std::uint32_t* cmd, int type); @@ -537,6 +539,21 @@ void testMemory() { check(packet[10] == 0x0f0f0f0fu && packet[11] == 0xffffffffu && packet[8] == 7, "64-bit mask patch changed the wrong word"); sceAgcWaitRegMemPatchMask(truncated, 0xff00u); check(truncated[9] == 0xff00u && truncated[8] == 0u && truncated[10] == 2u, "32-bit mask patch changed the wrong word"); + for (auto* waitPacket : {packet, truncated}) { + const auto lowMask = waitPacket == packet ? 7u : 3u; + waitPacket[6] |= lowMask; + waitPacket[7] = 0xa5fc0000u; + const auto tag = waitPacket[2] & 0xffff0000u; + const std::uintptr_t address = 0x1236abcd12345679ull; + check(sceAgcWaitRegMemPatchAddress(waitPacket, reinterpret_cast(address)) == 0, "wait address patch failed"); + check(waitPacket[2] == (tag | 0xabcdu) && waitPacket[3] == 0x12345679u, "wait wrapper address patch mismatch"); + check(waitPacket[6] == ((0x12345679u & ~lowMask) | lowMask) && waitPacket[7] == 0xa5feabcdu, "wait address patch did not preserve control bits"); + for (std::uint32_t comparison = 0; comparison <= 255; ++comparison) { + const auto control = waitPacket[5] & ~7u; + check(sceAgcWaitRegMemPatchCompareFunction(waitPacket, static_cast(comparison)) == 0, "wait comparison patch failed"); + check(waitPacket[5] == (control | (comparison & 7u)), "wait comparison patch changed control bits"); + } + } const auto beforeMask = storage.words; expectFailure([&] { sceAgcWaitRegMemPatchMask(packet + 4, 1); }); check(storage.words == beforeMask, "invalid mask patch modified packet memory"); From faebf639e907b6ccdec424cbca555e285bda309e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:52:58 +0300 Subject: [PATCH 462/523] fix(agc): restore semaphore memory clearing and errors --- .../libs/prx/libSceAgc/Misc/src/Semaphore.cpp | 25 ++++---------- core/libs/prx/libSceAgc/tests/Semaphore.cpp | 33 +++++++------------ docs/dev/debt/libSceAgc.md | 2 +- 3 files changed, 19 insertions(+), 41 deletions(-) diff --git a/core/libs/prx/libSceAgc/Misc/src/Semaphore.cpp b/core/libs/prx/libSceAgc/Misc/src/Semaphore.cpp index 7a6c802690..3ff95978d3 100644 --- a/core/libs/prx/libSceAgc/Misc/src/Semaphore.cpp +++ b/core/libs/prx/libSceAgc/Misc/src/Semaphore.cpp @@ -13,20 +13,12 @@ namespace { constexpr std::uint64_t semaphoreMemoryAlignment = 0x4000u; constexpr std::uint64_t semaphoreLabelBytes = 32u; -constexpr std::uint64_t semaphoreCounterBytes = 8u; std::mutex semaphoreMutex; std::uint8_t* semaphoreMemory = nullptr; std::uint64_t semaphoreBytes = 0; -bool hasOnlyZeroLabelTails(const std::uint8_t* memory, std::uint64_t sizeInBytes) { - for (std::uint64_t label = 0; label < sizeInBytes; label += semaphoreLabelBytes) { - for (std::uint64_t byte = semaphoreCounterBytes; byte < semaphoreLabelBytes; ++byte) { - if (memory[label + byte] != 0) return false; - } - } - return true; -} + } @@ -35,15 +27,12 @@ extern "C" { int APS5_VABI sceAgcSetAmmSemaphoreMemory(void* memory, std::uint64_t sizeInBytes) { const auto address = reinterpret_cast(memory); std::lock_guard lock(semaphoreMutex); - Agc::Command::Require(semaphoreMemory == nullptr, __func__, "the AMM semaphore memory is already registered; the console's error is unknown"); + if (semaphoreBytes != 0) return static_cast(0x8a6c0048u); + if (((address | sizeInBytes) & (semaphoreMemoryAlignment - 1u)) != 0) return static_cast(0x8a6c0002u); Agc::Command::Require(memory != nullptr && sizeInBytes != 0, __func__, "null or empty AMM semaphore memory; the console's error is unknown"); - Agc::Command::Require(((address | sizeInBytes) & (semaphoreMemoryAlignment - 1u)) == 0, __func__, "AMM semaphore memory or size is not 16 KiB aligned; the console's error is unknown"); Agc::Command::Require(sizeInBytes - 1u <= std::numeric_limits::max() - address, __func__, "AMM semaphore memory wraps the address space"); auto* labels = static_cast(memory); - Agc::Command::Require(hasOnlyZeroLabelTails(labels, sizeInBytes), __func__, "AMM semaphore labels hold data past their first 8 bytes; whether registration clears it is unknown"); - for (std::uint64_t label = 0; label < sizeInBytes; label += semaphoreLabelBytes) { - std::memset(labels + label, 0, semaphoreCounterBytes); - } + std::memset(labels, 0, sizeInBytes); semaphoreMemory = labels; semaphoreBytes = sizeInBytes; return 0; @@ -51,9 +40,9 @@ int APS5_VABI sceAgcSetAmmSemaphoreMemory(void* memory, std::uint64_t sizeInByte int APS5_VABI sceAgcGetSemaphoreLabel(std::uint32_t index, void** label) { std::lock_guard lock(semaphoreMutex); - Agc::Command::Require(semaphoreMemory != nullptr, __func__, "no AMM semaphore memory is registered; the console's error is unknown"); - Agc::Command::Require(index < semaphoreBytes / semaphoreLabelBytes, __func__, "the semaphore label lies outside the AMM semaphore memory; the console's error is unknown"); - Agc::Command::Require(label != nullptr, __func__, "null semaphore label output; the console's error is unknown"); + if (semaphoreBytes == 0) return static_cast(0x8a6c0049u); + if (index >= semaphoreBytes / semaphoreLabelBytes) return static_cast(0x8a6c000bu); + Agc::Command::Require(label != nullptr, __func__, "null semaphore label output"); *label = semaphoreMemory + static_cast(index) * semaphoreLabelBytes; return 0; } diff --git a/core/libs/prx/libSceAgc/tests/Semaphore.cpp b/core/libs/prx/libSceAgc/tests/Semaphore.cpp index 021eb3e7c2..789a303ad8 100644 --- a/core/libs/prx/libSceAgc/tests/Semaphore.cpp +++ b/core/libs/prx/libSceAgc/tests/Semaphore.cpp @@ -70,8 +70,8 @@ void fillStorage() { void testUnregistered() { std::array labels{storage.get(), storage.get(), storage.get()}; - expectFailure([&] { sceAgcGetSemaphoreLabel(0, &labels[1]); }); - expectFailure([] { sceAgcGetSemaphoreLabel(0, nullptr); }); + check(sceAgcGetSemaphoreLabel(0, &labels[1]) == static_cast(0x8a6c0049u), "unregistered label error mismatch"); + check(sceAgcGetSemaphoreLabel(0, nullptr) == static_cast(0x8a6c0049u), "unregistered null label error mismatch"); check(labels[0] == storage.get() && labels[1] == storage.get() && labels[2] == storage.get(), "a rejected semaphore label changed the output"); } @@ -80,10 +80,10 @@ void testRegistrationRejections() { expectFailure([] { sceAgcSetAmmSemaphoreMemory(nullptr, memoryBytes); }); expectFailure([] { sceAgcSetAmmSemaphoreMemory(nullptr, 0); }); expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), 0); }); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory() + 1, memoryBytes); }); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory() + labelBytes, blockBytes); }); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), blockBytes - 1u); }); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), blockBytes + labelBytes); }); + check(sceAgcSetAmmSemaphoreMemory(memory() + 1, memoryBytes) == static_cast(0x8a6c0002u), "unaligned semaphore memory error mismatch"); + check(sceAgcSetAmmSemaphoreMemory(memory() + labelBytes, blockBytes) == static_cast(0x8a6c0002u), "unaligned semaphore memory error mismatch"); + check(sceAgcSetAmmSemaphoreMemory(memory(), blockBytes - 1u) == static_cast(0x8a6c0002u), "unaligned semaphore memory error mismatch"); + check(sceAgcSetAmmSemaphoreMemory(memory(), blockBytes + labelBytes) == static_cast(0x8a6c0002u), "unaligned semaphore memory error mismatch"); expectFailure([] { const auto top = std::numeric_limits::max() & ~static_cast(blockBytes - 1u); sceAgcSetAmmSemaphoreMemory(reinterpret_cast(top), memoryBytes); @@ -92,18 +92,8 @@ void testRegistrationRejections() { testUnregistered(); } -void testUnsettledClearing() { - for (const std::uint64_t offset : {5u * labelBytes + counterBytes, memoryBytes - 1u}) { - memory()[offset] = 0x77u; - const auto before = snapshot(); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes); }); - checkUnchanged(before, "a registration with unsettled clearing changed memory"); - testUnregistered(); - memory()[offset] = 0; - } -} - void testRegistration() { + std::fill(memory(), memory() + memoryBytes, std::uint8_t{0x77}); const auto before = snapshot(); check(sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes) == 0, "registering the AMM semaphore memory failed"); check(std::all_of(memory(), memory() + memoryBytes, [](std::uint8_t byte) { return byte == 0; }), "registration did not clear the semaphore counters"); @@ -123,7 +113,7 @@ void testLabels() { void testLabelRejections() { for (const std::uint32_t index : {labelCount, labelCount + 1u, std::numeric_limits::max()}) { void* label = storage.get(); - expectFailure([&] { sceAgcGetSemaphoreLabel(index, &label); }); + check(sceAgcGetSemaphoreLabel(index, &label) == static_cast(0x8a6c000bu), "out-of-range label error mismatch"); check(label == storage.get(), "a rejected semaphore label changed the output"); } expectFailure([] { sceAgcGetSemaphoreLabel(0, nullptr); }); @@ -132,9 +122,9 @@ void testLabelRejections() { void testRepeatedRegistration() { std::fill(memory(), memory() + memoryBytes, std::uint8_t{0x3c}); const auto before = snapshot(); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes); }); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(storage.get(), blockBytes); }); - expectFailure([] { sceAgcSetAmmSemaphoreMemory(nullptr, 0); }); + check(sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes) == static_cast(0x8a6c0048u), "repeated registration error mismatch"); + check(sceAgcSetAmmSemaphoreMemory(storage.get(), blockBytes) == static_cast(0x8a6c0048u), "repeated registration error mismatch"); + check(sceAgcSetAmmSemaphoreMemory(nullptr, 0) == static_cast(0x8a6c0048u), "repeated registration error mismatch"); checkUnchanged(before, "a repeated registration changed memory"); void* label = nullptr; check(sceAgcGetSemaphoreLabel(labelCount - 1u, &label) == 0 && label == memory() + memoryBytes - labelBytes, "a repeated registration replaced the semaphore memory"); @@ -198,7 +188,6 @@ int main() { fillStorage(); testUnregistered(); testRegistrationRejections(); - testUnsettledClearing(); testRegistration(); testLabels(); testMemSemaphore(); diff --git a/docs/dev/debt/libSceAgc.md b/docs/dev/debt/libSceAgc.md index 4cc94d9004..196840a44a 100644 --- a/docs/dev/debt/libSceAgc.md +++ b/docs/dev/debt/libSceAgc.md @@ -42,7 +42,7 @@ ### Functional - [sceAgcDebugRaiseException](../../../core/libs/prx/libSceAgc/Misc/src/Debug.cpp) (libSceAgc) - logs the exception id and returns 0, as retail hardware does nothing per [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/3c7e8072f0d7684e930454794fd5c02056946d73/src/game_compat.c); the trap (`ud2`) OpenAGC describes for development hardware is not reproduced. Neither is verified on a console. The `uint32` exception id is [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/6f0cddd4404ec5203e71fea77e75fc373299ba3f/src/libs/agc.cpp)'s claim; OpenAGC declares no arguments, so the logged id may be leftover register contents -- [sceAgcSetAmmSemaphoreMemory, sceAgcGetSemaphoreLabel](../../../core/libs/prx/libSceAgc/Misc/src/Semaphore.cpp) (libSceAgc) - the `(memory, sizeInBytes)` and `(index, label)` signatures, the 16 KiB alignment of the memory and its size, the single registration, the 32-byte labels and the zeroed first 8 bytes of each are what [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/3c7e8072f0d7684e930454794fd5c02056946d73/src/game_compat.c#L508-L546) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/a2983692930564815a77f81e74c151268f5b935f/src/SharpEmu.Libs/Agc/AgcExports.AmmSemaphore.cs) agree on; KytyPS5 has neither function. OpenAGC clears the whole memory and sharpemu only those 8 bytes, so a registration whose labels hold data past them throws. The two return different errors for every rejected call (OpenAGC `0x8a6c0002`, `0x8a6c000b`, `0x8a6c0048` and `0x8a6c0049`, sharpemu generic invalid-argument and already-exists errors), so every rejected call throws, including a lookup before registration. Not measured on the console; PPSA02801 imports both (OpenAGC's import analysis), no title is shown calling them. +- [sceAgcSetAmmSemaphoreMemory](../../../core/libs/prx/libSceAgc/Misc/src/Semaphore.cpp) - native registration first invokes a kernel ioctl and driver registration; host registration does not reproduce their memory-access checks or error propagation. Null, empty and address-wrapping ranges throw. The full range is cleared after successful registration; duplicate registration, alignment and label-range errors match the native wrapper. - [sceAgcDcbSetWorkloadsActive and sceAgcDcbSetWorkloadComplete](../../../core/libs/prx/libSceAgc/DcbState/src/Workload.cpp) (libSceAgc) and [sceAgcDriverRegisterWorkloadStream](../../../core/libs/prx/libSceAgcDriver/Resource/src/Registration.cpp) (libSceAgcDriver) - the driver call records only the stream id (1 to 31; it returns 0 for any argument and keeps no stream record), and the DCB calls write the packets [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/agc.cpp) writes: an 18-dword (active) or 12-dword (complete) `IT_NOP` packet with the stream id, and for active the 64-bit mask of the workload ids, for complete the workload id and the 64-bit clear mask of that id. Both return null for a null command buffer, an unregistered or out-of-range stream, a workload id above 63, and for active a duplicate id or a count of 0 or above 63. The layouts and checks come from KytyPS5 alone; the console oracle in [prosper](https://github.com/mattias800/prosper/blob/main/prosper/tests/data/console_oracle/agcbuild.golden.tsv) measures the null return only for unregistered streams, not the packet contents of a registered one. The error codes the console driver returns for an invalid or repeated registration are unknown, so registration never fails here. PPSA29866 calls sceAgcDcbSetWorkloadsActive while building its command buffers (sceAgcDcbSetWorkloadComplete was not observed); nothing else was observed. - [sceAgcDcbSetWorkloadStreamInactive](../../../core/libs/prx/libSceAgc/DcbState/src/Workload.cpp) and [sceAgcAcbSetWorkloadsActive, sceAgcAcbSetWorkloadComplete, sceAgcAcbSetWorkloadStreamInactive](../../../core/libs/prx/libSceAgc/Acb/src/Control.cpp) (libSceAgc) - return null without writing to the buffer for a null command buffer or a stream that is not registered (out-of-range ids included), and throw for a registered stream. The console oracle in prosper measured only that null return, with no stream registered, three argument patterns each and nothing written ([ACB](https://github.com/mattias800/prosper/blob/df074498e56c0df22d74f9c6622e4709bc1c9a19/prosper/tests/data/console_oracle/agcbuild.golden.tsv#L48-L56), [DCB](https://github.com/mattias800/prosper/blob/df074498e56c0df22d74f9c6622e4709bc1c9a19/prosper/tests/data/console_oracle/agcbuild.golden.tsv#L196-L204)); the packet a registered stream gets is not measured. KytyPS5 has no body for these four, and its `IT_NOP` layout for the DCB active and complete calls differs from the SET_UCONFIG_REG, WRITE_DATA and SET_WORKLOAD sequence [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/3c7e8072f0d7684e930454794fd5c02056946d73/src/agc_workload_packet.c) derives from the driver, so neither is taken. The `(buf, streamId)`, `(buf, streamId, workloadId)` and `(buf, streamId, workloadIds, workloadCount)` signatures are those of the DCB calls in [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/3c7e8072f0d7684e930454794fd5c02056946d73/src/game_compat.c#L2317-L2384), which says the ACB calls use the same driver builders; no ACB declaration was found. `sceAgcDcbSetWorkloadsActive` checks the stream before it reads the workload ids, as OpenAGC does: the console returned null for stream 1 with the ids at address 2 ([measured](https://github.com/mattias800/prosper/blob/df074498e56c0df22d74f9c6622e4709bc1c9a19/prosper/tests/data/console_oracle/agcbuild.golden.tsv#L203)) without faulting - [sceAgcCreatePrimState](../../../core/libs/prx/libSceAgc/Shader/src/PrimState.cpp) (libSceAgc) - the `ge_user_vgpr_en` special is passed to the user configuration registers at whatever offset the shader header stores, as the SPRX does according to [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/3c7e8072f0d7684e930454794fd5c02056946d73/src/game_compat.c#L1181-L1252), which copies all four specials verbatim (KytyPS5 stops on any other offset). PPSA99039, PPSA99505, PPSA99515 and PPSA99997 build their headers by hand with it at 0x25c (`GE_USER_VGPR1`) instead of 0x262 (`GE_USER_VGPR_EN`), value 0, and run on the console; it is not moved to 0x262, and the driver reads neither register. `ge_cntl`, `vgt_shader_stages_en` and a used `vgt_gs_out_prim_type` must still be at their own offsets, since the driver reads those registers there, so a header that stores one elsewhere throws naming the offset, although by OpenAGC the SPRX would copy it as well From c311d6dc2a22597827238cb11ade2ffc8c7b591f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:54:17 +0300 Subject: [PATCH 463/523] fix(agc): take offchip lds enable from the front shader half --- core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp | 2 +- core/libs/prx/libSceAgc/tests/ShaderFusion.cpp | 2 +- docs/dev/debt/libSceAgc.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp b/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp index ad00d374ce..7c1bfcc5e5 100644 --- a/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp +++ b/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp @@ -129,7 +129,7 @@ std::uint32_t MergeRsrc1(std::uint32_t back, std::uint32_t front) { std::uint32_t MergeRsrc2(const char* function, std::uint32_t back, std::uint32_t front) { if ((front & ~(Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable) & ~back) != 0) Fail(function, "front half requires hardware state the back half does not enable"); - return (back & ~Rsrc2UserSgprMask) | (front & (Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)); + return (back & ~(Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)) | (front & (Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)); } const ShaderRegister* FindHalfRegister(const Shader* shader, std::uint32_t offset) { diff --git a/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp b/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp index 2f451acf50..6a37f831ff 100644 --- a/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp +++ b/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp @@ -208,7 +208,7 @@ void testOffchipLdsEnable() { Shader fused{}; check(sceAgcFuseShaderHalves_0200(&fused, &halves.front, &halves.back, aligned(storage, 256)) == 0, "offchip LDS enable rejected by fusion"); const auto* resource = find(fused, 0x08bu); - check(resource != nullptr && resource->value == ((6u << 1u) | (1u << 7u) | (1u << 15u) | frontEnable | backEnable), "fusion lost offchip LDS enable or changed the back-half state or front user SGPRs"); + check(resource != nullptr && resource->value == ((6u << 1u) | (1u << 7u) | (1u << 15u) | frontEnable), "fusion did not take offchip LDS enable and user SGPRs from the front half"); } } Halves reported; diff --git a/docs/dev/debt/libSceAgc.md b/docs/dev/debt/libSceAgc.md index 196840a44a..fa576d05c8 100644 --- a/docs/dev/debt/libSceAgc.md +++ b/docs/dev/debt/libSceAgc.md @@ -2,7 +2,7 @@ ### Unknown function info -- [GS shader-half fusion](../../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) (libSceAgc) - `SPI_SHADER_PGM_RSRC2_GS.OC_LDS_EN` is preserved if either half requests it, while user SGPR allocation comes from the front half and other state stays with the back half. Bit 18 is `OC_LDS_EN` in AMD's [GFX10 register definitions](https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/asic_reg/gc/gc_10_1_0_sh_mask.h). Mesa's radeonsi sets it on a merged ES+GS program exactly when the ES stage is a tessellation evaluation shader ([`si_shader_gs_legacy`](https://gitlab.freedesktop.org/mesa/mesa/-/blob/45ea3ed8ce649d6b72d1843307a29d9cc3b7704e/src/gallium/drivers/radeonsi/gfx/si_state_shaders.cpp#L631), [`gfx10_shader_ngg`](https://gitlab.freedesktop.org/mesa/mesa/-/blob/45ea3ed8ce649d6b72d1843307a29d9cc3b7704e/src/gallium/drivers/radeonsi/gfx/si_state_shaders.cpp#L1144)), so it belongs to the front half; console fusion output has not been measured. Cocoon (PPSA08766) requests front `0x70004` and back `0xab0020`. Other unmatched hardware state still throws. +- [GS shader-half fusion](../../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) - front-half hardware state outside the user SGPR and offchip LDS enable fields still throws when the back half does not enable it. Native shared VGPR allocation and resource-field maxima remain to be reconciled with the host fusion path. - [sceAgcDcbSetCfRegisterDirect, sceAgcDcbSetCfRegisterRangeDirect](../../../core/libs/prx/libSceAgc/DcbState/src/Registers.cpp) (libSceAgc) - signatures follow [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Agc/SceAgc.cs), which agree; the SET_CONFIG_REG (0x68) packet is OpenAGC's. The driver does not execute SET_CONFIG_REG, so a submitted packet throws - [Command buffer cursors](../../../core/libs/prx/libSceAgc/Command/src/Packet.cpp) (libSceAgc) - a null `cursor_down` is taken as a buffer without downward allocations, whose limit is `top`. PPSA16064 builds one-packet buffers of 8 dwords that way; the console's handling is unconfirmed From df4d0beae67a1fd86813a00858f342552b1f98cf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:56:44 +0300 Subject: [PATCH 464/523] fix(agc): restore geometry shader resource merging --- .../prx/libSceAgc/Misc/src/ShaderFusion.cpp | 31 ++++++++------ .../libs/prx/libSceAgc/tests/ShaderFusion.cpp | 40 ++++++++++++++++--- docs/dev/debt/libSceAgc.md | 1 - 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp b/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp index 7c1bfcc5e5..a8fdedf664 100644 --- a/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp +++ b/core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp @@ -35,7 +35,6 @@ constexpr std::size_t FusedCodeAlignment = 256; constexpr char TrailerMagic[8] = {'b', 'a', 'r', 'e', 'f', 'o', 'o', 't'}; constexpr std::size_t TrailerCodeBytesOffset = sizeof(TrailerMagic) + 3 * sizeof(std::uint32_t); constexpr std::uint32_t Rsrc1VgprMask = 0x3fu; -constexpr std::uint32_t Rsrc1SgprMask = 0xfu << 6u; constexpr std::uint32_t Rsrc2OffchipLdsEnable = 1u << 18u; constexpr std::uint32_t Rsrc2UserSgprMask = (0x1fu << 1u) | (1u << 27u); @@ -121,15 +120,19 @@ std::uint32_t FrontProgramBytes(const char* function, const Shader* front) { return static_cast((end - 1) * 4); } -std::uint32_t MergeRsrc1(std::uint32_t back, std::uint32_t front) { - const auto vgprs = std::max(back & Rsrc1VgprMask, front & Rsrc1VgprMask); - const auto sgprs = std::max(back & Rsrc1SgprMask, front & Rsrc1SgprMask); - return (back & ~(Rsrc1VgprMask | Rsrc1SgprMask)) | vgprs | sgprs; -} - -std::uint32_t MergeRsrc2(const char* function, std::uint32_t back, std::uint32_t front) { - if ((front & ~(Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable) & ~back) != 0) Fail(function, "front half requires hardware state the back half does not enable"); - return (back & ~(Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)) | (front & (Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)); +std::array mergeGeometryResources(std::uint32_t frontRsrc1, std::uint32_t frontRsrc2, std::uint32_t backRsrc1, std::uint32_t backRsrc2) { + const auto frontVgprs = ((frontRsrc1 & Rsrc1VgprMask) + 1u) * 4u; + const auto backVgprs = ((backRsrc1 & Rsrc1VgprMask) + 1u) * 4u; + const auto frontTotal = frontVgprs + (frontRsrc2 >> 28u) * 8u; + const auto backTotal = backVgprs + (backRsrc2 >> 28u) * 8u; + const auto maxTotal = std::max(frontTotal, backTotal); + const auto shared = std::max(frontVgprs, backVgprs) >= maxTotal ? 0u : (maxTotal - std::min(frontTotal, backTotal) + 7u) / 64u; + const auto vgprs = std::max(frontRsrc1 & Rsrc1VgprMask, backRsrc1 & Rsrc1VgprMask); + const auto rsrc1Field = std::max((frontRsrc1 >> 29u) & 3u, (backRsrc1 >> 29u) & 3u); + const auto rsrc2Field = std::max((frontRsrc2 >> 16u) & 3u, (backRsrc2 >> 16u) & 3u); + const auto rsrc1 = (backRsrc1 & ~(Rsrc1VgprMask | (3u << 29u))) | vgprs | (rsrc1Field << 29u); + const auto rsrc2 = (backRsrc2 & ~(0xf0000000u | (3u << 16u) | Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)) | ((shared & 15u) << 28u) | (rsrc2Field << 16u) | (frontRsrc2 & (Rsrc2UserSgprMask | Rsrc2OffchipLdsEnable)); + return {rsrc1, rsrc2}; } const ShaderRegister* FindHalfRegister(const Shader* shader, std::uint32_t offset) { @@ -149,10 +152,14 @@ GeometryLayout ComputeGeometryLayout(const char* function, const Shader* front, const auto* frontRsrc1 = FindHalfRegister(front, SPI_SHADER_PGM_RSRC1_GS); const auto* frontRsrc2 = FindHalfRegister(front, SPI_SHADER_PGM_RSRC2_GS); if (frontRsrc1 == nullptr || frontRsrc2 == nullptr) Fail(function, "front half has no GS resource registers"); + const auto* backRsrc1 = FindHalfRegister(back, SPI_SHADER_PGM_RSRC1_GS); + const auto* backRsrc2 = FindHalfRegister(back, SPI_SHADER_PGM_RSRC2_GS); + if (backRsrc1 == nullptr || backRsrc2 == nullptr) Fail(function, "back half has no GS resource registers"); + const auto resources = mergeGeometryResources(frontRsrc1->value, frontRsrc2->value, backRsrc1->value, backRsrc2->value); layout.sh.assign(back->sh_registers, back->sh_registers + back->num_sh_registers); for (auto& reg : layout.sh) { - if (reg.offset == SPI_SHADER_PGM_RSRC1_GS) reg.value = MergeRsrc1(reg.value, frontRsrc1->value); - else if (reg.offset == SPI_SHADER_PGM_RSRC2_GS) reg.value = MergeRsrc2(function, reg.value, frontRsrc2->value); + if (reg.offset == SPI_SHADER_PGM_RSRC1_GS) reg.value = resources[0]; + else if (reg.offset == SPI_SHADER_PGM_RSRC2_GS) reg.value = resources[1]; } for (std::uint32_t i = 0; i < front->num_sh_registers; ++i) { const auto& reg = front->sh_registers[i]; diff --git a/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp b/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp index 6a37f831ff..7b66b5e57c 100644 --- a/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp +++ b/core/libs/prx/libSceAgc/tests/ShaderFusion.cpp @@ -147,7 +147,7 @@ void testFusion(std::size_t misalignment, std::uint32_t terminator = SSetpcS6) { const auto* frontLo = find(fused, 0x0C8u); check(backLo != nullptr && backLo->value == static_cast((base + 256) >> 8u), "back program address was not patched"); check(frontLo != nullptr && frontLo->value == static_cast(base >> 8u), "fused program address was not patched"); - check(find(fused, 0x08Au)->value == ((1u << 24u) | (2u << 6u) | 5u), "RSRC1 register counts were not merged"); + check(find(fused, 0x08Au)->value == ((1u << 24u) | (1u << 6u) | 5u), "RSRC1 register counts were not merged"); check(find(fused, 0x08Bu)->value == ((6u << 1u) | (1u << 7u) | (1u << 15u)), "RSRC2 user SGPRs were not taken from the front half"); check(find(fused, 0x0B2u) != nullptr && find(fused, 0x0B2u)->value == 0x55u, "front-only SH register was lost"); check(find(fused, 0x080u) == nullptr, "front program checksum was kept"); @@ -222,6 +222,38 @@ void testOffchipLdsEnable() { check(find(fused, 0x08bu)->value == 0x00af0004u, "reported shader resources did not retain the front offchip LDS enable"); } +void testResourceMerging() { + struct Case { + std::uint32_t front1; + std::uint32_t front2; + std::uint32_t back1; + std::uint32_t back2; + std::uint32_t expected1; + std::uint32_t expected2; + }; + const Case cases[] = { + {0u, 0x80000000u, 0u, 0u, 0u, 0x10000000u}, + {1u, 0xf0000000u, 3u, 0u, 3u, 0x10000000u}, + {0u, 0xf0000000u, 0u, 0xf0000000u, 0u, 0u}, + {0x400003c1u, 0x0006000cu, 0x61000043u, 0x00018001u, 0x61000043u, 0x0006800du}, + {0x60000007u, 0x00010000u, 0x21000041u, 0x00020000u, 0x61000047u, 0x00020000u} + }; + for (const auto& entry : cases) { + Halves halves; + halves.frontSh[1].value = entry.front1; + halves.frontSh[2].value = entry.front2; + halves.backSh[2].value = entry.back1; + halves.backSh[3].value = entry.back2; + SizeAlign size{}; + check(sceAgcGetFusedShaderSize_0080(&size, &halves.front, &halves.back) == 0, "resource merge size query failed"); + std::vector scratch(size.m_size); + Shader fused{}; + check(sceAgcFuseShaderHalves_0200(&fused, &halves.front, &halves.back, scratch.data()) == 0, "resource merge failed"); + check(find(fused, 0x08au)->value == entry.expected1, "resource register 1 merge mismatch"); + check(find(fused, 0x08bu)->value == entry.expected2, "resource register 2 merge mismatch"); + } +} + void testRejections() { Halves halves; SizeAlign size{}; @@ -249,11 +281,6 @@ void testRejections() { noTrailer.frontCode[5] = 0; expectFailure([&] { sceAgcGetFusedShaderSize_0080(&size, &noTrailer.front, &noTrailer.back); }); } - { - Halves hardware; - hardware.frontSh[2].value |= 1u << 16u; - expectFailure([&] { sceAgcGetFusedShaderSize_0080(&size, &hardware.front, &hardware.back); }); - } { Halves constants; constants.back.embedded_constant_buffer_size_dqw = 1; @@ -305,6 +332,7 @@ int main() { testFusion(0, SSwappcNullS6); testScratchReuse(); testOffchipLdsEnable(); + testResourceMerging(); testRejections(); testHullHalves(); LibcRunShutdown_nid_postfix(); diff --git a/docs/dev/debt/libSceAgc.md b/docs/dev/debt/libSceAgc.md index fa576d05c8..ab5eaacf4e 100644 --- a/docs/dev/debt/libSceAgc.md +++ b/docs/dev/debt/libSceAgc.md @@ -2,7 +2,6 @@ ### Unknown function info -- [GS shader-half fusion](../../../core/libs/prx/libSceAgc/Misc/src/ShaderFusion.cpp) - front-half hardware state outside the user SGPR and offchip LDS enable fields still throws when the back half does not enable it. Native shared VGPR allocation and resource-field maxima remain to be reconciled with the host fusion path. - [sceAgcDcbSetCfRegisterDirect, sceAgcDcbSetCfRegisterRangeDirect](../../../core/libs/prx/libSceAgc/DcbState/src/Registers.cpp) (libSceAgc) - signatures follow [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Agc/SceAgc.cs), which agree; the SET_CONFIG_REG (0x68) packet is OpenAGC's. The driver does not execute SET_CONFIG_REG, so a submitted packet throws - [Command buffer cursors](../../../core/libs/prx/libSceAgc/Command/src/Packet.cpp) (libSceAgc) - a null `cursor_down` is taken as a buffer without downward allocations, whose limit is `top`. PPSA16064 builds one-packet buffers of 8 dwords that way; the console's handling is unconfirmed From 2ad07c61ee74fb4f4b6794c68c1f04b24dbf04a4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:03:00 +0300 Subject: [PATCH 465/523] fix(automounter): define usb device output layout --- .../prx/libSceAutoMounterClient/Export.cpp | 20 ++++++++++++++++++- docs/dev/debt/libSceAutoMounterClient.md | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAutoMounterClient/Export.cpp b/core/libs/prx/libSceAutoMounterClient/Export.cpp index e4845005e9..77af95c0ff 100644 --- a/core/libs/prx/libSceAutoMounterClient/Export.cpp +++ b/core/libs/prx/libSceAutoMounterClient/Export.cpp @@ -3,7 +3,25 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" -struct AutoMounterUsbDeviceInfo; +struct AutoMounterUsbDeviceInfo { + char deviceName[128]; + std::byte metadata[22]; + std::byte data0[256]; + std::byte padding[2]; + std::uint64_t data0Size; + std::byte data1[256]; + std::uint64_t data1Size; + std::byte data2[256]; + std::uint64_t data2Size; +}; + +static_assert(sizeof(AutoMounterUsbDeviceInfo) == 944); +static_assert(offsetof(AutoMounterUsbDeviceInfo, data0) == 150); +static_assert(offsetof(AutoMounterUsbDeviceInfo, data0Size) == 408); +static_assert(offsetof(AutoMounterUsbDeviceInfo, data1) == 416); +static_assert(offsetof(AutoMounterUsbDeviceInfo, data1Size) == 672); +static_assert(offsetof(AutoMounterUsbDeviceInfo, data2) == 680); +static_assert(offsetof(AutoMounterUsbDeviceInfo, data2Size) == 936); extern "C" { diff --git a/docs/dev/debt/libSceAutoMounterClient.md b/docs/dev/debt/libSceAutoMounterClient.md index 1517732495..2996a7b44b 100644 --- a/docs/dev/debt/libSceAutoMounterClient.md +++ b/docs/dev/debt/libSceAutoMounterClient.md @@ -3,4 +3,4 @@ ### Unknown function info - [sceAutoMounterClientRegisterCallback, sceAutoMounterClientTerm and sceAutoMounterClientUnregisterCallback](../../../core/libs/prx/libSceAutoMounterClient/Export.cpp) (libSceAutoMounterClient) - their names and NIDs are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAutoMounterClient.c), but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified. -- [sceAutoMounterClientGetUsbDeviceInfo](../../../core/libs/prx/libSceAutoMounterClient/Export.cpp): the name string and output pointer are confirmed. The output occupies 944 bytes, including a 128-byte device name and three variable-length byte arrays with 64-bit lengths, but the metadata field meanings remain unresolved. +- [sceAutoMounterClientGetUsbDeviceInfo](../../../core/libs/prx/libSceAutoMounterClient/Export.cpp): the 22 metadata bytes at offset 128 remain opaque. The 944-byte output layout, 128-byte device name and three 256-byte arrays with 64-bit lengths are recovered. From 886974fd6d15833227fb6a2cee33131c01ffd9f6 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:03:01 +0300 Subject: [PATCH 466/523] fix(bgft): define storage task parameter layout --- core/libs/prx/libSceBgft/Export.cpp | 27 ++++++++++++++++++++++++++- docs/dev/debt/libSceBgft.md | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceBgft/Export.cpp b/core/libs/prx/libSceBgft/Export.cpp index 8480641566..483969400b 100644 --- a/core/libs/prx/libSceBgft/Export.cpp +++ b/core/libs/prx/libSceBgft/Export.cpp @@ -9,7 +9,32 @@ struct BgftInitParam { }; struct BgftProgress; -struct BgftStorageTaskParam; +struct BgftStorageTaskParam { + std::uint32_t value0; + std::uint32_t value4; + const char* contentId; + const char* text16; + std::byte unused24[8]; + const char* text32; + const char* text40; + const char* text48; + std::uint32_t value56; + std::byte padding60[4]; + const char* text64; + const char* text72; + const char* text80; + const char* text88; + std::uint64_t value96; + std::uint32_t value104; +}; + +static_assert(sizeof(BgftStorageTaskParam) == 112); +static_assert(offsetof(BgftStorageTaskParam, contentId) == 8); +static_assert(offsetof(BgftStorageTaskParam, text32) == 32); +static_assert(offsetof(BgftStorageTaskParam, value56) == 56); +static_assert(offsetof(BgftStorageTaskParam, text64) == 64); +static_assert(offsetof(BgftStorageTaskParam, value96) == 96); +static_assert(offsetof(BgftStorageTaskParam, value104) == 104); static_assert(sizeof(BgftInitParam) == 16); static_assert(offsetof(BgftInitParam, heapSize) == 8); diff --git a/docs/dev/debt/libSceBgft.md b/docs/dev/debt/libSceBgft.md index 839539717c..385f701d0f 100644 --- a/docs/dev/debt/libSceBgft.md +++ b/docs/dev/debt/libSceBgft.md @@ -3,5 +3,5 @@ ### Unknown function info - [sceBgftServiceDownloadGetProgress](../../../core/libs/prx/libSceBgft/Export.cpp), `sceBgftServiceIntDownloadGetProgress`, `sceBgftServiceIntDownloadGetGameAndGameAcProgress` and `sceBgftServiceIntDownloadGetPlayGoProgress`: signatures and the shared 88-byte output are confirmed. The library forwards the output through IPC without accessing individual fields; their types remain unresolved. -- [sceBgftServiceIntDownloadRegisterTaskByStorageEx](../../../core/libs/prx/libSceBgft/Export.cpp): confirmed parameter pointer and 32-bit task ID output. The input contains 32-bit scalars at offsets 0, 4, 56 and 104, strings at offsets 8, 16, 32, 40, 48, 64, 72, 80 and 88, and a 64-bit scalar at offset 96. Scalar meanings and the field at offset 24 remain unresolved; the parameter type is forward-declared. +- [sceBgftServiceIntDownloadRegisterTaskByStorageEx](../../../core/libs/prx/libSceBgft/Export.cpp): confirmed parameter pointer and 32-bit task ID output. The input contains 32-bit scalars at offsets 0, 4, 56 and 104, strings at offsets 8, 16, 32, 40, 48, 64, 72, 80 and 88, and a 64-bit scalar at offset 96. The typed layout preserves the unused eight bytes at offset 24; scalar meanings and several string roles remain unresolved. - [sceBgftServiceIntDownloadGetPatchProgress](../../../core/libs/prx/libSceBgft/Export.cpp) and `sceBgftServiceIntDownloadGetTaskInfo`: neither export nor a caller was found in the supplied decompiled library corpus. Their signatures remain unresolved. From 6aa2dd97b71c382c254e907dd96bcc7606da8d1f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:03:02 +0300 Subject: [PATCH 467/523] fix(npmanager): restore availability output and online id input --- core/libs/prx/libSceNpManager/Export.cpp | 8 ++++---- core/libs/tests/GuestNpManager.cpp | 6 +++--- core/libs/tests/GuestNpReachability.cpp | 14 +++++++++++--- docs/dev/debt/libSceNpManager.md | 6 +++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceNpManager/Export.cpp b/core/libs/prx/libSceNpManager/Export.cpp index 1b45d38294..0035e68915 100644 --- a/core/libs/prx/libSceNpManager/Export.cpp +++ b/core/libs/prx/libSceNpManager/Export.cpp @@ -683,9 +683,9 @@ int APS5_VABI sceNpCheckCallback(void) { return 0; } -int APS5_VABI sceNpCheckNpAvailability(int req_id, const NpOnlineId* online_id) { - (void)req_id; - if (!online_id) return SCE_NP_ERROR_INVALID_ARGUMENT; +int APS5_VABI sceNpCheckNpAvailability(std::int32_t requestId, const NpOnlineId* onlineId, std::uint8_t* available) { + if (requestId == 0 || onlineId == nullptr) return SCE_NP_ERROR_INVALID_ARGUMENT; + if (available != nullptr) *available = 0; return SCE_NP_ERROR_USER_NOT_FOUND; } @@ -961,7 +961,7 @@ int APS5_VABI sceNpGetSandboxInfo(void* sandbox_info) { } APS5_EXPORT("8weiQyIdI3A", sceNpUnknown_8weiQyIdI3A); -int APS5_VABI sceNpUnknown_8weiQyIdI3A(int userId, void* buffer) { +int APS5_VABI sceNpUnknown_8weiQyIdI3A(int userId, const NpOnlineId* buffer) { (void)userId; (void)buffer; NotImplemented_nid_no_patch(__func__); diff --git a/core/libs/tests/GuestNpManager.cpp b/core/libs/tests/GuestNpManager.cpp index 83a5566664..75393b1321 100644 --- a/core/libs/tests/GuestNpManager.cpp +++ b/core/libs/tests/GuestNpManager.cpp @@ -12,7 +12,7 @@ int APS5_VABI sceNpGetAccountCountryA(int user_id, void* country_code); int APS5_VABI sceNpGetNpId(int user_id, NpId* np_id); int APS5_VABI sceNpGetOnlineId(int user_id, NpOnlineId* online_id); int APS5_VABI sceNpGetUserIdByAccountId(std::uint64_t account_id, int* user_id); -int APS5_VABI sceNpUnknown_8weiQyIdI3A(int userId, void* buffer); +int APS5_VABI sceNpUnknown_8weiQyIdI3A(int userId, const NpOnlineId* buffer); int APS5_VABI sceNpUnknown_WxLcN76q19o(void); int APS5_VABI sceNpGetAccountCountry(const NpOnlineId* online_id, void* country_code); int APS5_VABI sceNpGetAccountDateOfBirth(const NpOnlineId* online_id, void* date_of_birth); @@ -306,9 +306,9 @@ int main() { Require(sceNpGetUserIdByAccountId(0x1234, nullptr) == InvalidArgument, "sceNpGetUserIdByAccountId must reject a null output"); Require(sceNpGetUserIdByAccountId(0x1234, &userId) == SignedOut, "sceNpGetUserIdByAccountId must report NP as signed out"); Require(userId == 0x5a5a, "sceNpGetUserIdByAccountId must leave the user id untouched"); - unsigned char buffer[0x11]{}; + NpOnlineId buffer{}; try { - sceNpUnknown_8weiQyIdI3A(0x10000, buffer); + sceNpUnknown_8weiQyIdI3A(0x10000, &buffer); Require(false, "sceNpUnknown_8weiQyIdI3A must report that it is not implemented"); } catch (const std::runtime_error& error) { Require(std::string(error.what()) == "sceNpUnknown_8weiQyIdI3A not implemented", diff --git a/core/libs/tests/GuestNpReachability.cpp b/core/libs/tests/GuestNpReachability.cpp index 4e0c1f2262..e6995ac80d 100644 --- a/core/libs/tests/GuestNpReachability.cpp +++ b/core/libs/tests/GuestNpReachability.cpp @@ -6,7 +6,7 @@ int APS5_VABI sceNpRegisterNpReachabilityStateCallback(void*, void*); int APS5_VABI sceNpUnregisterNpReachabilityStateCallback(); int APS5_VABI sceNpGetNpReachabilityState(int, uint32_t*); int APS5_VABI sceNpCheckCallback(); -int APS5_VABI sceNpCheckNpAvailability(int, const NpOnlineId*); +int APS5_VABI sceNpCheckNpAvailability(int, const NpOnlineId*, std::uint8_t*); } static constexpr int ErrorArgument = static_cast(0x80550003); @@ -33,6 +33,14 @@ int main() { Require(sceNpUnregisterNpReachabilityStateCallback() == 0); NpOnlineId onlineId{}; onlineId.data[0] = 'p'; - Require(sceNpCheckNpAvailability(1, &onlineId) == ErrorUserNotFound); - Require(sceNpCheckNpAvailability(1, nullptr) == ErrorArgument); + Require(sceNpCheckNpAvailability(1, &onlineId, nullptr) == ErrorUserNotFound); + Require(sceNpCheckNpAvailability(1, nullptr, nullptr) == ErrorArgument); + std::uint8_t available[3]{0x55, 0x55, 0x55}; + Require(sceNpCheckNpAvailability(0, &onlineId, &available[1]) == ErrorArgument); + Require(available[1] == 0x55); + Require(sceNpCheckNpAvailability(1, nullptr, &available[1]) == ErrorArgument); + Require(available[1] == 0x55); + Require(sceNpCheckNpAvailability(1, &onlineId, &available[1]) == ErrorUserNotFound); + Require(available[0] == 0x55 && available[1] == 0 && available[2] == 0x55); + } diff --git a/docs/dev/debt/libSceNpManager.md b/docs/dev/debt/libSceNpManager.md index 9cf1c6accd..b7d03f1c33 100644 --- a/docs/dev/debt/libSceNpManager.md +++ b/docs/dev/debt/libSceNpManager.md @@ -9,8 +9,8 @@ - [libSceNpManager pending exports](../../../core/libs/prx/libSceNpManager/Export.cpp) - 150 named callable exports are present in the local `libSceNpManager.sprx` export table and the public PS5 3.20 symbol list. Their signatures and behavior are unverified; each declaration uses the no-parameter unknown-signature convention and throws through `NotImplemented_nid_no_patch`. - [sceNpGetSandboxInfo](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager, NID `ZVZ++68nNI8`) - the name hashes to the imported NID (found with nidhunt, confirmed with `tools/nid_names.py`). STAR WARS: Galactic Racer (PPSA28416) imports it; its OnlineSubsystemPS5 calls it with one pointer to a 0x50-byte stack buffer and reads the buffer as a string (discussions/3086). The returned string, the exact parameter struct and console errors are unknown, so every call throws without reading or writing caller memory. A working implementation belongs with that title's bring-up. -- [sceNpUnknown_8weiQyIdI3A](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager, NID `8weiQyIdI3A`) - the name is not in aerolib. EA SPORTS FC 27 (PPSA34015) fails to load `libSceNpManager.prx` without it (`unresolved ELF import` / `GetLastError: 127`). Its only call is `mov edi, r14d` (a user id) and `lea rsi, [rbp-0x40]` after a 17-byte copy into that buffer (`mov edx, 0x11`); `test eax, eax` follows. The C name, the type of the 17-byte object and the console return are unknown, so every call throws without reading or writing caller memory. -- [sceNpUnknown_WxLcN76q19o](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager, NID `WxLcN76q19o`) - the name is not in aerolib. PPSA34015 imports it from `libSceNpManager.prx` as a strong `JUMP_SLOT` with no call found in the eboot; the loader still requires the export. Arguments, result and console errors are unknown, so every call throws. +- [sceNpUnknown_8weiQyIdI3A](../../../core/libs/prx/libSceNpManager/Export.cpp): the original name remains unknown. The native wrapper takes a user ID and a read-only 20-byte online-ID record, validates its first byte and sends the record through IPC. The implementation remains a throwing stub. +- [sceNpUnknown_WxLcN76q19o](../../../core/libs/prx/libSceNpManager/Export.cpp): the original name remains unknown. The native wrapper has no input arguments and returns a 32-bit status from an IPC request after initialization checks. The implementation remains a throwing stub. - [sceNpSetTimeout](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the signature follows [orbital](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json), [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Np/NpAccount.cs); the rejected values (all five 0, negative retries, a resolve timeout under 1 s or another timeout under 10 s, 0 meaning the default) are shadPS4's, not checked on a console. An id that was never created or was deleted returns `REQUEST_NOT_FOUND`, as in shadPS4; there is no network, so the timeouts are not stored - [sceNpGetUserIdByAccountId](../../../core/libs/prx/libSceNpManager/Export.cpp) - the `(account id, user id output)` signature and its errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_manager.cpp): account id 0 or a null output returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and a signed-out NP returns `SCE_NP_ERROR_SIGNED_OUT` without writing the output. NP is always signed out here, so no account id resolves to a user - [sceNpNotifyPlusFeature](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 titles import it; its parameter block and what the console does with it for a signed-out user are unknown ([shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L1170) returns 0 without reading it), so every call throws. @@ -22,7 +22,7 @@ - [sceNpCheckPlus](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(request id, parameter, result)` signature follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L246): request id 0 or a null parameter or result returns `SCE_NP_ERROR_INVALID_ARGUMENT`, and every other call returns `SCE_NP_ERROR_SIGNED_OUT` without writing the result, as `sceNpCheckPremium` does. The parameter's size and feature fields, which shadPS4 checks, are not read: the errors the console returns for them are not known - [sceNpGetAccountId, sceNpSetGamePresenceOnline](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - PS4 forms; the `(online id, account id output)` and `(request id, online id, presence)` signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/81be949167d0060b08f26dc3e055a0b640d129c8/src/core/libraries/np/np_manager.cpp#L1092-L1096). A null online id or output, or request id 0 for `sceNpSetGamePresenceOnline`, returns `SCE_NP_ERROR_INVALID_ARGUMENT`. `sceNpSetGamePresenceOnline` then returns `SCE_NP_ERROR_REQUEST_NOT_FOUND` for a request that was never created or was deleted, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/c1b26558352b863f80142256e4df38585aa7d727/src/core/libraries/np/np_manager.cpp#L1098-L1104) does. Otherwise both return `SCE_NP_ERROR_SIGNED_OUT` without writing, as `sceNpGetAccountIdA` does. The console's result for a signed-out user is not measured - [NP reachability callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) retain one callback and its user data; duplicate registration and unregistering an absent callback return the errors used by [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/libraries/np/np_manager.cpp). No callback is delivered because the existing NP model remains signed out with reachability unavailable. -- [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests +- [sceNpCheckNpAvailability](../../../core/libs/prx/libSceNpManager/Export.cpp): native NP initialization and online-ID lookup are not modeled. The offline host returns user-not-found for valid arguments after clearing the optional one-byte availability output; request ID 0 and a null online-ID pointer return invalid-argument before writing the output. - [NP state callbacks](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the registration rules are [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L857-L901)'s: `sceNpRegisterStateCallbackA` keeps up to 8 callbacks and returns the slot plus 1, a null callback returns `INVALID_ARGUMENT`, a callback already registered `CALLBACK_ALREADY_REGISTERED` and a ninth one `CALLBACK_MAX` (0x8055001D, [np_error.h](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_error.h#L38)); `sceNpUnregisterStateCallbackA` returns `INVALID_ARGUMENT` for an id outside 1..8 and `CALLBACK_NOT_REGISTERED` for an empty slot; the legacy [sceNpRegisterStateCallback](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L981-L1005) keeps one callback with the same errors. Each registration gets `SIGNED_OUT` for the initial user (0x10000000) once, from the next `sceNpCheckCallback` on the calling thread. For the A form this follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L3959-L3974), whose [sceNpRegisterStateCallbackA](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4021-L4034) marks the registration pending. KytyPS5 never calls the legacy callback ([sceNpRegisterStateCallback](https://github.com/KytyPS5/KytyPS5/blob/9e41d8320a2774b303fbad344c2347e8d0292e83/src/libs/network.cpp#L4009-L4019) stores nothing); it is called as `(user, SIGNED_OUT, nullptr, userdata)`, as in shadPS4, which passes an NpId only for `SIGNED_IN` ([L823](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L823), [L938-L940](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L938-L940)). shadPS4 instead queues `SIGNED_OUT` from the user service login event and delivers it from `sceNpCheckCallback` ([DispatchPendingNpStateCallbacks](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L903-L962)); the reachability callback is not given an initial `UNAVAILABLE`, unlike that function ([L922-L961](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/np/np_manager.cpp#L922-L961)). A callback being delivered when another thread unregisters it is still called once. When and how often the console calls the state callbacks, and the errors, are not measured - [sceNpRegisterGamePresenceCallback](../../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - accepts any callback, null included, and returns nothing; no game presence event is delivered because the NP model remains signed out. [Kyty](https://github.com/InoriRus/Kyty/blob/4733b7e1c91b10554a52007903d74dc76c39a230/source/emulator/src/Network.cpp#L1549-L1552) ignores the callback the same way, and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/8d50471b7dc141dd700b51276b8edd1f293bce26/src/core/libraries/np/np_manager.cpp#L1121-L1126) keeps the last one without calling it. Not measured on a PS5 From 73120899a97ac080bdcb9b9987fbff595f2427d0 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:05:14 +0300 Subject: [PATCH 468/523] fix(playerselection): restore dialog validation and result contract --- .../prx/libSceCommonDialog/CommonDialog.hpp | 40 ++++++++++++ .../CMakeLists.txt | 3 +- .../libScePlayerSelectionDialog/Export.cpp | 32 +++++++--- .../libs/tests/GuestPlayerSelectionDialog.cpp | 62 +++++++++++++++---- docs/dev/debt/libScePlayerSelectionDialog.md | 2 +- 5 files changed, 118 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index b430b8ec87..bb2d229e7c 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,46 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct PlayerSelectionDialogPlayer { + std::uint64_t accountId; + std::uint32_t option0; + std::uint32_t option1; + std::uint32_t mode; + std::uint8_t reserved[64]; + std::uint32_t padding; +}; + +struct PlayerSelectionDialogParam { + CommonDialogBaseParam base; + std::uint64_t size; + std::int32_t userId; + char title[64]; + std::uint32_t maxPlayers; + std::uint32_t mode; + std::uint32_t padding0; + const PlayerSelectionDialogPlayer* players; + std::uint32_t playerCount; + std::uint8_t reserved[64]; + std::uint32_t padding1; +}; + +struct PlayerSelectionDialogResult { + std::int32_t result; + std::uint32_t option; + std::uint64_t* accountIds; + std::uint32_t count; + std::uint32_t padding; +}; + +static_assert(sizeof(PlayerSelectionDialogPlayer) == 88); +static_assert(offsetof(PlayerSelectionDialogPlayer, reserved) == 20); +static_assert(sizeof(PlayerSelectionDialogParam) == 216); +static_assert(offsetof(PlayerSelectionDialogParam, maxPlayers) == 124); +static_assert(offsetof(PlayerSelectionDialogParam, players) == 136); +static_assert(offsetof(PlayerSelectionDialogParam, reserved) == 148); +static_assert(sizeof(PlayerSelectionDialogResult) == 24); +static_assert(offsetof(PlayerSelectionDialogResult, accountIds) == 8); + struct NpCommerceDialogResult { std::int32_t result; std::uint8_t authorized; diff --git a/core/libs/prx/libScePlayerSelectionDialog/CMakeLists.txt b/core/libs/prx/libScePlayerSelectionDialog/CMakeLists.txt index 670b5b3b0a..0d85abead0 100644 --- a/core/libs/prx/libScePlayerSelectionDialog/CMakeLists.txt +++ b/core/libs/prx/libScePlayerSelectionDialog/CMakeLists.txt @@ -1,11 +1,12 @@ add_library(libScePlayerSelectionDialog SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libScePlayerSelectionDialog PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libScePlayerSelectionDialog) +target_link_libraries(libScePlayerSelectionDialog PRIVATE libSceUserService) if(BUILD_TESTING) add_test_executable(guest_player_selection_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestPlayerSelectionDialog.cpp) target_include_directories(guest_player_selection_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_player_selection_dialog_tests PRIVATE libScePlayerSelectionDialog libc) + target_link_libraries(guest_player_selection_dialog_tests PRIVATE libScePlayerSelectionDialog libSceUserService libc) configure_windows_unwind(guest_player_selection_dialog_tests) add_test(NAME guest_player_selection_dialog COMMAND guest_player_selection_dialog_tests) if(WIN32) diff --git a/core/libs/prx/libScePlayerSelectionDialog/Export.cpp b/core/libs/prx/libScePlayerSelectionDialog/Export.cpp index 4f9c50b9a7..06df01e1c8 100644 --- a/core/libs/prx/libScePlayerSelectionDialog/Export.cpp +++ b/core/libs/prx/libScePlayerSelectionDialog/Export.cpp @@ -3,8 +3,11 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +extern "C" int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* users); + namespace { constexpr int COMMON_DIALOG_STATUS_NONE = 0; @@ -12,9 +15,10 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); +constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; @@ -30,17 +34,30 @@ int APS5_VABI scePlayerSelectionDialogInitialize(void) { return 0; } -int APS5_VABI scePlayerSelectionDialogOpen(const void* param) { +int APS5_VABI scePlayerSelectionDialogOpen(const PlayerSelectionDialogParam* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (param->size != sizeof(*param) || param->userId == -1) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + UserServiceLoginUserIdList users{}; + if (sceUserServiceGetLoginUserIdList(&users) != 0 || std::find(std::begin(users.user_id), std::end(users.user_id), param->userId) == std::end(users.user_id)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->maxPlayers > 256 || param->mode > 3 || param->playerCount > 256) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->playerCount != 0 && param->players == nullptr) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + for (std::uint32_t index = 0; index < param->playerCount; ++index) { + const auto& player = param->players[index]; + if (player.accountId == 0 || player.option0 > 1 || player.option1 > 1 || player.mode > 2 || !std::all_of(std::begin(player.reserved), std::end(player.reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->maxPlayers == 1 && player.option0 == 1) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + } + if (!std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } int APS5_VABI scePlayerSelectionDialogClose(void) { if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.load() != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -53,14 +70,13 @@ int APS5_VABI scePlayerSelectionDialogGetStatus(void) { return g_status.load(); } -int APS5_VABI scePlayerSelectionDialogGetResult(void* result) { +int APS5_VABI scePlayerSelectionDialogGetResult(PlayerSelectionDialogResult* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &canceled, sizeof(canceled)); - return 0; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + return COMMON_DIALOG_RESULT_USER_CANCELED; } int APS5_VABI scePlayerSelectionDialogParamInitialize(void* param) { diff --git a/core/libs/tests/GuestPlayerSelectionDialog.cpp b/core/libs/tests/GuestPlayerSelectionDialog.cpp index 8814165e4f..4d3e163d12 100644 --- a/core/libs/tests/GuestPlayerSelectionDialog.cpp +++ b/core/libs/tests/GuestPlayerSelectionDialog.cpp @@ -2,13 +2,17 @@ #include #include #include +#include "prx/libSceCommonDialog/CommonDialog.hpp" +#include "prx/libSceUserService/UserService.hpp" extern "C" { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* param); +int APS5_VABI sceUserServiceTerminate(); int APS5_VABI scePlayerSelectionDialogInitialize(void); -int APS5_VABI scePlayerSelectionDialogOpen(const void* param); +int APS5_VABI scePlayerSelectionDialogOpen(const PlayerSelectionDialogParam* param); int APS5_VABI scePlayerSelectionDialogUpdateStatus(void); int APS5_VABI scePlayerSelectionDialogGetStatus(void); -int APS5_VABI scePlayerSelectionDialogGetResult(void* result); +int APS5_VABI scePlayerSelectionDialogGetResult(PlayerSelectionDialogResult* result); int APS5_VABI scePlayerSelectionDialogParamInitialize(void* param); int APS5_VABI scePlayerSelectionDialogClose(void); int APS5_VABI scePlayerSelectionDialogTerminate(void); @@ -28,8 +32,13 @@ constexpr int kResultUserCanceled = 1; } int main() { - std::int64_t param = 0; - std::int32_t result[2] = {-1, -1}; + Require(sceUserServiceInitialize(nullptr) == 0); + PlayerSelectionDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = USER_SERVICE_INITIAL_USER_ID; + PlayerSelectionDialogResult result{-1, 99, nullptr, 77, 55}; Require(scePlayerSelectionDialogTerminate() == kErrNotInitialized); Require(scePlayerSelectionDialogOpen(¶m) == kErrNotInitialized); @@ -42,8 +51,8 @@ int main() { threw = true; } Require(threw); - Require(param == 0); - Require(scePlayerSelectionDialogGetResult(result) == kErrNotInitialized); + Require(param.size == sizeof(param)); + Require(scePlayerSelectionDialogGetResult(&result) == kErrNotInitialized); Require(scePlayerSelectionDialogGetStatus() == 0); Require(scePlayerSelectionDialogUpdateStatus() == 0); @@ -53,20 +62,51 @@ int main() { Require(scePlayerSelectionDialogUpdateStatus() == 1); Require(scePlayerSelectionDialogOpen(nullptr) == kErrArgNull); - Require(scePlayerSelectionDialogGetResult(result) == kErrNotFinished); + Require(scePlayerSelectionDialogGetResult(&result) == kErrNotFinished); + param.size = 0; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.size = sizeof(param); + param.maxPlayers = 257; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.maxPlayers = 1; + param.mode = 4; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.mode = 0; + param.playerCount = 1; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + PlayerSelectionDialogPlayer player{}; + param.players = &player; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + player.accountId = 1; + player.option0 = 1; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.maxPlayers = 2; + player.option1 = 2; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + player.option1 = 1; + player.mode = 3; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + player.mode = 2; + player.reserved[63] = 1; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + player.reserved[63] = 0; + param.reserved[63] = 1; + Require(scePlayerSelectionDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.reserved[63] = 0; Require(scePlayerSelectionDialogOpen(¶m) == 0); Require(scePlayerSelectionDialogGetStatus() == kStatusFinished); Require(scePlayerSelectionDialogUpdateStatus() == kStatusFinished); Require(scePlayerSelectionDialogGetResult(nullptr) == kErrArgNull); - Require(scePlayerSelectionDialogGetResult(result) == 0); - Require(result[0] == kResultUserCanceled); - Require(result[1] == -1); + Require(scePlayerSelectionDialogGetResult(&result) == kResultUserCanceled); + Require(result.result == kResultUserCanceled); + Require(result.option == 99 && result.accountIds == nullptr && result.count == 77 && result.padding == 55); - Require(scePlayerSelectionDialogClose() == 0); + Require(scePlayerSelectionDialogClose() == static_cast(0x80B8000Bu)); Require(scePlayerSelectionDialogTerminate() == 0); Require(scePlayerSelectionDialogTerminate() == kErrNotInitialized); Require(scePlayerSelectionDialogGetStatus() == 0); Require(scePlayerSelectionDialogClose() == kErrNotInitialized); + Require(sceUserServiceTerminate() == 0); } diff --git a/docs/dev/debt/libScePlayerSelectionDialog.md b/docs/dev/debt/libScePlayerSelectionDialog.md index c4936f8740..96b3ef7efc 100644 --- a/docs/dev/debt/libScePlayerSelectionDialog.md +++ b/docs/dev/debt/libScePlayerSelectionDialog.md @@ -2,7 +2,7 @@ ### Silent stubs -- [libScePlayerSelectionDialog](../../../core/libs/prx/libScePlayerSelectionDialog/Export.cpp) - the player-selection dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. `GetResult` writes only the first `int32` (`USER_CANCELED`); the rest of `ScePlayerSelectionDialogResult` (player list pointer and length) is not filled. EA SPORTS FC 27 (PPSA34015) fails to load `libScePlayerSelectionDialog.prx` without `Initialize`, `Open`, `Close`, `GetStatus`, `UpdateStatus` and `GetResult` (`unresolved ELF import` / `GetLastError: 127`) +- [libScePlayerSelectionDialog](../../../core/libs/prx/libScePlayerSelectionDialog/Export.cpp) shows no selection UI and immediately reports cancellation. Account discovery, selection and successful-result list delivery are not implemented. ### Unknown function info From 6fdcac937c198e594b922155d72fa14cffa05e39 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:07:22 +0300 Subject: [PATCH 469/523] fix(playgodialog): restore lifecycle errors and result layout --- core/libs/SceTypes.hpp | 3 ++- core/libs/prx/libScePlayGoDialog/Export.cpp | 28 +++++++++++++-------- core/libs/tests/GuestPlayGoDialog.cpp | 26 ++++++++++++------- docs/dev/debt/libScePlayGoDialog.md | 2 +- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 38b336e42c..95443d9e12 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -2006,8 +2006,9 @@ union PlayGoOptionalChunk { }; struct PlayGoDialogResult { + std::int32_t errorCode; std::int32_t result; - std::int32_t reserved[9]; + std::uint8_t reserved[32]; }; struct RudpPollEvent { diff --git a/core/libs/prx/libScePlayGoDialog/Export.cpp b/core/libs/prx/libScePlayGoDialog/Export.cpp index 65b0b8eba1..e341d17b84 100644 --- a/core/libs/prx/libScePlayGoDialog/Export.cpp +++ b/core/libs/prx/libScePlayGoDialog/Export.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -6,6 +8,8 @@ #include "prx/libc/include/General.hpp" static_assert(sizeof(PlayGoDialogResult) == 40); +static_assert(offsetof(PlayGoDialogResult, result) == 4); +static_assert(offsetof(PlayGoDialogResult, reserved) == 8); namespace { @@ -15,11 +19,13 @@ constexpr std::int32_t STATUS_RUNNING = 2; constexpr std::int32_t STATUS_FINISHED = 3; constexpr std::int32_t RESULT_OK = 0; +constexpr std::int32_t ERROR_ARG_NULL = static_cast(0x80B8000Du); +constexpr std::int32_t ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); -constexpr std::int32_t ERROR_NOT_INITIALIZED = static_cast(0x80ED0001); -constexpr std::int32_t ERROR_ALREADY_INITIALIZED = static_cast(0x80ED0002); -constexpr std::int32_t ERROR_PARAM_INVALID = static_cast(0x80ED0003); -constexpr std::int32_t ERROR_INVALID_STATE = static_cast(0x80ED0005); +constexpr std::int32_t ERROR_NOT_INITIALIZED = static_cast(0x80B80003); +constexpr std::int32_t ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004); +constexpr std::int32_t ERROR_PARAM_INVALID = static_cast(0x80B8000A); +constexpr std::int32_t ERROR_INVALID_STATE = static_cast(0x80B80007); constexpr std::int32_t COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); std::atomic g_status{STATUS_NONE}; @@ -44,7 +50,8 @@ int APS5_VABI scePlayGoDialogTerminate(void) { } int APS5_VABI scePlayGoDialogOpen(const void* param) { - if (!param) return ERROR_PARAM_INVALID; + if (!param) return ERROR_ARG_NULL; + if (g_status.load() == STATUS_NONE) return ERROR_NOT_INITIALIZED; int expected = STATUS_INITIALIZED; if (!g_status.compare_exchange_strong(expected, STATUS_FINISHED)) { expected = STATUS_FINISHED; @@ -55,6 +62,7 @@ int APS5_VABI scePlayGoDialogOpen(const void* param) { int APS5_VABI scePlayGoDialogClose(void) { if (g_status.load() == STATUS_NONE) return ERROR_NOT_INITIALIZED; + if (g_status.load() != STATUS_RUNNING) return ERROR_NOT_RUNNING; g_status.store(STATUS_FINISHED); return 0; } @@ -65,13 +73,13 @@ int APS5_VABI scePlayGoDialogUpdateStatus(void) { return g_status.load(); } -int APS5_VABI scePlayGoDialogGetResult(void* result) { +int APS5_VABI scePlayGoDialogGetResult(PlayGoDialogResult* result) { + if (result == nullptr) return ERROR_ARG_NULL; + if (!std::all_of(std::begin(result->reserved), std::end(result->reserved), [](std::uint8_t value) { return value == 0; })) return ERROR_PARAM_INVALID; if (g_status.load() == STATUS_NONE) return ERROR_NOT_INITIALIZED; - if (!result) return ERROR_PARAM_INVALID; if (g_status.load() != STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - auto* r = static_cast(result); - std::memset(r, 0, sizeof(*r)); - r->result = RESULT_OK; + *result = {}; + result->result = RESULT_OK; return 0; } diff --git a/core/libs/tests/GuestPlayGoDialog.cpp b/core/libs/tests/GuestPlayGoDialog.cpp index 470cfee79d..0a8291f96c 100644 --- a/core/libs/tests/GuestPlayGoDialog.cpp +++ b/core/libs/tests/GuestPlayGoDialog.cpp @@ -11,27 +11,30 @@ int APS5_VABI scePlayGoDialogOpen(const void* param); int APS5_VABI scePlayGoDialogClose(void); int APS5_VABI scePlayGoDialogUpdateStatus(void); int APS5_VABI scePlayGoDialogGetStatus(void); -int APS5_VABI scePlayGoDialogGetResult(void* result); +int APS5_VABI scePlayGoDialogGetResult(PlayGoDialogResult* result); } static void Require(bool value) { if (!value) std::abort(); } int main() { - constexpr std::int32_t notInitialized = static_cast(0x80ED0001); - constexpr std::int32_t alreadyInitialized = static_cast(0x80ED0002); - constexpr std::int32_t paramInvalid = static_cast(0x80ED0003); - constexpr std::int32_t invalidState = static_cast(0x80ED0005); + constexpr std::int32_t notInitialized = static_cast(0x80B80003); + constexpr std::int32_t alreadyInitialized = static_cast(0x80B80004); + constexpr std::int32_t paramInvalid = static_cast(0x80B8000D); constexpr std::int32_t notFinished = static_cast(0x80B80005u); Require(scePlayGoDialogGetStatus() == 0); Require(scePlayGoDialogTerminate() == notInitialized); Require(scePlayGoDialogClose() == notInitialized); Require(scePlayGoDialogOpen(nullptr) == paramInvalid); - Require(scePlayGoDialogGetResult(nullptr) == notInitialized); + Require(scePlayGoDialogGetResult(nullptr) == paramInvalid); const int openParam = 1; - Require(scePlayGoDialogOpen(&openParam) == invalidState); + Require(scePlayGoDialogOpen(&openParam) == notInitialized); + PlayGoDialogResult early{}; + Require(scePlayGoDialogGetResult(&early) == notInitialized); + early.reserved[31] = 1; + Require(scePlayGoDialogGetResult(&early) == static_cast(0x80B8000Au)); Require(scePlayGoDialogInitialize() == 0); Require(scePlayGoDialogGetStatus() == 1); Require(scePlayGoDialogInitialize() == alreadyInitialized); @@ -47,8 +50,13 @@ int main() { PlayGoDialogResult result{}; Require(scePlayGoDialogGetResult(&result) == 0); - Require(result.result == 0); - Require(scePlayGoDialogClose() == 0); + Require(result.result == 0 && result.errorCode == 0); + result.errorCode = 7; + result.result = 9; + result.reserved[0] = 1; + Require(scePlayGoDialogGetResult(&result) == static_cast(0x80B8000Au)); + Require(result.errorCode == 7 && result.result == 9 && result.reserved[0] == 1); + Require(scePlayGoDialogClose() == static_cast(0x80B8000Bu)); Require(scePlayGoDialogTerminate() == 0); Require(scePlayGoDialogGetStatus() == 0); Require(scePlayGoDialogTerminate() == notInitialized); diff --git a/docs/dev/debt/libScePlayGoDialog.md b/docs/dev/debt/libScePlayGoDialog.md index 7e06e1309c..53b4cd2147 100644 --- a/docs/dev/debt/libScePlayGoDialog.md +++ b/docs/dev/debt/libScePlayGoDialog.md @@ -2,4 +2,4 @@ ### Unknown function info -- [scePlayGoDialogGetResult](../../../core/libs/prx/libScePlayGoDialog/Export.cpp) (libScePlayGoDialog) - checks that the dialog has reached STATUS_FINISHED and returns 0x80B80005 (COMMON_DIALOG_ERROR_NOT_FINISHED) when called before the dialog completes, following the other common dialog modules in the tree (MsgDialog, SigninDialog, PlayerSelectionDialog, SaveDataDialog). shadPS4's scePlayGoDialogGetResult is a dummy that always returns OK and provides no evidence for either value; the exact return code and intermediate state check have not been verified against a real hardware trace or title measurement. +- [scePlayGoDialogOpen](../../../core/libs/prx/libScePlayGoDialog/Export.cpp) does not validate or copy the native nested parameters or query download progress. The host UI stub immediately reports completion; service-driven failures and transitions remain unimplemented. From 4722e7d859712bef03e00ba0e2a9d0868a21885b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:07:23 +0300 Subject: [PATCH 470/523] docs(nkweb): close verified initialization signature debt --- docs/dev/debt/libSceNKWeb.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/dev/debt/libSceNKWeb.md b/docs/dev/debt/libSceNKWeb.md index 4191fc13a3..5b311c43ed 100644 --- a/docs/dev/debt/libSceNKWeb.md +++ b/docs/dev/debt/libSceNKWeb.md @@ -1,5 +1,5 @@ # libSceNKWeb technical debt -### Unknown function info +### Functional -- [sceNKWebInitialize and sceNKWebTerminate](../../../core/libs/prx/libSceNKWeb/Export.cpp) (libSceNKWeb) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNKWeb.c), but no credible prototypes were found. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified +- [sceNKWebInitialize and sceNKWebTerminate](../../../core/libs/prx/libSceNKWeb/Export.cpp) remain throwing stubs. Both native exports take no input arguments and return a 32-bit status; decompiler SIMD parameters are unused register propagation through initialization and cleanup helpers. From 2a8c0f82ab512e9a96d0d04efc7f820be3e6b170 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:12:08 +0300 Subject: [PATCH 471/523] fix(pad): define vr controller sample layout --- core/libs/prx/libScePad/Export.cpp | 30 ++++++++++++++++++++++++++++-- docs/dev/debt/libScePad.md | 2 +- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 34ff03cd2b..3a6f40f658 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -26,7 +26,33 @@ struct PadRemoteSetting; struct PadVirtualTriggerSetting; struct PadProcessFocusParameter; struct PadDataExtended; -struct PadVrControllerData; +struct PadVrControllerData { + std::uint32_t buttons; + std::uint8_t stick[2]; + std::uint8_t unused6[2]; + std::uint8_t triggers[2]; + std::uint8_t unused10[2]; + std::uint32_t touchButtons; + std::uint8_t touchValues[4]; + float acceleration[3]; + float angularVelocity[3]; + float orientation[4]; + std::uint8_t unused60[4]; + std::uint8_t connected; + std::uint8_t connectedCount; + std::uint8_t unused66[6]; + std::uint64_t timestamp; + std::uint8_t unused80[24]; +}; + +static_assert(sizeof(PadVrControllerData) == 104); +static_assert(offsetof(PadVrControllerData, triggers) == 8); +static_assert(offsetof(PadVrControllerData, touchButtons) == 12); +static_assert(offsetof(PadVrControllerData, acceleration) == 20); +static_assert(offsetof(PadVrControllerData, angularVelocity) == 32); +static_assert(offsetof(PadVrControllerData, orientation) == 44); +static_assert(offsetof(PadVrControllerData, connected) == 64); +static_assert(offsetof(PadVrControllerData, timestamp) == 72); struct PadVrControllerDeviceInformation; struct PadVrTriggerEffect; struct PadVrTriggerEffects; @@ -363,7 +389,7 @@ int APS5_VABI scePadVrControllerGetTriggerEffectState(std::int32_t leftHandle, s return 0; } -int APS5_VABI scePadVrControllerReadState(int handle, void* state) { +int APS5_VABI scePadVrControllerReadState(int handle, PadVrControllerData* state) { (void)handle; (void)state; NotImplemented_nid_no_patch(__func__); diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 10146571bc..3fdf755b89 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -9,7 +9,7 @@ - [scePadUnknown_n3kSX62fgNo](../../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `n3kSX62fgNo`) - the name is unknown, and the 0 returned after `scePadInit` is inferred, not measured. In the OS 10.01 system module the function takes no arguments, returns `0x80920005` before `scePadInit`, and otherwise sends a per-process value of 1 to ShellCore over Mbus, returning `0x809200ff` if that call fails and 0 if it succeeds, with or without an open handle. The #3177 probe measured `0x809200ff` in a process that is not a game; a game process is assumed to get 0. The ShellCore setting it changes is unknown and not modelled - [scePadSetAngularVelocityDeadbandState](../../../core/libs/prx/libScePad/src/PadState.cpp) (libScePad) - with the deadband on, each angular velocity axis whose magnitude is below 0.017453292 rad/s (1 degree per second) reads as 0, before the orientation is integrated; it starts off. Threshold, per-axis test and default follow [duaLib](https://github.com/WujekFoliarz/duaLib/blob/03bad1bea2a36561b520846776f2a07ced6773e0/src/source/duaLib.cpp#L44), which reimplements the PC `libScePad.dll`; the console's threshold is not measured, and the deadband is kept per library rather than per handle since one pad handle exists - [scePadOpen and scePadOpenExt](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - special ports (type 2) return `DEVICE_NOT_CONNECTED` (0x80920007) because the host has no special controller, and `scePadGetHandle` has no handle for them; whether the console opens a handle and reports the device as disconnected later is unknown. scePadOpenExt handles only special ports. PPSA01317 passes a 16-byte param that names the device by vendor and product id (`b7 0e 04 0e ...` for 0x0eb7:0x0e04) and skips the user on any negative result. Other port types throw -- [scePadVrControllerReadState](../../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `iA-DdobUen8`) - the name hashes to the imported NID; the `(int handle, void* state)` signature follows [PS5VR](https://github.com/theghostonline/PS5VR/blob/0e408909ce7a39891a2e06eee3438d07a09e11e3/app/src/app_vr.c#L326-L369) (PPSA99177), which passes a PS VR2 Sense controller handle from `scePadOpen` type 3 or 4 and a 0x68-byte state it reads as buttons at 0, stick bytes at 4 and a connected byte at 0x40. The title calls it only after `sceHmd2Initialize` succeeds, which it does not here, so every call throws; the state layout is otherwise unknown +- [scePadVrControllerReadState](../../../core/libs/prx/libScePad/Export.cpp) - the 104-byte output layout is recovered, including buttons, stick, triggers, touch fields, motion, connection state and timestamp. Touch-value semantics and kernel report production remain incomplete; the export still throws. - [scePadVrControllerReadCombinedState](../../../core/libs/prx/libScePad/Export.cpp) - the two handle arguments and 128-byte output are recovered; output field semantics remain incomplete. - [scePadSetVibrationMode](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - modes 1 to 3 and 5 are accepted and stored. On a PS5 (firmware 13.60) the call returns `0x80920005` before `scePadInit`, then rejects a mode of 0 or below with `INVALID_ARG` before it checks the handle, and returns `INVALID_HANDLE` for a bad handle with any higher mode; whether an open handle accepts modes above 3 was not measured, since the payload could not open a pad; 5 is accepted because PPSA25286 passes it on its open handle at start-up; the meanings of modes 3 and 5 are unknown (PPSA21480 calls it with 3 right after `scePadOpen` and aborts on failure; [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Pad/Pad.cs) names only 1 and 2). The stored mode has no other effect - [scePadIsRemoteController](../../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false From a159f77523f4a018fcd11169bb96854720b10989 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:14:28 +0300 Subject: [PATCH 472/523] fix(npauthdialog): restore parameter validation and lifecycle --- .../prx/libSceCommonDialog/CommonDialog.hpp | 18 ++++++ .../CMakeLists.txt | 4 +- .../Export.cpp | 26 ++++++--- .../tests/GuestNpAuthAuthorizedAppDialog.cpp | 58 +++++++++++++++---- .../debt/libSceNpAuthAuthorizedAppDialog.md | 3 +- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index bb2d229e7c..a1f4710b79 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -12,6 +12,24 @@ struct CommonDialogBaseParam { std::uint32_t magic; }; +struct NpAuthAuthorizedAppDialogParam { + CommonDialogBaseParam base; + std::uint64_t size; + std::int32_t userId; + std::uint32_t padding60; + const char* application; + const char* text; + std::uint32_t mode; + std::uint32_t padding84; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(NpAuthAuthorizedAppDialogParam) == 120); +static_assert(offsetof(NpAuthAuthorizedAppDialogParam, application) == 64); +static_assert(offsetof(NpAuthAuthorizedAppDialogParam, text) == 72); +static_assert(offsetof(NpAuthAuthorizedAppDialogParam, mode) == 80); +static_assert(offsetof(NpAuthAuthorizedAppDialogParam, reserved) == 88); + struct G2PDialogParam { CommonDialogBaseParam base; std::int32_t userId; diff --git a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt index 3d65cfe796..15dcbb4f2e 100644 --- a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt +++ b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/CMakeLists.txt @@ -2,10 +2,12 @@ add_library(libSceNpAuthAuthorizedAppDialog SHARED EXCLUDE_FROM_ALL Export.cpp) target_include_directories(libSceNpAuthAuthorizedAppDialog PRIVATE ${LIBS_INCLUDE_DIR}) configure_windows_unwind(libSceNpAuthAuthorizedAppDialog) +target_link_libraries(libSceNpAuthAuthorizedAppDialog PRIVATE libSceUserService) + if(BUILD_TESTING) add_test_executable(guest_np_auth_authorized_app_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpAuthAuthorizedAppDialog.cpp) target_include_directories(guest_np_auth_authorized_app_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_np_auth_authorized_app_dialog_tests PRIVATE libSceNpAuthAuthorizedAppDialog) + target_link_libraries(guest_np_auth_authorized_app_dialog_tests PRIVATE libSceNpAuthAuthorizedAppDialog libSceUserService) add_test(NAME guest_np_auth_authorized_app_dialog COMMAND guest_np_auth_authorized_app_dialog_tests) if(WIN32) set_tests_properties(guest_np_auth_authorized_app_dialog PROPERTIES ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp index f1cbb534c2..9244dedeea 100644 --- a/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp +++ b/core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp @@ -3,8 +3,11 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" +extern "C" int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* users); + namespace { constexpr int COMMON_DIALOG_STATUS_NONE = 0; @@ -12,7 +15,8 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); +constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); @@ -30,11 +34,17 @@ int APS5_VABI sceNpAuthAuthorizedAppDialogInitialize(void) { return 0; } -int APS5_VABI sceNpAuthAuthorizedAppDialogOpen(const void* param) { +int APS5_VABI sceNpAuthAuthorizedAppDialogOpen(const NpAuthAuthorizedAppDialogParam* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->size != sizeof(*param) || param->userId == -1) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + UserServiceLoginUserIdList users{}; + if (sceUserServiceGetLoginUserIdList(&users) != 0 || std::find(std::begin(users.user_id), std::end(users.user_id), param->userId) == std::end(users.user_id)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->application == nullptr || param->application[0] == 0 || param->text == nullptr || param->text[0] == 0) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (std::strlen(param->text) > 1024 || param->mode > 1 || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -47,18 +57,18 @@ int APS5_VABI sceNpAuthAuthorizedAppDialogGetStatus(void) { return g_status.load(); } -int APS5_VABI sceNpAuthAuthorizedAppDialogGetResult(void* result) { +int APS5_VABI sceNpAuthAuthorizedAppDialogGetResult(std::int32_t* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &canceled, sizeof(canceled)); - return 0; + *result = COMMON_DIALOG_RESULT_USER_CANCELED; + return COMMON_DIALOG_RESULT_USER_CANCELED; } int APS5_VABI sceNpAuthAuthorizedAppDialogClose(void) { if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.load() != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp b/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp index 7eecd54c43..aedef7375a 100644 --- a/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp +++ b/core/libs/tests/GuestNpAuthAuthorizedAppDialog.cpp @@ -1,13 +1,18 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include +#include "prx/libSceCommonDialog/CommonDialog.hpp" +#include "prx/libSceUserService/UserService.hpp" extern "C" { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* param); +int APS5_VABI sceUserServiceTerminate(); int APS5_VABI sceNpAuthAuthorizedAppDialogInitialize(void); -int APS5_VABI sceNpAuthAuthorizedAppDialogOpen(const void* param); +int APS5_VABI sceNpAuthAuthorizedAppDialogOpen(const NpAuthAuthorizedAppDialogParam* param); int APS5_VABI sceNpAuthAuthorizedAppDialogUpdateStatus(void); int APS5_VABI sceNpAuthAuthorizedAppDialogGetStatus(void); -int APS5_VABI sceNpAuthAuthorizedAppDialogGetResult(void* result); +int APS5_VABI sceNpAuthAuthorizedAppDialogGetResult(std::int32_t* result); int APS5_VABI sceNpAuthAuthorizedAppDialogClose(void); int APS5_VABI sceNpAuthAuthorizedAppDialogTerminate(void); } @@ -28,7 +33,16 @@ constexpr int kResultUserCanceled = 1; } int main() { - std::int64_t param = 0; + Require(sceUserServiceInitialize(nullptr) == 0); + NpAuthAuthorizedAppDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = USER_SERVICE_INITIAL_USER_ID; + char application[136] = "test"; + std::string text(1024, 'a'); + param.application = application; + param.text = text.c_str(); std::int32_t result[2] = {-1, -1}; Require(sceNpAuthAuthorizedAppDialogGetStatus() == kStatusNone); @@ -45,27 +59,51 @@ int main() { Require(sceNpAuthAuthorizedAppDialogOpen(nullptr) == kErrArgNull); Require(sceNpAuthAuthorizedAppDialogGetResult(result) == kErrNotFinished); + constexpr int invalidArgument = static_cast(0x80B8000Au); + param.size = 119; + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + param.size = sizeof(param); + param.userId = 42; + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + param.userId = USER_SERVICE_INITIAL_USER_ID; + param.application = nullptr; + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + param.application = application; + param.text = ""; + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + text.push_back('a'); + param.text = text.c_str(); + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + text.pop_back(); + param.text = text.c_str(); + param.mode = 2; + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + param.mode = 1; + param.reserved[31] = 1; + Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == invalidArgument); + param.reserved[31] = 0; Require(sceNpAuthAuthorizedAppDialogOpen(¶m) == 0); Require(sceNpAuthAuthorizedAppDialogUpdateStatus() == kStatusFinished); Require(sceNpAuthAuthorizedAppDialogGetStatus() == kStatusFinished); Require(sceNpAuthAuthorizedAppDialogGetResult(nullptr) == kErrArgNull); - Require(sceNpAuthAuthorizedAppDialogGetResult(result) == 0); + Require(sceNpAuthAuthorizedAppDialogGetResult(result) == kResultUserCanceled); Require(result[0] == kResultUserCanceled); Require(result[1] == -1); - Require(sceNpAuthAuthorizedAppDialogClose() == 0); + Require(sceNpAuthAuthorizedAppDialogClose() == static_cast(0x80B8000Bu)); Require(sceNpAuthAuthorizedAppDialogTerminate() == 0); Require(sceNpAuthAuthorizedAppDialogTerminate() == kErrNotInitialized); Require(sceNpAuthAuthorizedAppDialogGetStatus() == kStatusNone); Require(sceNpAuthAuthorizedAppDialogInitialize() == 0); Require(sceNpAuthAuthorizedAppDialogGetStatus() == kStatusInitialized); - Require(sceNpAuthAuthorizedAppDialogClose() == 0); - Require(sceNpAuthAuthorizedAppDialogGetStatus() == kStatusFinished); - result[0] = -1; - Require(sceNpAuthAuthorizedAppDialogGetResult(result) == 0); - Require(result[0] == kResultUserCanceled); + Require(sceNpAuthAuthorizedAppDialogClose() == static_cast(0x80B8000Bu)); + Require(sceNpAuthAuthorizedAppDialogGetStatus() == kStatusInitialized); + Require(sceNpAuthAuthorizedAppDialogGetResult(result) == kErrNotFinished); Require(sceNpAuthAuthorizedAppDialogTerminate() == 0); + Require(sceNpAuthAuthorizedAppDialogOpen(nullptr) == kErrArgNull); + Require(sceNpAuthAuthorizedAppDialogGetResult(nullptr) == kErrArgNull); + Require(sceUserServiceTerminate() == 0); return 0; } diff --git a/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md b/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md index e6b5fa19a6..6f98749d82 100644 --- a/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md +++ b/docs/dev/debt/libSceNpAuthAuthorizedAppDialog.md @@ -2,5 +2,4 @@ ### Silent stubs -- [libSceNpAuthAuthorizedAppDialog](../../../core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp) - the authorized-app dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no PSN sign-in to authorize an app against. The signatures follow the other common dialogs (`Open` takes a parameter block, `GetResult` writes a 32-bit result); Minecraft (PPSA17221) imports the seven functions and fails to load without them. -- [sceNpAuthAuthorizedAppDialogClose](../../../core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp) - `sceNpAuthAuthorizedAppDialogClose` moves an initialized dialog straight to `COMMON_DIALOG_STATUS_FINISHED`, also when called before `Open`, following the Signin / NetCtlAp dialogs; MsgDialog / SaveDataDialog / ErrorDialog return NOT_RUNNING or INVALID_STATE for that case, and which one the real library does is unverified. +- [libSceNpAuthAuthorizedAppDialog](../../../core/libs/prx/libSceNpAuthAuthorizedAppDialog/Export.cpp) - the authorized-app dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no PSN sign-in to authorize an app against. Parameter fields at offsets 64 and 72 feed a 136-byte application record and a text value of at most 1024 bytes; their service-side meaning remains incomplete. From 420e1aee0767a6bf7643e447dfb3df8b2c08ff3e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:15:31 +0300 Subject: [PATCH 473/523] fix(vrsetupdialog): restore parameter header and error codes --- .../prx/libSceCommonDialog/CommonDialog.hpp | 9 ++++++ core/libs/prx/libSceVrSetupDialog/Export.cpp | 23 +++++++------- core/libs/tests/GuestVrSetupDialog.cpp | 30 ++++++++++++------- docs/dev/debt/libSceVrSetupDialog.md | 2 +- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index a1f4710b79..2f2f6381de 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -30,6 +30,15 @@ static_assert(offsetof(NpAuthAuthorizedAppDialogParam, text) == 72); static_assert(offsetof(NpAuthAuthorizedAppDialogParam, mode) == 80); static_assert(offsetof(NpAuthAuthorizedAppDialogParam, reserved) == 88); +struct VrSetupDialogParam { + std::uint64_t size; + std::uint8_t reserved8; + std::uint8_t reserved9; + std::uint8_t data[46]; +}; + +static_assert(sizeof(VrSetupDialogParam) == 56); + struct G2PDialogParam { CommonDialogBaseParam base; std::int32_t userId; diff --git a/core/libs/prx/libSceVrSetupDialog/Export.cpp b/core/libs/prx/libSceVrSetupDialog/Export.cpp index f4b2718a30..73e247dafe 100644 --- a/core/libs/prx/libSceVrSetupDialog/Export.cpp +++ b/core/libs/prx/libSceVrSetupDialog/Export.cpp @@ -3,6 +3,7 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" namespace { @@ -12,9 +13,11 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); +constexpr int VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x8A7C0001u); +constexpr int VR_SETUP_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x8A7C0002u); +constexpr int VR_SETUP_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x8A7C0003u); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); -constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); +constexpr int VR_SETUP_DIALOG_ERROR_BUSY = static_cast(0x8A7C0005u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; @@ -26,15 +29,15 @@ extern "C" { int APS5_VABI sceVrSetupDialogInitialize(void) { int expected = COMMON_DIALOG_STATUS_NONE; - if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; + if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return VR_SETUP_DIALOG_ERROR_ALREADY_INITIALIZED; return 0; } -int APS5_VABI sceVrSetupDialogOpen(const void* param) { +int APS5_VABI sceVrSetupDialogOpen(const VrSetupDialogParam* param) { + if (param == nullptr || param->size != sizeof(*param) || param->reserved8 != 0 || param->reserved9 != 0) return VR_SETUP_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (status == COMMON_DIALOG_STATUS_NONE) return VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED; + if (status == COMMON_DIALOG_STATUS_RUNNING) return VR_SETUP_DIALOG_ERROR_BUSY; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -58,13 +61,13 @@ int APS5_VABI sceVrSetupDialogGetResult(void* result) { } int APS5_VABI sceVrSetupDialogClose(void) { - if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - g_status = COMMON_DIALOG_STATUS_FINISHED; + if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.load() == COMMON_DIALOG_STATUS_RUNNING) g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } int APS5_VABI sceVrSetupDialogTerminate(void) { - if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED; return 0; } diff --git a/core/libs/tests/GuestVrSetupDialog.cpp b/core/libs/tests/GuestVrSetupDialog.cpp index 56a48ccdd0..b879875b89 100644 --- a/core/libs/tests/GuestVrSetupDialog.cpp +++ b/core/libs/tests/GuestVrSetupDialog.cpp @@ -1,10 +1,11 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include "prx/libSceCommonDialog/CommonDialog.hpp" extern "C" { int APS5_VABI sceVrSetupDialogInitialize(void); -int APS5_VABI sceVrSetupDialogOpen(const void* param); +int APS5_VABI sceVrSetupDialogOpen(const VrSetupDialogParam* param); int APS5_VABI sceVrSetupDialogUpdateStatus(void); int APS5_VABI sceVrSetupDialogGetStatus(void); int APS5_VABI sceVrSetupDialogGetResult(void* result); @@ -19,8 +20,8 @@ void Require(bool value) { if (!value) std::abort(); } constexpr int kStatusNone = 0; constexpr int kStatusInitialized = 1; constexpr int kStatusFinished = 3; -constexpr int kErrNotInitialized = static_cast(0x80B80003); -constexpr int kErrAlreadyInitialized = static_cast(0x80B80004); +constexpr int kErrNotInitialized = static_cast(0x8A7C0001); +constexpr int kErrAlreadyInitialized = static_cast(0x8A7C0002); constexpr int kErrNotFinished = static_cast(0x80B80005); constexpr int kErrArgNull = static_cast(0x80B8000D); constexpr int kResultUserCanceled = 1; @@ -28,13 +29,14 @@ constexpr int kResultUserCanceled = 1; } int main() { - std::int64_t param = 0; + VrSetupDialogParam param{}; + param.size = sizeof(param); std::int32_t result[2] = {-1, -1}; Require(sceVrSetupDialogTerminate() == kErrNotInitialized); Require(sceVrSetupDialogOpen(¶m) == kErrNotInitialized); Require(sceVrSetupDialogClose() == kErrNotInitialized); - Require(sceVrSetupDialogGetResult(result) == kErrNotInitialized); + Require(sceVrSetupDialogGetResult(result) == static_cast(0x80B80003u)); Require(sceVrSetupDialogGetStatus() == kStatusNone); Require(sceVrSetupDialogUpdateStatus() == kStatusNone); @@ -43,9 +45,19 @@ int main() { Require(sceVrSetupDialogGetStatus() == kStatusInitialized); Require(sceVrSetupDialogUpdateStatus() == kStatusInitialized); - Require(sceVrSetupDialogOpen(nullptr) == kErrArgNull); + Require(sceVrSetupDialogOpen(nullptr) == static_cast(0x8A7C0003u)); Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); + param.size = 55; + Require(sceVrSetupDialogOpen(¶m) == static_cast(0x8A7C0003u)); + param.size = sizeof(param); + param.reserved8 = 1; + Require(sceVrSetupDialogOpen(¶m) == static_cast(0x8A7C0003u)); + param.reserved8 = 0; + param.reserved9 = 1; + Require(sceVrSetupDialogOpen(¶m) == static_cast(0x8A7C0003u)); + param.reserved9 = 0; + param.data[45] = 255; Require(sceVrSetupDialogOpen(¶m) == 0); Require(sceVrSetupDialogGetStatus() == kStatusFinished); Require(sceVrSetupDialogUpdateStatus() == kStatusFinished); @@ -64,9 +76,7 @@ int main() { Require(sceVrSetupDialogInitialize() == 0); Require(sceVrSetupDialogUpdateStatus() == kStatusInitialized); Require(sceVrSetupDialogClose() == 0); - Require(sceVrSetupDialogUpdateStatus() == kStatusFinished); - result[0] = -1; - Require(sceVrSetupDialogGetResult(result) == 0); - Require(result[0] == kResultUserCanceled); + Require(sceVrSetupDialogUpdateStatus() == kStatusInitialized); + Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); Require(sceVrSetupDialogTerminate() == 0); } diff --git a/docs/dev/debt/libSceVrSetupDialog.md b/docs/dev/debt/libSceVrSetupDialog.md index 336e3361d1..138686c11e 100644 --- a/docs/dev/debt/libSceVrSetupDialog.md +++ b/docs/dev/debt/libSceVrSetupDialog.md @@ -2,4 +2,4 @@ ### Silent stubs -- [libSceVrSetupDialog](../../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). The signatures are assumed from the other common dialogs; PPSA12544 imports the library. `sceVrSetupDialogGetStatus` (NID `qRUACYyUNFA`) is present in the firmware export database (aerolib), but direct import by PPSA12544 has not been explicitly confirmed. +- [libSceVrSetupDialog](../../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). Initialization does not check the native process eligibility record, and the 46-byte setup payload remains opaque. Open validates the recovered 56-byte parameter header. GetResult is absent from the supplied module, so its signature and cancellation result remain inferred. From 39cb8593f331d8e96b300d96b7c364b206a3ee4a Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:17:58 +0300 Subject: [PATCH 474/523] fix(usbstoragedialog): restore parameters and result contract --- core/libs/CMakeLists.txt | 4 +- .../prx/libSceCommonDialog/CommonDialog.hpp | 21 ++++++ .../prx/libSceUsbStorageDialog/Export.cpp | 40 ++++++++--- core/libs/tests/GuestUsbStorageDialog.cpp | 69 ++++++++++++++++--- docs/dev/debt/libSceUsbStorageDialog.md | 3 +- 5 files changed, 118 insertions(+), 19 deletions(-) diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 737f8fa20f..84952285be 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -186,6 +186,8 @@ foreach(LIB_DIR ${LIBS_DIRS}) endif() endforeach() +target_link_libraries(libSceUsbStorageDialog PRIVATE libSceUserService) + if(APS5_ENABLE_TRACY) set_target_properties(TracyClient PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${UNPATCHED_DIR} @@ -406,7 +408,7 @@ if(BUILD_TESTING) add_test_executable(guest_usb_storage_dialog_tests tests/GuestUsbStorageDialog.cpp) target_include_directories(guest_usb_storage_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries(guest_usb_storage_dialog_tests PRIVATE libSceUsbStorageDialog libc) + target_link_libraries(guest_usb_storage_dialog_tests PRIVATE libSceUsbStorageDialog libSceUserService libc) configure_windows_unwind(guest_usb_storage_dialog_tests) add_test(NAME guest_usb_storage_dialog COMMAND guest_usb_storage_dialog_tests) if(WIN32) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index 2f2f6381de..3138618de7 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -39,6 +39,27 @@ struct VrSetupDialogParam { static_assert(sizeof(VrSetupDialogParam) == 56); +struct UsbStorageDialogParam { + CommonDialogBaseParam base; + std::uint64_t size; + char path[1024]; + std::int32_t userId; + std::uint32_t padding; +}; + +struct UsbStorageDialogResult { + std::uint64_t size; + std::int32_t result; + std::int32_t status; + std::uint8_t reserved[64]; +}; + +static_assert(offsetof(UsbStorageDialogParam, path) == 56); +static_assert(offsetof(UsbStorageDialogParam, userId) == 1080); +static_assert(sizeof(UsbStorageDialogResult) == 80); +static_assert(offsetof(UsbStorageDialogResult, result) == 8); +static_assert(offsetof(UsbStorageDialogResult, status) == 12); + struct G2PDialogParam { CommonDialogBaseParam base; std::int32_t userId; diff --git a/core/libs/prx/libSceUsbStorageDialog/Export.cpp b/core/libs/prx/libSceUsbStorageDialog/Export.cpp index 78a410fe58..612ac95a57 100644 --- a/core/libs/prx/libSceUsbStorageDialog/Export.cpp +++ b/core/libs/prx/libSceUsbStorageDialog/Export.cpp @@ -3,8 +3,12 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libc/include/General.hpp" +extern "C" int APS5_VABI sceUserServiceGetLoginUserIdList(UserServiceLoginUserIdList* users); + namespace { constexpr int COMMON_DIALOG_STATUS_NONE = 0; @@ -12,7 +16,7 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); @@ -20,22 +24,36 @@ constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; +std::uint32_t g_sdkVersion = 0; +std::atomic_bool g_closed{false}; } extern "C" { int APS5_VABI sceUsbStorageDialogInitialize(void) { + if (g_status.load() != COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; + const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t sdkVersion; + std::memcpy(&sdkVersion, process + 16, sizeof(sdkVersion)); int expected = COMMON_DIALOG_STATUS_NONE; if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; + g_sdkVersion = sdkVersion; + g_closed = false; return 0; } -int APS5_VABI sceUsbStorageDialogOpen(const void* param) { +int APS5_VABI sceUsbStorageDialogOpen(const UsbStorageDialogParam* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + UserServiceLoginUserIdList users{}; + if (param->userId == -1 || sceUserServiceGetLoginUserIdList(&users) != 0 || std::find(std::begin(users.user_id), std::end(users.user_id), param->userId) == std::end(users.user_id)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->path[1023] != 0 || param->path[0] == 0) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (g_sdkVersion >= 0x04500000 && (std::strpbrk(param->path, ",*?\"<>|") != nullptr || std::strstr(param->path, "..") != nullptr)) return static_cast(0x812D0003u); if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + g_closed = false; g_status = COMMON_DIALOG_STATUS_RUNNING; return 0; } @@ -50,20 +68,26 @@ int APS5_VABI sceUsbStorageDialogUpdateStatus(void) { return g_status.load(); } -int APS5_VABI sceUsbStorageDialogGetResult(void* result) { +int APS5_VABI sceUsbStorageDialogGetResult(UsbStorageDialogResult* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (g_closed.load()) { + result->result = COMMON_DIALOG_ERROR_NOT_FINISHED; + result->status = 0; + return 0; + } if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - const std::int32_t dialogResult = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &dialogResult, sizeof(dialogResult)); - return 0; + if (g_sdkVersion >= 0x04500000 && (result->size != sizeof(*result) || result->result != COMMON_DIALOG_ERROR_BUSY)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + return COMMON_DIALOG_RESULT_USER_CANCELED; } int APS5_VABI sceUsbStorageDialogClose(void) { const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; + g_closed = true; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestUsbStorageDialog.cpp b/core/libs/tests/GuestUsbStorageDialog.cpp index 5217f66be0..e21bcc50eb 100644 --- a/core/libs/tests/GuestUsbStorageDialog.cpp +++ b/core/libs/tests/GuestUsbStorageDialog.cpp @@ -2,13 +2,20 @@ #include #include #include +#include +#include +#include "ProcessParametersFixture.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" +#include "prx/libSceUserService/UserService.hpp" extern "C" { +int APS5_VABI sceUserServiceInitialize(const UserServiceInitParam* param); +int APS5_VABI sceUserServiceTerminate(); int APS5_VABI sceUsbStorageDialogInitialize(void); -int APS5_VABI sceUsbStorageDialogOpen(const void* param); +int APS5_VABI sceUsbStorageDialogOpen(const UsbStorageDialogParam* param); int APS5_VABI sceUsbStorageDialogUpdateStatus(void); int APS5_VABI sceUsbStorageDialogGetStatus(void); -int APS5_VABI sceUsbStorageDialogGetResult(void* result); +int APS5_VABI sceUsbStorageDialogGetResult(UsbStorageDialogResult* result); int APS5_VABI sceUsbStorageDialogClose(void); int APS5_VABI sceUsbStorageDialogTerminate(void); } @@ -32,8 +39,21 @@ constexpr int kResultUserCanceled = 1; } int main() { - std::int64_t param = 0; - std::int32_t result = 0x5a5a5a5a; + static std::array process{}; + Require(ProcessParametersFixture::Install(process.data(), process.size())); + std::uint32_t sdkVersion = 0x04500000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(sceUserServiceInitialize(nullptr) == 0); + UsbStorageDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + param.userId = USER_SERVICE_INITIAL_USER_ID; + std::strcpy(param.path, "test"); + UsbStorageDialogResult result; + std::memset(&result, 0x5a, sizeof(result)); + result.size = sizeof(result); + result.result = kErrBusy; Require(sceUsbStorageDialogTerminate() == kErrNotInitialized); Require(sceUsbStorageDialogOpen(¶m) == kErrNotInitialized); @@ -49,6 +69,17 @@ int main() { Require(sceUsbStorageDialogOpen(nullptr) == kErrArgNull); Require(sceUsbStorageDialogGetResult(&result) == kErrNotFinished); + param.path[1023] = 'a'; + Require(sceUsbStorageDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.path[1023] = 0; + std::strcpy(param.path, "a..b"); + Require(sceUsbStorageDialogOpen(¶m) == static_cast(0x812D0003u)); + for (const char invalid : std::string(",*?\"<>|")) { + param.path[0] = invalid; + param.path[1] = 0; + Require(sceUsbStorageDialogOpen(¶m) == static_cast(0x812D0003u)); + } + std::strcpy(param.path, "test"); Require(sceUsbStorageDialogOpen(¶m) == 0); Require(sceUsbStorageDialogOpen(¶m) == kErrBusy); Require(sceUsbStorageDialogGetStatus() == kStatusRunning); @@ -58,8 +89,16 @@ int main() { Require(sceUsbStorageDialogGetStatus() == kStatusFinished); Require(sceUsbStorageDialogGetResult(nullptr) == kErrArgNull); - Require(sceUsbStorageDialogGetResult(&result) == 0); - Require(result == kResultUserCanceled); + result.size = 79; + Require(sceUsbStorageDialogGetResult(&result) == static_cast(0x80B8000Au)); + result.size = sizeof(result); + result.result = 0; + Require(sceUsbStorageDialogGetResult(&result) == static_cast(0x80B8000Au)); + result.result = kErrBusy; + Require(sceUsbStorageDialogGetResult(&result) == kResultUserCanceled); + Require(result.result == kResultUserCanceled); + Require(result.size == sizeof(result) && result.status == 0x5a5a5a5a); + for (const auto value : result.reserved) Require(value == 0x5a); Require(sceUsbStorageDialogClose() == kErrNotRunning); Require(sceUsbStorageDialogTerminate() == 0); @@ -71,9 +110,23 @@ int main() { Require(sceUsbStorageDialogGetStatus() == kStatusRunning); Require(sceUsbStorageDialogClose() == 0); Require(sceUsbStorageDialogGetStatus() == kStatusFinished); - result = 0; + result.result = 0; Require(sceUsbStorageDialogGetResult(&result) == 0); - Require(result == kResultUserCanceled); + Require(result.result == kErrNotFinished && result.status == 0); Require(sceUsbStorageDialogTerminate() == 0); + sdkVersion = 0x04400000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(sceUsbStorageDialogInitialize() == 0); + std::strcpy(param.path, "a..b"); + Require(sceUsbStorageDialogOpen(¶m) == 0); + Require(sceUsbStorageDialogUpdateStatus() == kStatusFinished); + result.size = 0; + result.result = 0; + Require(sceUsbStorageDialogGetResult(&result) == kResultUserCanceled); + Require(result.size == 0 && result.result == kResultUserCanceled); + Require(sceUsbStorageDialogTerminate() == 0); + Require(sceUsbStorageDialogOpen(nullptr) == kErrArgNull); + Require(sceUsbStorageDialogGetResult(nullptr) == kErrArgNull); + Require(sceUserServiceTerminate() == 0); return 0; } diff --git a/docs/dev/debt/libSceUsbStorageDialog.md b/docs/dev/debt/libSceUsbStorageDialog.md index 93445b0d4a..58151aaf70 100644 --- a/docs/dev/debt/libSceUsbStorageDialog.md +++ b/docs/dev/debt/libSceUsbStorageDialog.md @@ -6,5 +6,4 @@ ### Unknown function info -- [sceUsbStorageDialogGetResult](../../../core/libs/prx/libSceUsbStorageDialog/Export.cpp) (libSceUsbStorageDialog) - `SceUsbStorageDialogResult` full layout, size and additional fields are unverified across open references (KytyPS5, shadPS4, fpPS4, SharpProspero); only the 4-byte `result` field (`COMMON_DIALOG_RESULT_USER_CANCELED = 1`) is written to avoid guest memory corruption -- [sceUsbStorageDialogOpen, sceUsbStorageDialogClose](../../../core/libs/prx/libSceUsbStorageDialog/Export.cpp) (libSceUsbStorageDialog) - the relative validation precedence between uninitialized state, busy/running state, and null parameter checks is inferred from standard common dialog conventions and unverified against real console firmware +- [libSceUsbStorageDialog](../../../core/libs/prx/libSceUsbStorageDialog/Export.cpp) - the service-specific result status at offset 12 is not produced because the UI always cancels. The 80-byte result header and SDK-dependent validation are recovered; the remaining 64 bytes are untouched by the native wrapper. The purpose of the Open parameter size field remains unverified; the native wrapper does not validate it. From b688c4044bf9bdb860a84e438c4397860088c145 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:24:45 +0300 Subject: [PATCH 475/523] fix(share): validate extended capture parameters --- core/libs/prx/libSceShare/Export.cpp | 49 +++++++++++++++++++--------- core/libs/tests/GuestShare.cpp | 33 +++++++++++++------ docs/dev/debt/libSceShare.md | 6 ++-- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/core/libs/prx/libSceShare/Export.cpp b/core/libs/prx/libSceShare/Export.cpp index a313edc364..375682887e 100644 --- a/core/libs/prx/libSceShare/Export.cpp +++ b/core/libs/prx/libSceShare/Export.cpp @@ -6,6 +6,28 @@ // Capture and sharing are not emulated; policy and parameter calls are accepted. +struct ShareScreenshotParamExtended { + std::uint64_t size; + std::uint32_t source; + std::uint32_t mode; + const char* text; +}; + +struct ShareVideoClipParamExtended { + std::uint64_t size; + std::uint64_t value8; + std::uint64_t value16; + std::uint32_t mode; + std::uint32_t reserved; + const char* text; +}; + +static_assert(sizeof(ShareScreenshotParamExtended) == 24); +static_assert(offsetof(ShareScreenshotParamExtended, text) == 16); +static_assert(sizeof(ShareVideoClipParamExtended) == 40); +static_assert(offsetof(ShareVideoClipParamExtended, mode) == 24); +static_assert(offsetof(ShareVideoClipParamExtended, text) == 32); + namespace { constexpr std::int32_t ERROR_INVALID_PARAM = static_cast(0x81960002); @@ -96,20 +118,16 @@ int APS5_VABI sceShareUnregisterContentEventCallback(void* callback) { return 0; } -int APS5_VABI sceShareCaptureScreenshotExtended(const void* extended_param, int32_t* req_id) { - (void)extended_param; - if (req_id != nullptr) { - *req_id = REQUEST_ID_INVALID; - } +int APS5_VABI sceShareCaptureScreenshotExtended(const ShareScreenshotParamExtended* param, std::int32_t* requestId) { + if (param == nullptr || param->size != sizeof(*param)) return ERROR_INVALID_PARAM; + if (requestId != nullptr) *requestId = REQUEST_ID_INVALID; return ERROR_NOT_SUPPORTED; } - -int APS5_VABI sceShareCaptureVideoClipExtended(const void* extended_param, int32_t* req_id) { - (void)extended_param; - if (req_id != nullptr) { - *req_id = REQUEST_ID_INVALID; - } +int APS5_VABI sceShareCaptureVideoClipExtended(const ShareVideoClipParamExtended* param, std::int32_t* requestId) { + if (param == nullptr || param->size != sizeof(*param)) return ERROR_INVALID_PARAM; + if (requestId != nullptr) *requestId = REQUEST_ID_INVALID; + if (param->reserved != 0 || (param->mode == 0 && param->value16 != 0)) return ERROR_INVALID_PARAM; return ERROR_NOT_SUPPORTED; } @@ -121,10 +139,11 @@ int APS5_VABI sceShareGetRunningStatus(uint32_t* status) { return 0; } -int APS5_VABI sceShareSetContentParamForApplicationTitle(const char* application_title) { - if (application_title == nullptr) { - return ERROR_INVALID_PARAM; - } +int APS5_VABI sceShareSetContentParamForApplicationTitle(const char* applicationTitle) { + if (applicationTitle == nullptr) return ERROR_INVALID_PARAM; + std::size_t length = 0; + while (length < 5120 && applicationTitle[length] != 0) ++length; + if (length == 0 || length == 5120) return ERROR_INVALID_PARAM; return 0; } diff --git a/core/libs/tests/GuestShare.cpp b/core/libs/tests/GuestShare.cpp index 698b357e1e..0ef5a51bc5 100644 --- a/core/libs/tests/GuestShare.cpp +++ b/core/libs/tests/GuestShare.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { int APS5_VABI sceShareCaptureScreenshotExtended(const void* extended_param, std::int32_t* req_id); @@ -27,17 +28,25 @@ int main() { using Capture = int (APS5_VABI*)(const void*, std::int32_t*); std::int32_t reqId = 7; + constexpr std::int32_t invalidParam = static_cast(0x81960002); for (Capture capture : {sceShareCaptureScreenshotExtended, sceShareCaptureVideoClipExtended}) { + std::uint64_t fields[5]{}; + const std::uint64_t size = capture == sceShareCaptureScreenshotExtended ? 24 : 40; reqId = 7; - Require(capture(param, &reqId) == notSupported); - Require(reqId == -1); - - reqId = 7; - Require(capture(nullptr, &reqId) == notSupported); - Require(reqId == -1); - - Require(capture(param, nullptr) == notSupported); - Require(capture(nullptr, nullptr) == notSupported); + Require(capture(fields, &reqId) == invalidParam && reqId == 7); + Require(capture(nullptr, &reqId) == invalidParam && reqId == 7); + fields[0] = size; + Require(capture(fields, &reqId) == notSupported && reqId == -1); + Require(capture(fields, nullptr) == notSupported); + if (size == 40) { + fields[2] = 1; + reqId = 7; + Require(capture(fields, &reqId) == invalidParam && reqId == -1); + fields[3] = 1; + Require(capture(fields, nullptr) == notSupported); + fields[3] |= std::uint64_t{1} << 32; + Require(capture(fields, nullptr) == invalidParam); + } } for (Capture capture : {sceShareCaptureScreenshot, sceShareCaptureVideoClip}) { @@ -53,7 +62,6 @@ int main() { Require(sceShareOpenMenuForContent(param) == notSupported); Require(sceShareOpenMenuForContent(nullptr) == notSupported); - constexpr std::int32_t invalidParam = static_cast(0x81960002); std::uint8_t status[18]; std::memset(status, 0x5a, sizeof(status)); Require(sceShareGetCurrentStatus(1, status) == 0); @@ -71,5 +79,10 @@ int main() { Require(sceShareGetRunningStatus(nullptr) == invalidParam); Require(sceShareSetContentParamForApplicationTitle("title") == 0); Require(sceShareSetContentParamForApplicationTitle(nullptr) == invalidParam); + Require(sceShareSetContentParamForApplicationTitle("") == invalidParam); + std::string title(5119, 'a'); + Require(sceShareSetContentParamForApplicationTitle(title.c_str()) == 0); + title.push_back('a'); + Require(sceShareSetContentParamForApplicationTitle(title.c_str()) == invalidParam); return 0; } diff --git a/docs/dev/debt/libSceShare.md b/docs/dev/debt/libSceShare.md index 8ad04d2333..e9062c9e03 100644 --- a/docs/dev/debt/libSceShare.md +++ b/docs/dev/debt/libSceShare.md @@ -3,9 +3,9 @@ ### Unknown function info - [sceShareGetCurrentStatus](../../../core/libs/prx/libSceShare/Export.cpp) (libSceShare) - validation (feature flag non-zero, status non-null) and the all-zero 16-byte status taken from the [KytyPS5](https://github.com/KytyPS5/KytyPS5) reimplementation, not confirmed on a PS5 title; the meaning of the recording status values is unknown -- [sceShareGetRunningStatus](../../../core/libs/prx/libSceShare/Export.cpp) (libSceShare) - only the first 32-bit word of the status is known: PPSA01317 treats bits 0x3c of it as a running share feature and gives the call a 40-byte buffer. The host never records or broadcasts, so it stores 0 in that word and leaves the rest untouched; a null status returns `INVALID_PARAM` by analogy with `sceShareGetCurrentStatus` -- [sceShareSetContentParamForApplicationTitle](../../../core/libs/prx/libSceShare/Export.cpp) (libSceShare) - imported by PPSA12544's Unity plugins; the signature follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libShare.cpp), not confirmed on a PS5 title. The application title is only checked for null, as KytyPS5 does, since nothing is captured +- [sceShareGetRunningStatus](../../../core/libs/prx/libSceShare/Export.cpp) - the native wrapper writes only the first 32-bit word, populated from a kernel event flag. The host reports zero because capture and broadcast services are absent; native initialization checks and event-flag failures are not modeled. +- [sceShareSetContentParamForApplicationTitle](../../../core/libs/prx/libSceShare/Export.cpp) - the recovered string length limit is 1 through 5119 bytes. The title is validated but not sent to the capture service; native initialization and service failures are not modeled. ### Functional -- [sceShareCaptureVideoClipExtended](../../../core/libs/prx/libSceShare/Export.cpp) (libSceShare) - imported by PPSA12544's Share plugin; the signature (extended parameter, request id) and the answer follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/147188b143f67c27d8da396e759c19336f64c105/src/libs/libShare.cpp), not confirmed on a PS5 title. Nothing is captured, so, like `sceShareCaptureScreenshotExtended`, it stores the invalid request id -1 when the pointer is not null and returns `NOT_SUPPORTED` without reading the extended parameter, whose layout is unknown +- [sceShareCaptureVideoClipExtended, sceShareCaptureScreenshotExtended](../../../core/libs/prx/libSceShare/Export.cpp) - the 40-byte video and 24-byte screenshot parameters and request-ID write precedence are recovered. Video values at offsets 8 and 16 and screenshot mode semantics remain incomplete. Valid requests still return NOT_SUPPORTED without capture; screenshot mode 2 platform checks, throttling, initialization and the backend text limit are not modeled. From 340429d151ff9294e8b7fc5aba52b7263aff0fef Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:24:46 +0300 Subject: [PATCH 476/523] fix(videorecording): restore parameter layouts and handle widths --- .../libs/prx/libSceVideoRecordingP/Export.cpp | 53 ++++++++++++++++--- docs/dev/debt/libSceVideoRecordingP.md | 15 +++--- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceVideoRecordingP/Export.cpp b/core/libs/prx/libSceVideoRecordingP/Export.cpp index a5cdee3d3a..93cf413444 100644 --- a/core/libs/prx/libSceVideoRecordingP/Export.cpp +++ b/core/libs/prx/libSceVideoRecordingP/Export.cpp @@ -9,6 +9,43 @@ #include #include +struct VideoRecordingContext; + +struct VideoRecordingSupportInfo { + std::uint8_t data[18]; +}; + +static_assert(sizeof(VideoRecordingSupportInfo) == 18); + +struct VideoRecordingParam { + std::uint64_t size; + std::int32_t threadPriority; + std::uint32_t duration; + std::uint32_t mode; + std::int32_t outputMode; + std::uint32_t option; + std::uint32_t format; +}; + +struct VideoRecordingParam2 { + std::uint64_t size; + std::uint64_t affinityMask; + std::int32_t threadPriority; + std::uint32_t duration; + std::uint32_t mode; + std::int32_t outputMode; + std::uint32_t option; + std::uint32_t format; +}; + +static_assert(sizeof(VideoRecordingParam) == 32); +static_assert(offsetof(VideoRecordingParam, duration) == 12); +static_assert(offsetof(VideoRecordingParam, format) == 28); +static_assert(sizeof(VideoRecordingParam2) == 40); +static_assert(offsetof(VideoRecordingParam2, threadPriority) == 16); +static_assert(offsetof(VideoRecordingParam2, duration) == 20); +static_assert(offsetof(VideoRecordingParam2, format) == 36); + namespace { std::mutex g_infoMutex; std::map> g_info; @@ -28,7 +65,7 @@ int APS5_VABI sceVideoRecordingClose(int discard) { return 0; } -int APS5_VABI sceVideoRecordingCloseByHandle(int handle, int discard) { +int APS5_VABI sceVideoRecordingCloseByHandle(VideoRecordingContext* handle, int discard) { (void)handle; (void)discard; NotImplemented_nid_no_patch(__func__); @@ -54,7 +91,7 @@ int APS5_VABI sceVideoRecordingGetInfo(int32_t info, void* data, size_t size) { return 0; } -int APS5_VABI sceVideoRecordingGetInfoByHandle(int handle, int32_t info, void* data, size_t size) { +int APS5_VABI sceVideoRecordingGetInfoByHandle(VideoRecordingContext* handle, int32_t info, void* data, size_t size) { (void)handle; (void)info; (void)data; @@ -67,7 +104,7 @@ int APS5_VABI sceVideoRecordingGetStatus(void) { return SceVideoRecordingStatusNone; } -int APS5_VABI sceVideoRecordingGetSupportInfo(void* info) { +int APS5_VABI sceVideoRecordingGetSupportInfo(VideoRecordingSupportInfo* info) { (void)info; NotImplemented_nid_no_patch(__func__); return 0; @@ -89,7 +126,7 @@ int APS5_VABI sceVideoRecordingInitialize(void* param) { return 0; } -int APS5_VABI sceVideoRecordingOpen(const char* path, const void* param, void* heap, int heapSize) { +int APS5_VABI sceVideoRecordingOpen(const char* path, const VideoRecordingParam* param, void* heap, int heapSize) { (void)path; (void)param; (void)heap; @@ -98,7 +135,7 @@ int APS5_VABI sceVideoRecordingOpen(const char* path, const void* param, void* h return 0; } -int APS5_VABI sceVideoRecordingOpen2(const char* path, const void* param, void* heap, int heapSize) { +int APS5_VABI sceVideoRecordingOpen2(const char* path, const VideoRecordingParam2* param, void* heap, int heapSize) { (void)path; (void)param; (void)heap; @@ -107,19 +144,19 @@ int APS5_VABI sceVideoRecordingOpen2(const char* path, const void* param, void* return 0; } -int APS5_VABI sceVideoRecordingQueryHddSize2(const void* param) { +std::int64_t APS5_VABI sceVideoRecordingQueryHddSize2(const VideoRecordingParam2* param) { (void)param; NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVideoRecordingQueryMemSize(const void* param) { +int APS5_VABI sceVideoRecordingQueryMemSize(const VideoRecordingParam* param) { (void)param; NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI sceVideoRecordingQueryMemSize2(const void* param) { +int APS5_VABI sceVideoRecordingQueryMemSize2(const VideoRecordingParam2* param) { (void)param; NotImplemented_nid_no_patch(__func__); return 0; diff --git a/docs/dev/debt/libSceVideoRecordingP.md b/docs/dev/debt/libSceVideoRecordingP.md index 50c98e9fc0..ec40ae9fd8 100644 --- a/docs/dev/debt/libSceVideoRecordingP.md +++ b/docs/dev/debt/libSceVideoRecordingP.md @@ -3,17 +3,14 @@ ### Unknown function info - [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - declared with `void(void* param)` signature following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L67-L69); unimplemented and throws `NotImplemented_nid_no_patch` -- [sceVideoRecordingCloseByHandle, sceVideoRecordingGetInfoByHandle](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - inferred handle-based recording control signatures taking an integer recording handle; unimplemented and throw `NotImplemented_nid_no_patch` -- [sceVideoRecordingCopyBGRAtoNV12](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - inferred pixel buffer conversion signature `(void* dst, const void* src, uint32_t width, uint32_t height)`; unimplemented and throws `NotImplemented_nid_no_patch` -- [sceVideoRecordingGetSupportInfo, sceVideoRecordingGetVideoOutMode](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - query signatures taking an output pointer; exact structure layouts and return values are unverified and throw `NotImplemented_nid_no_patch` +- [sceVideoRecordingCloseByHandle, sceVideoRecordingGetInfoByHandle](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - native handles are context pointers. Recording state and per-handle queries remain unimplemented. +- [sceVideoRecordingCopyBGRAtoNV12](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the destination, source, 32-bit width and height arguments are confirmed. Pixel conversion remains unimplemented. +- [sceVideoRecordingGetSupportInfo, sceVideoRecordingGetVideoOutMode](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - GetSupportInfo writes an 18-byte payload whose fields remain unresolved. GetVideoOutMode is absent from the supplied recording modules, so its signature remains unverified. Both exports remain unimplemented. - [sceVideoRecordingInit, sceVideoRecordingTerm](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - parameterless initialization and termination stubs; unimplemented and throw `NotImplemented_nid_no_patch` - [sceVideoRecordingInitialize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - initialization stub taking an optional parameter structure pointer; unverified and throws `NotImplemented_nid_no_patch` -- [sceVideoRecordingOpen2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - extended recording open signature; parameters inferred from `sceVideoRecordingOpen` and [fpPS4](https://github.com/red-prig/fpPS4); unimplemented and throws `NotImplemented_nid_no_patch` -- [sceVideoRecordingQueryHddSize2, sceVideoRecordingQueryMemSize2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - memory and disk size query signatures taking parameter pointer; unverified and throw `NotImplemented_nid_no_patch` -- [sceVideoRecordingQueryMemSize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - signature `int(const SceVideoRecordingParam*)` from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L75-L77), which says only that it reports the heap bytes needed for the settings in the parameter; how the size follows from `ringSec` and the format is not documented. [`MaxMemSize` (4096)](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L64-L65) is the largest heap the recorder needs, an upper bound and not the size for given settings, so returning it would be invented. Throws `NotImplemented_nid_no_patch` -- [sceVideoRecordingOpen, sceVideoRecordingStart, sceVideoRecordingStop, sceVideoRecordingClose](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - signatures from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L79-L101). The recorder is not modelled, so these throw `NotImplemented_nid_no_patch` instead of pretending to record. SharpProspero documents no error code for an unavailable recorder; [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/15abf68c9b52c4b04d094f2837719fdb871021f3/src/core/libraries/video_recording/video_recording_error.h#L9-L18) lists codes such as `0x80A80008` (UNSUPPORTED), but nothing shows the console returns one here -- [sceVideoRecordingRestModeProcessGetStatus, sceVideoRecordingRestModeProcessStart](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - rest mode recording interfaces; unverified and throw `NotImplemented_nid_no_patch` -- [sceVideoRecordingSendCommand](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - command dispatch signature taking integer command ID and parameter pointer; unverified and throws `NotImplemented_nid_no_patch` +- [sceVideoRecordingOpen, sceVideoRecordingOpen2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the 32-byte and 40-byte parameter variants, four argument registers and 32-bit heap size are recovered. Mode, output mode, option and format semantics remain incomplete. QueryHddSize2 returns a signed 64-bit size or error; recording and sizing exports remain unimplemented. + +- [Handle and support queries](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - native recording handles are context pointers, and GetSupportInfo writes 18 bytes. Support payload field semantics remain incomplete. RestModeProcessStart, RestModeProcessGetStatus and SendCommand consume no caller arguments in the supplied native module; their existing unused parameter slots remain because the protected bodies reference those parameter names. ### Functional From f414adb3d6501f7ac696985d7c68427e93caf526 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:24:47 +0300 Subject: [PATCH 477/523] docs(kernel): close thread sanitizer signature debt --- docs/dev/debt/libkernel.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 77f9284967..6a4136fc3c 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -8,7 +8,7 @@ - [sceKernelGetMainSocId and sceKernelGetCpumode](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) both return 0, and [sceKernelGetCpuFrequency](../../../core/libs/prx/libkernel/Time/Time.cpp) returns the host TSC frequency measured for `sceKernelGetTscFrequency`, not the console CPU clock. The PS5's SoC id and CPU mode are unknown; shadPS4's PS4 values are [0x710f10/0x740f30 and 0, 2 or 5 from the param.sfo CPU-mode flags](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1a/src/core/libraries/kernel/process.cpp). PPSA99203 only logs them (`[console] main SoC id: result=%#x out=%#x`, `CPU: %llu Hz, cpumode %d`), passing a pointer that `sceKernelGetMainSocId` leaves untouched - [sceKernelEnableDmemAliasing](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) returns 0 and changes nothing: direct memory can always be mapped more than once here, as `DoMapDirect` in [DirectMemory.cpp](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) only checks that the physical pages are allocated, so there is no aliasing to turn on. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/memory.cpp#L39-L43) (PS4) sets a flag there that lifts the `EBUSY` its [`sceKernelMapDirectMemory2`](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/memory.cpp#L299-L301), and [`sceKernelMapDirectMemory` for SDK 2.50 and later](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/memory.cpp#L237-L242), return for direct memory that is [already mapped](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/memory.cpp#L548-L553); that check is not applied here. The no-argument `int` signature follows shadPS4 and ProsperoEden (PPSA99008), which calls it before mapping the same direct memory again. Whether the PS5 refuses such a mapping without the call is unverified - [sceKernelInternalMemoryGetAvailableSize](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) reports 0 bytes and returns 0: what the console's internal memory pool holds and how large it is are unknown. The `int (size_t*)` signature is from [xUtilty](https://github.com/Backporter/xUtilty/blob/328a856af1ef/source/SystemWrapper.cpp#L85) (PS4). PPSA99203 calls it once beside `sceKernelAvailableFlexibleMemorySize` while starting its GS renderer and never reads the result ([GS.cpp](https://github.com/Swordpdf/PS5SX2/blob/b4cf55e87c34/pcsx2/GS/GS.cpp#L233-L247)) -- [sceKernelIsThreadSanitizerEnabled](../../../core/libs/prx/libkernel/System/src/Sanitizer.cpp) (libkernel, NID `LzoM-wVLJDE`) returns 0, as `sceKernelIsAddressSanitizerEnabled` does: a converted title never runs under the thread sanitizer, so a title that checks it skips the [libSceDbgThreadSanitizer](../../../core/libs/prx/libSceDbgThreadSanitizer/Export.cpp) annotation calls, which throw. PPSA23226's bundled `libSceJobManager_nosubmission.prx` makes those calls only when it reports nonzero +- [sceKernelIsThreadSanitizerEnabled](../../../core/libs/prx/libkernel/System/src/Sanitizer.cpp) - the native zero-argument query returns bit 3 of the process sanitizer flags loaded from sysctl `{1, 14, 41}`. The host always reports disabled because guest sanitizer activation is not modeled. ### Unknown function info @@ -109,7 +109,6 @@ - [uDGXwUKqi8s](../../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - unknown name, signature - [sceKernelGetCpuTemperature, sceKernelGetSocSensorTemperature](../../../core/libs/prx/libkernel/System/src/ThermalSensors.cpp) (libkernel) - read the host's thermal sensors: on Linux from `/sys/class/hwmon` and `/sys/class/thermal`, on Windows from the ACPI thermal zones (`MSAcpi_ThermalZoneTemperature` over WMI) and the storage temperature property of the first physical drive. Sensor 0 is the CPU/package value, 1 the first other value and 2 an NVMe value; a host that exposes none reports every sensor as unsupported (`EINVAL`). The console's SoC channel count and meanings are hardware-dependent and differ from the host's, the host sensors are not the console's, the ACPI reading is unavailable on many machines, and the storage temperature property is numbered 52 on Windows 10 2004 and later and 22 before it, so the mapping, the values and the unsupported-input error are unverified; the storage channel was checked against the drive's own SMART composite temperature on one NVMe host (`StorageDeviceTemperatureProperty`, 36 °C on a WD_BLACK SN7100). `sceKernelGetCpuTemperature` reports sensor 0. PPSA99058 and PPSA99420 call both - [LinuxProtFromSce](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - protection bits 0x100 and 0x200 are accepted and assumed not to affect host CPU access; host protection is derived only from the read, write and execute bits -- [sceKernelIsThreadSanitizerEnabled](../../../core/libs/prx/libkernel/System/src/Sanitizer.cpp) (libkernel, NID `LzoM-wVLJDE`) - no source gives the signature; `int (void)` is assumed from `sceKernelIsAddressSanitizerEnabled` and from PPSA23226's bundled `libSceJobManager_nosubmission.prx`, which passes no arguments and tests only the 32-bit result - [getdtablesize](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - returns the smaller of the `RLIMIT_NOFILE` soft limit of `getrlimit` and the host's `getdtablesize()`, capped at `INT_MAX`: FreeBSD returns the smaller of the `RLIMIT_NOFILE` soft limit and `maxfilesperproc` ([`sys_getdtablesize`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L338-L355)), and the host call supplies that bound on macOS, where the soft limit alone is not one (`setrlimit` accepts soft limits above `maxfilesperproc`, `RLIM_INFINITY` included); `maxfilesperproc` is not measured on the console. The guest limit is the host's soft limit, raised to the hard limit when libkernel loads only on Linux: `core/libs/prx/libkernel/File/src/OpenFileLimit.cpp` is `__linux__`-only, so on macOS the guest gets whatever soft limit the host process inherited. VLC (PPSA85300) imports it for a `closefrom` loop in the child of its spawn helper ([libc_stubs.c](https://github.com/ZiZc3/VLC/blob/d46be6a68f4244466b041c2dc1b640915ac1a983/ps5/compat/libc_stubs.c#L66-L72)), which never runs because its `fork` fails with `ENOSYS` - [close, sceKernelClose](../../../core/libs/prx/libkernel/File/src/HostStreams.cpp) (libkernel) - descriptors 0 to 2 that are open when libkernel loads belong to the host process: closing one fails with `EBADF` and leaves it open. The console's result is unknown. PPSA21703 calls `sceKernelClose(0)` repeatedly; closing the host stream let the host C runtime give descriptor 0 to the next file opened, which the next call then closed - [sceKernelPrintBacktraceWithModuleInfo](../../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - imported by PS4 titles, which do not load without it, and added without an implementation: declared without parameters, every call throws From d109d51f43196806b932d8e71308d8ce8171d8a1 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:27:03 +0300 Subject: [PATCH 478/523] fix(opus): restore decoder validation errors and precedence --- core/libs/prx/libSceOpusDec/Export.cpp | 4 +-- .../prx/libSceOpusDec/include/OpusDecoder.hpp | 29 ++++++++++++------- core/libs/tests/GuestOpusCeltDec.cpp | 25 +++++++++++++--- core/libs/tests/GuestOpusDec.cpp | 29 +++++++++++++++---- docs/dev/debt/libSceOpusCeltDec.md | 2 +- docs/dev/debt/libSceOpusDec.md | 2 +- 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceOpusDec/Export.cpp b/core/libs/prx/libSceOpusDec/Export.cpp index a02f1d05be..ebf78206f4 100644 --- a/core/libs/prx/libSceOpusDec/Export.cpp +++ b/core/libs/prx/libSceOpusDec/Export.cpp @@ -13,11 +13,11 @@ extern "C" { int APS5_VABI sceOpusDecInitialize(std::uint32_t* context) { return library.Initialize(context); } -int APS5_VABI sceOpusDecTerminate(std::uint32_t* context) { return library.Terminate(context); } +int APS5_VABI sceOpusDecTerminate(const std::uint32_t* context) { return library.Terminate(context); } int APS5_VABI sceOpusDecGetSize(int channels) { return library.GetSize(channels); } -int APS5_VABI sceOpusDecCreateEx(std::uint32_t* context, void* state, int sampleRate, int channels) { +int APS5_VABI sceOpusDecCreateEx(const std::uint32_t* context, void* state, int sampleRate, int channels) { return library.CreateEx(context, state, sampleRate, channels); } diff --git a/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp b/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp index 818381ea7c..fee4c48957 100644 --- a/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp +++ b/core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp @@ -27,7 +27,7 @@ public: OpusDecoderLibrary(const char* libraryName, bool celtPacketsOnly) : name(libraryName), celtOnly(celtPacketsOnly) {} int Initialize(std::uint32_t* context) { - if (!context) fail("null context output"); + if (!context) return error(13); std::lock_guard lock(mutex); for (const auto& [id, owner] : contexts) if (owner == context) fail("context already initialized"); @@ -49,15 +49,15 @@ public: } int GetSize(int channels) { - if (channels != 1 && channels != 2) fail("only mono and stereo are supported"); + if (channels != 1 && channels != 2) return error(3); return 640; } int CreateEx(const std::uint32_t* context, void* state, int sampleRate, int channels) { - if (!context || !state) fail("null context or state"); - GetSize(channels); - if (sampleRate != 8000 && sampleRate != 12000 && sampleRate != 16000 && sampleRate != 24000 && sampleRate != 48000) - fail("sample rates other than 8000, 12000, 16000, 24000 and 48000 Hz are not supported"); + if (!context || !state) return error(0); + if (celtOnly && channels != 1 && channels != 2) return error(3); + if (sampleRate != 8000 && sampleRate != 12000 && sampleRate != 16000 && sampleRate != 24000 && sampleRate != 48000) return error(2); + if (channels != 1 && channels != 2) return error(3); std::lock_guard lock(mutex); if (!contexts.contains(*context)) fail("unknown context"); if (decoders.contains(state)) fail("state already owns a decoder"); @@ -84,11 +84,15 @@ public: } int Decode(void* state, const std::uint8_t* data, int bytes, std::int16_t* pcm, int capacity) { - if (!data || bytes <= 0 || bytes > std::numeric_limits::max() - AV_INPUT_BUFFER_PADDING_SIZE || !pcm || capacity < 0) - fail("invalid packet or PCM buffer; packet loss concealment is not implemented"); + if (!state) return error(0); + if (bytes != 0 && !data) return error(6); + if (bytes < 0) return error(7); + if (!pcm) return error(8); + if (capacity < 0) return error(9); std::lock_guard lock(mutex); const auto found = decoders.find(state); - if (found == decoders.end()) fail("unknown decoder state"); + if (found == decoders.end()) return error(1); + if (bytes == 0 || bytes > std::numeric_limits::max() - AV_INPUT_BUFFER_PADDING_SIZE) fail("packet loss concealment or oversized packet is not implemented"); auto& decoder = *found->second; if (celtOnly && (data[0] >> 3) < 16) fail("SILK and hybrid packets are not implemented"); const int samples = packetSamples(data, bytes) / (48000 / decoder.sampleRate); @@ -135,8 +139,9 @@ public: } int Destroy(void* state) { + if (!state) return error(0); std::lock_guard lock(mutex); - if (decoders.erase(state) == 0) fail("unknown decoder state"); + if (decoders.erase(state) == 0) return error(1); return 0; } @@ -161,6 +166,10 @@ private: std::unordered_map> decoders; std::uint32_t nextContext = 1; + int error(std::uint32_t offset) const { + return static_cast((celtOnly ? 0x81400000u : 0x81740000u) + offset); + } + [[noreturn]] void fail(const char* message) const { throw std::runtime_error(std::string(name) + ": " + message); } diff --git a/core/libs/tests/GuestOpusCeltDec.cpp b/core/libs/tests/GuestOpusCeltDec.cpp index d6422e910b..3245cc824e 100644 --- a/core/libs/tests/GuestOpusCeltDec.cpp +++ b/core/libs/tests/GuestOpusCeltDec.cpp @@ -75,11 +75,28 @@ void RequireTone(const std::int16_t* pcm, int samples, int channels, int rate) { } // namespace int main() { + constexpr std::uint32_t errorBase = 0x81400000u; + Require(sceOpusCeltDecInitialize(nullptr) == static_cast(errorBase + 13)); + Require(sceOpusCeltDecDestroy(nullptr) == static_cast(errorBase)); std::uint32_t context = 0; Require(sceOpusCeltDecInitialize(&context) == 0); + std::array invalidState{}; + std::int16_t output = 12345; + const std::uint8_t packet = 0; + Require(sceOpusCeltDecCreateEx(nullptr, invalidState.data(), 0, 0) == static_cast(errorBase)); + Require(sceOpusCeltDecCreateEx(&context, invalidState.data(), 0, 0) == static_cast(errorBase + 3)); + Require(sceOpusCeltDecCreateEx(&context, invalidState.data(), 48000, 0) == static_cast(errorBase + 3)); + Require(sceOpusCeltDecDecode(nullptr, nullptr, -1, nullptr, -1) == static_cast(errorBase)); + Require(sceOpusCeltDecDecode(invalidState.data(), nullptr, -1, nullptr, -1) == static_cast(errorBase + 6)); + Require(sceOpusCeltDecDecode(invalidState.data(), &packet, -1, nullptr, -1) == static_cast(errorBase + 7)); + Require(sceOpusCeltDecDecode(invalidState.data(), &packet, 1, nullptr, -1) == static_cast(errorBase + 8)); + Require(sceOpusCeltDecDecode(invalidState.data(), &packet, 1, &output, -1) == static_cast(errorBase + 9)); + Require(sceOpusCeltDecDecode(invalidState.data(), &packet, 1, &output, 2) == static_cast(errorBase + 1)); + Require(output == 12345); + RequireThrows([&] { sceOpusCeltDecInitialize(&context); }); Require(sceOpusCeltDecGetSize(1) == 640 && sceOpusCeltDecGetSize(2) == 640); - RequireThrows([&] { sceOpusCeltDecGetSize(3); }); + Require(sceOpusCeltDecGetSize(3) == static_cast(errorBase + 3)); std::array state{}, monoState{}; Require(sceOpusCeltDecCreateEx(&context, state.data(), 48000, 2) == 0); Require(sceOpusCeltDecCreateEx(&context, monoState.data(), 48000, 1) == 0); @@ -109,7 +126,7 @@ int main() { std::array unsupported{}; for (int rate : {0, 22050, 44100, 96000}) - RequireThrows([&] { sceOpusCeltDecCreateEx(&context, unsupported.data(), rate, 2); }); + Require(sceOpusCeltDecCreateEx(&context, unsupported.data(), rate, 2) == static_cast(errorBase + 2)); for (int rate : {8000, 12000, 16000, 24000, 48000}) { std::array toneState{}; Require(sceOpusCeltDecCreateEx(&context, toneState.data(), rate, 2) == 0); @@ -126,8 +143,8 @@ int main() { } Require(sceOpusCeltDecDestroy(state.data()) == 0); - RequireThrows([&] { sceOpusCeltDecDestroy(state.data()); }); - RequireThrows([&] { sceOpusCeltDecDecode(state.data(), CELT_STEREO + 2, CELT_STEREO[0], pcm.data(), 3840); }); + Require(sceOpusCeltDecDestroy(state.data()) == static_cast(errorBase + 1)); + Require(sceOpusCeltDecDecode(state.data(), CELT_STEREO + 2, CELT_STEREO[0], pcm.data(), 3840) == static_cast(errorBase + 1)); Require(sceOpusCeltDecDestroy(monoState.data()) == 0); Require(sceOpusCeltDecTerminate(&context) == 0); RequireThrows([&] { sceOpusCeltDecTerminate(&context); }); diff --git a/core/libs/tests/GuestOpusDec.cpp b/core/libs/tests/GuestOpusDec.cpp index a5824ee855..e19fb78141 100644 --- a/core/libs/tests/GuestOpusDec.cpp +++ b/core/libs/tests/GuestOpusDec.cpp @@ -8,9 +8,9 @@ extern "C" { int APS5_VABI sceOpusDecInitialize(std::uint32_t*); -int APS5_VABI sceOpusDecTerminate(std::uint32_t*); +int APS5_VABI sceOpusDecTerminate(const std::uint32_t*); int APS5_VABI sceOpusDecGetSize(int); -int APS5_VABI sceOpusDecCreateEx(std::uint32_t*, void*, int, int); +int APS5_VABI sceOpusDecCreateEx(const std::uint32_t*, void*, int, int); int APS5_VABI sceOpusDecDecode(void*, const std::uint8_t*, int, std::int16_t*, int); int APS5_VABI sceOpusDecDestroy(void*); } @@ -49,8 +49,25 @@ const std::uint8_t OPUS_NARROWBAND[] = { } // namespace int main() { + constexpr std::uint32_t errorBase = 0x81740000u; + Require(sceOpusDecInitialize(nullptr) == static_cast(errorBase + 13)); + Require(sceOpusDecDestroy(nullptr) == static_cast(errorBase)); std::uint32_t context = 0, otherContext = 0; Require(sceOpusDecInitialize(&context) == 0); + std::array invalidState{}; + std::int16_t output = 12345; + const std::uint8_t packet = 0; + Require(sceOpusDecCreateEx(nullptr, invalidState.data(), 0, 0) == static_cast(errorBase)); + Require(sceOpusDecCreateEx(&context, invalidState.data(), 0, 0) == static_cast(errorBase + 2)); + Require(sceOpusDecCreateEx(&context, invalidState.data(), 48000, 0) == static_cast(errorBase + 3)); + Require(sceOpusDecDecode(nullptr, nullptr, -1, nullptr, -1) == static_cast(errorBase)); + Require(sceOpusDecDecode(invalidState.data(), nullptr, -1, nullptr, -1) == static_cast(errorBase + 6)); + Require(sceOpusDecDecode(invalidState.data(), &packet, -1, nullptr, -1) == static_cast(errorBase + 7)); + Require(sceOpusDecDecode(invalidState.data(), &packet, 1, nullptr, -1) == static_cast(errorBase + 8)); + Require(sceOpusDecDecode(invalidState.data(), &packet, 1, &output, -1) == static_cast(errorBase + 9)); + Require(sceOpusDecDecode(invalidState.data(), &packet, 1, &output, 2) == static_cast(errorBase + 1)); + Require(output == 12345); + Require(sceOpusDecInitialize(&otherContext) == 0 && context != otherContext); RequireThrows([&] { sceOpusDecInitialize(&context); }); Require(sceOpusDecGetSize(1) == 640 && sceOpusDecGetSize(2) == 640); @@ -60,10 +77,10 @@ int main() { Require(sceOpusDecCreateEx(&context, monoState.data(), 48000, 1) == 0); RequireThrows([&] { sceOpusDecTerminate(&context); }); RequireThrows([&] { sceOpusDecCreateEx(&context, state.data(), 48000, 2); }); - RequireThrows([&] { sceOpusDecGetSize(3); }); + Require(sceOpusDecGetSize(3) == static_cast(errorBase + 3)); std::array unsupported{}; for (int rate : {0, 22050, 44100, 96000}) - RequireThrows([&] { sceOpusDecCreateEx(&context, unsupported.data(), rate, 2); }); + Require(sceOpusDecCreateEx(&context, unsupported.data(), rate, 2) == static_cast(errorBase + 2)); std::array pcm{}, reference{}; pcm.fill(12345); RequireThrows([&] { sceOpusDecDecode(state.data(), OPUS_STEREO + 2, 70, pcm.data() + 1, 3839); }); @@ -119,8 +136,8 @@ int main() { } Require(sceOpusDecDestroy(state.data()) == 0); - RequireThrows([&] { sceOpusDecDestroy(state.data()); }); - RequireThrows([&] { sceOpusDecDecode(state.data(), OPUS_STEREO + 2, 70, pcm.data(), 3840); }); + Require(sceOpusDecDestroy(state.data()) == static_cast(errorBase + 1)); + Require(sceOpusDecDecode(state.data(), OPUS_STEREO + 2, 70, pcm.data(), 3840) == static_cast(errorBase + 1)); Require(sceOpusDecCreateEx(&context, state.data(), 48000, 2) == 0); Require(sceOpusDecDecode(state.data(), OPUS_STEREO + 2, 70, pcm.data(), 3840) == 3840); std::array rejected; diff --git a/docs/dev/debt/libSceOpusCeltDec.md b/docs/dev/debt/libSceOpusCeltDec.md index 0c94aeb226..2b3827fa86 100644 --- a/docs/dev/debt/libSceOpusCeltDec.md +++ b/docs/dev/debt/libSceOpusCeltDec.md @@ -7,5 +7,5 @@ ### Functional -- [libSceOpusCeltDec](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp): the supplied module confirms the six exported argument lists, read-only context inputs, 640-byte state allocation, mono/stereo channels, five sample rates, byte-sized decode capacity and byte-count return. The implementation still keeps decoder state outside guest memory and uses host contexts instead of AJM contexts. Original error codes, AJM resource ownership and termination with live decoders are not implemented. +- [libSceOpusCeltDec](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp): the supplied module confirms the six exported argument lists, read-only context inputs, 640-byte state allocation, mono/stereo channels, five sample rates, byte-sized decode capacity and byte-count return. The implementation still keeps decoder state outside guest memory and uses host contexts instead of AJM contexts. AJM error propagation, resource ownership and termination with live decoders are not implemented. Wrapper validation errors and their precedence are recovered. - [sceOpusCeltDecDecode](../../../core/libs/prx/libSceOpusCeltDec/Export.cpp): the original forwards packets to AJM codec 16 without a local CELT-only check. The backend response to SILK/hybrid packets is unresolved; these inputs still throw. Packet loss concealment, asynchronous AJM execution, exact PCM conversion and resampler buffering remain outside the verified behavior of the FFmpeg implementation in [OpusDecoderLibrary](../../../core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp). diff --git a/docs/dev/debt/libSceOpusDec.md b/docs/dev/debt/libSceOpusDec.md index fa6aa1dfd1..6dbf108315 100644 --- a/docs/dev/debt/libSceOpusDec.md +++ b/docs/dev/debt/libSceOpusDec.md @@ -6,6 +6,6 @@ ### Functional -- [libSceOpusDec](../../../core/libs/prx/libSceOpusDec/Export.cpp) uses FFmpeg for mono/stereo signed-16 decoding at the creation rates the research lists: 8000, 12000, 16000, 24000 and 48000 Hz. The six signatures, 640-byte opaque state allocation and byte-count return follow [public PS5 decoder research](https://github.com/blackbearreloaded/ps5-hardware-audio-decoding/blob/26be5a12f92b2cdc67f8a9e082fd69ae0e9a35a2/docs/DECODING.md#direct-opus-abi) and [ProsperoRadio](https://github.com/blackbearreloaded/ProsperoRadio/blob/2c4029c3a8f43e9e1dfef98374d94d99b14d745d/src/opus_decoder.cpp). Console PCM equivalence and error codes are unverified. Other sample rates, packet loss concealment, invalid inputs and termination with live decoders throw. Context identifiers and host decoder state are private registries; the firmware's opaque state layout and error recovery are unknown. +- [libSceOpusDec](../../../core/libs/prx/libSceOpusDec/Export.cpp) uses FFmpeg for mono/stereo signed-16 decoding at the creation rates the research lists: 8000, 12000, 16000, 24000 and 48000 Hz. The supplied module confirms the exported arguments, read-only context inputs, 640-byte state size and creation rates. Validation errors and their precedence are recovered. Console PCM equivalence, AJM failures, context ownership and termination with live decoders remain unimplemented. Packet loss concealment still throws; decoder state is kept in host registries instead of the native guest state layout. - [sceOpusDecDecode](../../../core/libs/prx/libSceOpusDec/Export.cpp) (libSceOpusDec) returns exactly the packet's duration, as libopus does. FFmpeg resamples narrowband SILK from 8 kHz and holds back 24 samples of the first narrowband packet after the decoder starts or after a packet of another bandwidth, releasing them with the next packet that is not narrowband. The decoder keeps FFmpeg's output in order and pads the start with silence, so such a packet begins with 24 samples (0.5 ms) of silence and the output after it is 24 samples late; libopus has no such gap - [sceOpusDecDecode](../../../core/libs/prx/libSceOpusDec/include/OpusDecoder.hpp) (libSceOpusDec, libSceOpusCeltDec) decodes at 48000 Hz and converts to a lower creation rate with FFmpeg's resampler, where libopus decodes at that rate directly. The resampler's filter delay is padded with silence at the start of the stream like the narrowband delay above, so the output is late by that delay and is not sample-identical to libopus; which of the two the console matches is not measured From ff128a51a10f250f2f83a3a73db2e1f0cc6ea880 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:29:33 +0300 Subject: [PATCH 479/523] fix(logindialog): restore validation and result layout --- .../prx/libSceCommonDialog/CommonDialog.hpp | 19 +++++ core/libs/prx/libSceLoginDialog/Export.cpp | 58 ++++++------- core/libs/tests/GuestLoginDialog.cpp | 82 +++++++++---------- docs/dev/debt/libSceLoginDialog.md | 2 +- 4 files changed, 84 insertions(+), 77 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index 3138618de7..0a7890e949 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -60,6 +60,25 @@ static_assert(sizeof(UsbStorageDialogResult) == 80); static_assert(offsetof(UsbStorageDialogResult, result) == 8); static_assert(offsetof(UsbStorageDialogResult, status) == 12); +struct LoginDialogParam { + std::uint32_t size; + std::uint32_t mode; + std::int32_t userIds[9]; + std::uint32_t reserved[5]; +}; + +struct LoginDialogResult { + std::int32_t result; + std::int32_t errorCode; + std::uint8_t data[8]; +}; + +static_assert(sizeof(LoginDialogParam) == 64); +static_assert(offsetof(LoginDialogParam, userIds) == 8); +static_assert(offsetof(LoginDialogParam, reserved) == 44); +static_assert(sizeof(LoginDialogResult) == 16); +static_assert(offsetof(LoginDialogResult, errorCode) == 4); + struct G2PDialogParam { CommonDialogBaseParam base; std::int32_t userId; diff --git a/core/libs/prx/libSceLoginDialog/Export.cpp b/core/libs/prx/libSceLoginDialog/Export.cpp index 7ca5a3626c..8fcd0f4daf 100644 --- a/core/libs/prx/libSceLoginDialog/Export.cpp +++ b/core/libs/prx/libSceLoginDialog/Export.cpp @@ -2,8 +2,8 @@ #include #include #include -#include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" namespace { @@ -12,29 +12,13 @@ constexpr int COMMON_DIALOG_STATUS_NONE = 0; constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; -constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x81340001u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x81340002u); -constexpr int COMMON_DIALOG_ERROR_PARAM_INVALID = static_cast(0x81340003u); -constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x81340005u); -constexpr int COMMON_DIALOG_ERROR_NO_SERVICE = static_cast(0x81340007u); +constexpr int LOGIN_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x81340001u); +constexpr int LOGIN_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x81340002u); +constexpr int LOGIN_DIALOG_ERROR_BUSY = static_cast(0x81340005u); +constexpr int LOGIN_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x81340003u); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; -constexpr std::uint32_t LOGIN_DIALOG_PARAM_SIZE = 0x40; -constexpr std::uint32_t LOGIN_DIALOG_SELECTOR_LIMIT = 2; -constexpr std::size_t LOGIN_DIALOG_FIRST_RESERVED_WORD = 0x2C / 4; -bool ValidOpenParam(const void* param) { - if (param == nullptr) return false; - std::uint32_t size = 0; - std::memcpy(&size, param, sizeof(size)); - if (size != LOGIN_DIALOG_PARAM_SIZE) return false; - std::uint32_t words[LOGIN_DIALOG_PARAM_SIZE / sizeof(std::uint32_t)]; - std::memcpy(words, param, sizeof(words)); - if (words[1] >= LOGIN_DIALOG_SELECTOR_LIMIT) return false; - for (std::size_t i = LOGIN_DIALOG_FIRST_RESERVED_WORD; i < std::size(words); ++i) { - if (words[i] != 0) return false; - } - return true; -} +constexpr int LOGIN_DIALOG_ERROR_INVALID_USER = static_cast(0x81340007u); std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; @@ -44,15 +28,18 @@ extern "C" { int APS5_VABI sceLoginDialogInitialize(void) { int expected = COMMON_DIALOG_STATUS_NONE; - if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; + if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return LOGIN_DIALOG_ERROR_ALREADY_INITIALIZED; return 0; } -int APS5_VABI sceLoginDialogOpen(const void* param) { - if (!ValidOpenParam(param)) return COMMON_DIALOG_ERROR_PARAM_INVALID; +int APS5_VABI sceLoginDialogOpen(const LoginDialogParam* param) { + if (param == nullptr || param->size != sizeof(*param) || param->mode > 1 || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint32_t value) { return value == 0; })) return LOGIN_DIALOG_ERROR_INVALID_ARGUMENT; + for (const auto userId : param->userIds) { + if (userId != -1 && (static_cast(userId) < 0x10000000u || static_cast(userId) >= 0x40000000u)) return LOGIN_DIALOG_ERROR_INVALID_USER; + } const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; + if (status == COMMON_DIALOG_STATUS_NONE) return LOGIN_DIALOG_ERROR_NOT_INITIALIZED; + if (status == COMMON_DIALOG_STATUS_RUNNING) return LOGIN_DIALOG_ERROR_BUSY; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -65,23 +52,24 @@ int APS5_VABI sceLoginDialogGetStatus(void) { return g_status.load(); } -int APS5_VABI sceLoginDialogGetResult(void* result) { - if (result == nullptr) return COMMON_DIALOG_ERROR_PARAM_INVALID; +int APS5_VABI sceLoginDialogGetResult(LoginDialogResult* result) { + if (result == nullptr) return LOGIN_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - const std::int32_t words[] = {COMMON_DIALOG_RESULT_USER_CANCELED, COMMON_DIALOG_ERROR_NO_SERVICE}; - std::memcpy(result, words, sizeof(words)); - return status == COMMON_DIALOG_STATUS_FINISHED ? 0 : COMMON_DIALOG_ERROR_BUSY; + if (status == COMMON_DIALOG_STATUS_NONE) return LOGIN_DIALOG_ERROR_NOT_INITIALIZED; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + result->errorCode = LOGIN_DIALOG_ERROR_INVALID_USER; + if (status != COMMON_DIALOG_STATUS_FINISHED) return LOGIN_DIALOG_ERROR_BUSY; + return 0; } int APS5_VABI sceLoginDialogClose(void) { - if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return LOGIN_DIALOG_ERROR_NOT_INITIALIZED; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } int APS5_VABI sceLoginDialogTerminate(void) { - if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return LOGIN_DIALOG_ERROR_NOT_INITIALIZED; return 0; } diff --git a/core/libs/tests/GuestLoginDialog.cpp b/core/libs/tests/GuestLoginDialog.cpp index 0289d86fda..09e5d52663 100644 --- a/core/libs/tests/GuestLoginDialog.cpp +++ b/core/libs/tests/GuestLoginDialog.cpp @@ -1,16 +1,14 @@ #include "prx/libc/include/general/VabiMacros.hpp" -#include #include #include -#include -#include +#include "prx/libSceCommonDialog/CommonDialog.hpp" extern "C" { int APS5_VABI sceLoginDialogInitialize(void); -int APS5_VABI sceLoginDialogOpen(const void* param); +int APS5_VABI sceLoginDialogOpen(const LoginDialogParam* param); int APS5_VABI sceLoginDialogUpdateStatus(void); int APS5_VABI sceLoginDialogGetStatus(void); -int APS5_VABI sceLoginDialogGetResult(void* result); +int APS5_VABI sceLoginDialogGetResult(LoginDialogResult* result); int APS5_VABI sceLoginDialogClose(void); int APS5_VABI sceLoginDialogTerminate(void); } @@ -24,35 +22,22 @@ constexpr int kStatusInitialized = 1; constexpr int kStatusFinished = 3; constexpr int kErrNotInitialized = static_cast(0x81340001); constexpr int kErrAlreadyInitialized = static_cast(0x81340002); -constexpr int kErrParamInvalid = static_cast(0x81340003); -constexpr int kErrBusy = static_cast(0x81340005); -constexpr int kErrNoService = static_cast(0x81340007); +constexpr int kErrNotFinished = static_cast(0x81340005); +constexpr int kErrArgNull = static_cast(0x81340003); constexpr int kResultUserCanceled = 1; -void RequireRejected(const std::uint32_t (&valid)[16], std::size_t word, std::uint32_t value) { - std::uint32_t param[16]; - std::memcpy(param, valid, sizeof(param)); - param[word] = value; - Require(sceLoginDialogOpen(param) == kErrParamInvalid); -} - -void RequireResult(const std::int32_t (&result)[4]) { - Require(result[0] == kResultUserCanceled); - Require(result[1] == kErrNoService); - Require(result[2] == -1 && result[3] == -1); -} - } int main() { - const std::uint32_t param[16] = {0x40}; - std::int32_t result[4] = {-1, -1, -1, -1}; + LoginDialogParam param{}; + param.size = sizeof(param); + std::fill(std::begin(param.userIds), std::end(param.userIds), -1); + LoginDialogResult result{-1, -1, {11, 22, 33, 44, 55, 66, 77, 88}}; Require(sceLoginDialogTerminate() == kErrNotInitialized); - Require(sceLoginDialogOpen(param) == kErrNotInitialized); + Require(sceLoginDialogOpen(¶m) == kErrNotInitialized); Require(sceLoginDialogClose() == kErrNotInitialized); - Require(sceLoginDialogGetResult(result) == kErrNotInitialized); - Require(result[0] == -1 && result[1] == -1); + Require(sceLoginDialogGetResult(&result) == kErrNotInitialized); Require(sceLoginDialogGetStatus() == kStatusNone); Require(sceLoginDialogUpdateStatus() == kStatusNone); @@ -61,22 +46,37 @@ int main() { Require(sceLoginDialogGetStatus() == kStatusInitialized); Require(sceLoginDialogUpdateStatus() == kStatusInitialized); - Require(sceLoginDialogOpen(nullptr) == kErrParamInvalid); - for (std::uint32_t size : {0u, 0x3Cu, 0x3Fu, 0x41u}) RequireRejected(param, 0, size); - for (std::uint32_t selector : {2u, 0xFFFFFFFFu}) RequireRejected(param, 1, selector); - for (std::size_t word = 0x2C / 4; word < 16; ++word) RequireRejected(param, word, 1); - Require(sceLoginDialogGetStatus() == kStatusInitialized); - Require(sceLoginDialogGetResult(result) == kErrBusy); - RequireResult(result); - std::memset(result, 0xFF, sizeof(result)); + Require(sceLoginDialogOpen(nullptr) == kErrArgNull); + Require(sceLoginDialogGetResult(&result) == kErrNotFinished); + Require(result.result == 1 && result.errorCode == static_cast(0x81340007u)); - Require(sceLoginDialogOpen(param) == 0); + param.size = 63; + Require(sceLoginDialogOpen(¶m) == kErrArgNull); + param.size = sizeof(param); + param.mode = 2; + Require(sceLoginDialogOpen(¶m) == kErrArgNull); + param.mode = 1; + param.reserved[4] = 1; + Require(sceLoginDialogOpen(¶m) == kErrArgNull); + param.reserved[4] = 0; + for (auto& userId : param.userIds) { + for (const auto invalid : {0, 0x0fffffff, 0x40000000, -2}) { + userId = invalid; + Require(sceLoginDialogOpen(¶m) == static_cast(0x81340007u)); + } + userId = -1; + } + param.userIds[0] = 0x10000000; + param.userIds[8] = 0x3fffffff; + Require(sceLoginDialogOpen(¶m) == 0); Require(sceLoginDialogGetStatus() == kStatusFinished); Require(sceLoginDialogUpdateStatus() == kStatusFinished); - Require(sceLoginDialogGetResult(nullptr) == kErrParamInvalid); - Require(sceLoginDialogGetResult(result) == 0); - RequireResult(result); + Require(sceLoginDialogGetResult(nullptr) == kErrArgNull); + Require(sceLoginDialogGetResult(&result) == 0); + Require(result.result == kResultUserCanceled); + Require(result.errorCode == static_cast(0x81340007u)); + Require(result.data[0] == 11 && result.data[7] == 88); Require(sceLoginDialogClose() == 0); Require(sceLoginDialogTerminate() == 0); @@ -87,8 +87,8 @@ int main() { Require(sceLoginDialogGetStatus() == kStatusInitialized); Require(sceLoginDialogClose() == 0); Require(sceLoginDialogGetStatus() == kStatusFinished); - result[0] = -1; - Require(sceLoginDialogGetResult(result) == 0); - Require(result[0] == kResultUserCanceled); + result.result = -1; + Require(sceLoginDialogGetResult(&result) == 0); + Require(result.result == kResultUserCanceled); Require(sceLoginDialogTerminate() == 0); } diff --git a/docs/dev/debt/libSceLoginDialog.md b/docs/dev/debt/libSceLoginDialog.md index 07a38ff414..c9408a3d58 100644 --- a/docs/dev/debt/libSceLoginDialog.md +++ b/docs/dev/debt/libSceLoginDialog.md @@ -7,4 +7,4 @@ ### Unknown function info -- [sceLoginDialogOpen, sceLoginDialogGetResult](../../../core/libs/prx/libSceLoginDialog/Export.cpp) - the 0x8134xxxx codes, the Open param checks (size 0x40, word 1 below 2, words 0x2C to 0x3C zero) and the `{1, 0x81340007}` result words are the ones [prosper](https://github.com/mattias800/prosper/blob/ed42e499888d5a86792c3e1b2df3aebd8ca1a681/prosper/src/hle/service/hle_service.cpp#L805-L874) reads from the shipped module. The order of the param and state checks follows prosper's handlers and is not stated separately, the meaning of word 1 and of the second result word is unknown, words 2 to 10 are not checked, and Open's busy result is never reached because the dialog never runs. Closes when: the module's check order is read or measured on a PS5 +- [sceLoginDialogOpen, sceLoginDialogGetResult](../../../core/libs/prx/libSceLoginDialog/Export.cpp) - the 64-byte input and 16-byte result layouts are recovered. The roles of the nine user-ID slots and the eight trailing result bytes require service-side analysis. The UI stub returns the native disconnected-service cancellation result and does not populate successful login data. From 94e579a37aceb0613b68a47f26fab2d7978c1f0e Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:30:44 +0300 Subject: [PATCH 480/523] fix(netctlap): restore option and result validation order --- core/libs/prx/libSceNetCtlAp/Export.cpp | 11 ++++++++--- core/libs/tests/GuestNetCtlAp.cpp | 9 +++++++-- docs/dev/debt/libSceNetCtlAp.md | 5 ++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceNetCtlAp/Export.cpp b/core/libs/prx/libSceNetCtlAp/Export.cpp index d0bcb19845..9da3953097 100644 --- a/core/libs/prx/libSceNetCtlAp/Export.cpp +++ b/core/libs/prx/libSceNetCtlAp/Export.cpp @@ -7,6 +7,7 @@ namespace { +constexpr int NET_CTL_AP_ERROR_INVALID_OPT_STR = static_cast(0x80412901); constexpr int NET_CTL_AP_ERROR_NOT_STARTED = static_cast(0x8041298F); constexpr int NET_CTL_AP_ERROR_INVALID_TYPE = static_cast(0x80412990); constexpr int NET_CTL_AP_ERROR_INVALID_ADDR = static_cast(0x80412991); @@ -91,8 +92,8 @@ int APS5_VABI sceNetCtlApUnregisterCallback(int cid) { int APS5_VABI sceNetCtlApGetResult(int eventType, int* errorCode) { std::lock_guard lock(g_lock); if (!g_initialized) return NET_CTL_AP_ERROR_NOT_INITIALIZED; - if (!ValidEventType(eventType)) return NET_CTL_AP_ERROR_INVALID_TYPE; if (!errorCode) return NET_CTL_AP_ERROR_INVALID_ADDR; + if (!ValidEventType(eventType)) return NET_CTL_AP_ERROR_INVALID_TYPE; *errorCode = 0; return 0; } @@ -118,10 +119,14 @@ int APS5_VABI sceNetCtlApStop() { return g_initialized ? NET_CTL_AP_ERROR_NOT_STARTED : NET_CTL_AP_ERROR_NOT_INITIALIZED; } -int APS5_VABI sceNetCtlApGetConnectInfo(std::uint8_t* optStr, void* connectInfo) { - (void)optStr; +int APS5_VABI sceNetCtlApGetConnectInfo(const char* optStr, void* connectInfo) { std::lock_guard lock(g_lock); if (!g_initialized) return NET_CTL_AP_ERROR_NOT_INITIALIZED; + if (optStr != nullptr) { + std::size_t length = 0; + while (length < 9 && optStr[length] != 0) ++length; + if (length > 8) return NET_CTL_AP_ERROR_INVALID_OPT_STR; + } if (!connectInfo) return NET_CTL_AP_ERROR_INVALID_ADDR; if (*static_cast(connectInfo) != NET_CTL_AP_CONNECT_INFO_SIZE) return NET_CTL_AP_ERROR_INVALID_SIZE; return NET_CTL_AP_ERROR_NOT_STARTED; diff --git a/core/libs/tests/GuestNetCtlAp.cpp b/core/libs/tests/GuestNetCtlAp.cpp index fbb8b0f01d..3689260ee4 100644 --- a/core/libs/tests/GuestNetCtlAp.cpp +++ b/core/libs/tests/GuestNetCtlAp.cpp @@ -15,7 +15,7 @@ int APS5_VABI sceNetCtlApGetResult(int eventType, int* errorCode); int APS5_VABI sceNetCtlApGetInfo(void* info); int APS5_VABI sceNetCtlApGetState(int* state); int APS5_VABI sceNetCtlApStop(void); -int APS5_VABI sceNetCtlApGetConnectInfo(std::uint8_t* optStr, void* connectInfo); +int APS5_VABI sceNetCtlApGetConnectInfo(const char* optStr, void* connectInfo); } namespace { @@ -74,9 +74,13 @@ int main() { std::memset(connect, 0x5a, sizeof(connect)); const std::uint64_t connectSize = sizeof(connect); std::memcpy(connect, &connectSize, sizeof(connectSize)); - std::uint8_t option[] = "opt"; + const char option[] = "opt"; Require(sceNetCtlApGetConnectInfo(option, connect) == kErrNotStarted); Require(sceNetCtlApGetConnectInfo(nullptr, connect) == kErrNotStarted); + Require(sceNetCtlApGetConnectInfo("", connect) == kErrNotStarted); + Require(sceNetCtlApGetConnectInfo("12345678", connect) == kErrNotStarted); + Require(sceNetCtlApGetConnectInfo("123456789", nullptr) == static_cast(0x80412901u)); + Require(sceNetCtlApGetConnectInfo("123456789", connect) == static_cast(0x80412901u)); for (std::size_t i = sizeof(connectSize); i < sizeof(connect); ++i) Require(connect[i] == 0x5a); Require(sceNetCtlApGetConnectInfo(option, nullptr) == kErrInvalidAddr); const std::uint64_t wrongConnectSize = sizeof(info); @@ -95,6 +99,7 @@ int main() { int code = 0x5a5a; Require(sceNetCtlApClearEvent(type) == kErrInvalidType); Require(sceNetCtlApGetResult(type, &code) == kErrInvalidType && code == 0x5a5a); + Require(sceNetCtlApGetResult(type, nullptr) == kErrInvalidAddr); } int cids[8]; diff --git a/docs/dev/debt/libSceNetCtlAp.md b/docs/dev/debt/libSceNetCtlAp.md index 012a82c476..94b54af330 100644 --- a/docs/dev/debt/libSceNetCtlAp.md +++ b/docs/dev/debt/libSceNetCtlAp.md @@ -7,6 +7,5 @@ ### Unknown function info - [sceNetCtlApInit, sceNetCtlApTerm](../../../core/libs/prx/libSceNetCtlAp/Export.cpp) (libSceNetCtlAp) - every other call returns `NOT_INITIALIZED` (`0x80412994`) before `sceNetCtlApInit` and after `sceNetCtlApTerm`, which also drops the registered callbacks. A second `sceNetCtlApInit` throws, as no error for it is documented -- [sceNetCtlApGetInfo, sceNetCtlApGetConnectInfo, sceNetCtlApStop](../../../core/libs/prx/libSceNetCtlAp/Export.cpp) (libSceNetCtlAp) - checks run in this order, which is not verified: a null structure returns `INVALID_ADDR` (`0x80412991`), a size field other than 152 or 184 `INVALID_SIZE` (`0x80412998`), then `NOT_STARTED`. The option string is not read, since the accepted strings (`INVALID_OPT_STR`, `0x80412901`) are unknown. Whether `sceNetCtlApStop` of a stopped access point returns `NOT_STARTED` or 0 is unverified -- [sceNetCtlApClearEvent, sceNetCtlApGetResult](../../../core/libs/prx/libSceNetCtlAp/Export.cpp) (libSceNetCtlAp) - event types 1 to 4 are accepted and others return `INVALID_TYPE` (`0x80412990`), checked before the null `errorCode` (`INVALID_ADDR`). With no event ever delivered, `sceNetCtlApGetResult` writes 0; what the console writes before the first event is unverified -- [sceNetCtlApRegisterCallback, sceNetCtlApUnregisterCallback](../../../core/libs/prx/libSceNetCtlAp/Export.cpp) (libSceNetCtlAp) - a null function or id pointer returns `INVALID_ADDR`, a ninth registration `CALLBACK_MAX` (`0x80412995`), an id outside 0 to 7 `INVALID_ID` (`0x80412996`) and an unregistered id `ID_NOT_FOUND` (`0x80412997`). Ids are the lowest free slot, as in libSceNetCtl; the console's id assignment is unverified +- [sceNetCtlApGetInfo, sceNetCtlApGetConnectInfo, sceNetCtlApStop](../../../core/libs/prx/libSceNetCtlAp/Export.cpp) - stopped-state results come from a service on the console and remain unverified. Wrapper validation is recovered: initialization precedes arguments, the optional connection string is at most eight bytes, then output pointers and exact 152/184-byte sizes are checked. The service interpretation of the option string remains unresolved. +- [sceNetCtlApGetResult](../../../core/libs/prx/libSceNetCtlAp/Export.cpp) - with no event delivered, the host writes zero; the service result before the first event remains unknown. Wrapper validation checks initialization, output pointer and event type in that order. Event types 1 through 4 and lowest-free callback IDs 0 through 7 are confirmed. From dd5b81e1dfe0beaa3872e6d0a2cd51a92318fe27 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:32:12 +0300 Subject: [PATCH 481/523] fix(netctlapdialog): restore parameter and lifecycle validation --- .../prx/libSceCommonDialog/CommonDialog.hpp | 19 +++++++++ core/libs/prx/libSceNetCtlApDialog/Export.cpp | 28 ++++++------- core/libs/tests/GuestNetCtlApDialog.cpp | 42 +++++++++++++------ docs/dev/debt/libSceNetCtlApDialog.md | 2 +- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index 0a7890e949..f1949f6eeb 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -79,6 +79,25 @@ static_assert(offsetof(LoginDialogParam, reserved) == 44); static_assert(sizeof(LoginDialogResult) == 16); static_assert(offsetof(LoginDialogResult, errorCode) == 4); +struct NetCtlApDialogParam { + CommonDialogBaseParam base; + std::uint64_t size; + std::uint32_t value56; + std::uint32_t reserved60; + char option[9]; + std::uint8_t reserved[31]; +}; + +struct NetCtlApDialogResult { + std::int32_t result; + std::uint8_t reserved[36]; +}; + +static_assert(sizeof(NetCtlApDialogParam) == 104); +static_assert(offsetof(NetCtlApDialogParam, option) == 64); +static_assert(offsetof(NetCtlApDialogParam, reserved) == 73); +static_assert(sizeof(NetCtlApDialogResult) == 40); + struct G2PDialogParam { CommonDialogBaseParam base; std::int32_t userId; diff --git a/core/libs/prx/libSceNetCtlApDialog/Export.cpp b/core/libs/prx/libSceNetCtlApDialog/Export.cpp index e1d2bc4a27..64a0d3086e 100644 --- a/core/libs/prx/libSceNetCtlApDialog/Export.cpp +++ b/core/libs/prx/libSceNetCtlApDialog/Export.cpp @@ -3,6 +3,7 @@ #include #include #include "SceTypes.hpp" +#include "prx/libSceCommonDialog/CommonDialog.hpp" #include "prx/libc/include/General.hpp" namespace { @@ -12,19 +13,13 @@ constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); -constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); +constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000Au); +constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; -struct SceNetCtlApDialogResult { - std::int32_t result = COMMON_DIALOG_RESULT_USER_CANCELED; - std::uint8_t reserved[36] = {}; -}; - -static_assert(sizeof(SceNetCtlApDialogResult) == 40); - std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; } @@ -42,17 +37,20 @@ int APS5_VABI sceNetCtlApDialogTerminate(void) { return 0; } -int APS5_VABI sceNetCtlApDialogOpen(const void* param) { +int APS5_VABI sceNetCtlApDialogOpen(const NetCtlApDialogParam* param) { + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + if (param->size != sizeof(*param) || param->reserved60 != 0 || std::find(std::begin(param->option), std::end(param->option), '\0') == std::end(param->option) || !std::all_of(std::begin(param->reserved), std::end(param->reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; - if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } int APS5_VABI sceNetCtlApDialogClose(void) { if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (g_status.load() != COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_NOT_RUNNING; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -65,14 +63,14 @@ int APS5_VABI sceNetCtlApDialogGetStatus(void) { return g_status.load(); } -int APS5_VABI sceNetCtlApDialogGetResult(void* result) { +int APS5_VABI sceNetCtlApDialogGetResult(NetCtlApDialogResult* result) { + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; const int status = g_status.load(); if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (!std::all_of(std::begin(result->reserved), std::end(result->reserved), [](std::uint8_t value) { return value == 0; })) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; - const SceNetCtlApDialogResult dialogResult{}; - std::memcpy(result, &dialogResult, sizeof(dialogResult)); - return 0; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + return COMMON_DIALOG_RESULT_USER_CANCELED; } } diff --git a/core/libs/tests/GuestNetCtlApDialog.cpp b/core/libs/tests/GuestNetCtlApDialog.cpp index bdce6ca102..f17c171e56 100644 --- a/core/libs/tests/GuestNetCtlApDialog.cpp +++ b/core/libs/tests/GuestNetCtlApDialog.cpp @@ -2,11 +2,12 @@ #include #include #include +#include "prx/libSceCommonDialog/CommonDialog.hpp" extern "C" { int APS5_VABI sceNetCtlApDialogInitialize(void); int APS5_VABI sceNetCtlApDialogTerminate(void); -int APS5_VABI sceNetCtlApDialogOpen(const void* param); +int APS5_VABI sceNetCtlApDialogOpen(const NetCtlApDialogParam* param); int APS5_VABI sceNetCtlApDialogClose(void); int APS5_VABI sceNetCtlApDialogUpdateStatus(void); int APS5_VABI sceNetCtlApDialogGetStatus(void); @@ -37,12 +38,15 @@ void RequireCanceled(const std::uint8_t (&result)[44]) { } int main() { - std::uint8_t param[104] = {}; - std::uint8_t result[44]; + NetCtlApDialogParam param{}; + param.base.size = sizeof(param.base); + param.base.magic = static_cast(reinterpret_cast(¶m)) - 1060003575u; + param.size = sizeof(param); + alignas(NetCtlApDialogResult) std::uint8_t result[44]; std::memset(result, 0x5a, sizeof(result)); Require(sceNetCtlApDialogTerminate() == kErrNotInitialized); - Require(sceNetCtlApDialogOpen(param) == kErrNotInitialized); + Require(sceNetCtlApDialogOpen(¶m) == kErrNotInitialized); Require(sceNetCtlApDialogClose() == kErrNotInitialized); Require(sceNetCtlApDialogGetResult(result) == kErrNotInitialized); Require(sceNetCtlApDialogGetStatus() == kStatusNone); @@ -55,27 +59,39 @@ int main() { Require(sceNetCtlApDialogOpen(nullptr) == kErrArgNull); Require(sceNetCtlApDialogGetStatus() == kStatusInitialized); + Require(sceNetCtlApDialogGetResult(result) == static_cast(0x80B8000Au)); + std::memset(result + 4, 0, 36); Require(sceNetCtlApDialogGetResult(result) == kErrNotFinished); - Require(sceNetCtlApDialogOpen(param) == 0); + param.size = 103; + Require(sceNetCtlApDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.size = sizeof(param); + param.reserved60 = 1; + Require(sceNetCtlApDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.reserved60 = 0; + std::memset(param.option, 'a', sizeof(param.option)); + Require(sceNetCtlApDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.option[8] = 0; + param.reserved[30] = 1; + Require(sceNetCtlApDialogOpen(¶m) == static_cast(0x80B8000Au)); + param.reserved[30] = 0; + Require(sceNetCtlApDialogOpen(¶m) == 0); Require(sceNetCtlApDialogGetStatus() == kStatusFinished); Require(sceNetCtlApDialogUpdateStatus() == kStatusFinished); Require(sceNetCtlApDialogGetResult(nullptr) == kErrArgNull); - Require(sceNetCtlApDialogGetResult(result) == 0); + Require(sceNetCtlApDialogGetResult(result) == kResultUserCanceled); RequireCanceled(result); - Require(sceNetCtlApDialogOpen(param) == 0); - Require(sceNetCtlApDialogClose() == 0); + Require(sceNetCtlApDialogOpen(¶m) == 0); + Require(sceNetCtlApDialogClose() == static_cast(0x80B8000Bu)); Require(sceNetCtlApDialogTerminate() == 0); Require(sceNetCtlApDialogTerminate() == kErrNotInitialized); Require(sceNetCtlApDialogGetStatus() == kStatusNone); Require(sceNetCtlApDialogInitialize() == 0); - Require(sceNetCtlApDialogClose() == 0); - Require(sceNetCtlApDialogGetStatus() == kStatusFinished); - std::memset(result, 0x5a, sizeof(result)); - Require(sceNetCtlApDialogGetResult(result) == 0); - RequireCanceled(result); + Require(sceNetCtlApDialogClose() == static_cast(0x80B8000Bu)); + Require(sceNetCtlApDialogGetStatus() == kStatusInitialized); + Require(sceNetCtlApDialogGetResult(result) == kErrNotFinished); Require(sceNetCtlApDialogTerminate() == 0); } diff --git a/docs/dev/debt/libSceNetCtlApDialog.md b/docs/dev/debt/libSceNetCtlApDialog.md index aefc800699..faeb9b3cb6 100644 --- a/docs/dev/debt/libSceNetCtlApDialog.md +++ b/docs/dev/debt/libSceNetCtlApDialog.md @@ -2,4 +2,4 @@ ### Silent stubs -- [libSceNetCtlApDialog](../../../core/libs/prx/libSceNetCtlApDialog/Export.cpp) - the access-point dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no access-point connection is brought up. `Open` takes the 104-byte parameter block and `GetResult` writes a 40-byte result (int32 result and 36 zeroed reserved bytes), following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/NetCtlApDialog.cs); the parameter block is not validated and the 0x80B8xxxx error codes follow the other common dialogs, neither verified on hardware. Deathloop imports it +- [libSceNetCtlApDialog](../../../core/libs/prx/libSceNetCtlApDialog/Export.cpp) - the access-point dialog shows no UI and reports user cancellation without bringing up a connection. Parameter size, reserved-byte checks, option-string limit and lifecycle errors are recovered from the supplied module. The meaning of the input word at offset 56 and service-side connection behavior remain unresolved. From 20a2488b4b446f49710324bef4605b78993763bd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:37:50 +0300 Subject: [PATCH 482/523] fix(appcontent): restore initialization guards and validation --- core/libs/prx/libSceAppContent/Export.cpp | 42 ++++++++++++++++++++--- core/libs/tests/GuestAppContent.cpp | 42 ++++++++++++++++++++++- docs/dev/debt/libSceAppContent.md | 2 +- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index 30a0b1bc1c..24f6127080 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -3,14 +3,17 @@ #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "prx/libc/include/RemoveTree.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libkernel/AppMetadata/include/AppMetadata.hpp" #include +#include #include #include #include #include #include +static constexpr int SCE_APP_CONTENT_ERROR_NOT_INITIALIZED = static_cast(0x80D90001); static constexpr int SCE_APP_CONTENT_ERROR_PARAMETER = static_cast(0x80D90002); static constexpr int SCE_APP_CONTENT_ERROR_BUSY = static_cast(0x80D90003); static constexpr int SCE_APP_CONTENT_ERROR_NOT_MOUNTED = static_cast(0x80D90004); @@ -30,6 +33,16 @@ struct AppContentAddcontInfo { namespace { +std::atomic_bool initialized{false}; +std::mutex initializationMutex; + +std::uint32_t compiledSdkVersion() { + const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t version; + std::memcpy(&version, process + 16, sizeof(version)); + return version; +} + struct TemporaryData { std::mutex mutex; const std::filesystem::path directory = ResolvePath_nid_no_patch(TEMPORARY_MOUNT_POINT); @@ -57,23 +70,27 @@ static void ClearDirectory(const std::filesystem::path& directory) { extern "C" { int APS5_VABI sceAppContentAddcontMount(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, AppContentMountPoint* mount_point) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label || !mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentAddcontUnmount(const AppContentMountPoint* mount_point) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentGetAddcontInfo(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, AppContentAddcontInfo* info) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label || !info) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT; } int APS5_VABI sceAppContentGetAddcontInfoList(uint32_t serviceLabel, AppContentAddcontInfo* list, uint32_t listNum, uint32_t* hitNum) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)serviceLabel; if (!hitNum) return SCE_APP_CONTENT_ERROR_PARAMETER; *hitNum = 0; @@ -81,6 +98,7 @@ int APS5_VABI sceAppContentGetAddcontInfoList(uint32_t serviceLabel, AppContentA } int APS5_VABI sceAppContentAppParamGetInt(uint32_t param_id, int32_t* value) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!value) return SCE_APP_CONTENT_ERROR_PARAMETER; switch (param_id) { case APPPARAM_ID_SKU_FLAG: @@ -95,6 +113,7 @@ int APS5_VABI sceAppContentAppParamGetInt(uint32_t param_id, int32_t* value) { } int APS5_VABI sceAppContentDownloadDataFormat(const AppContentMountPoint* mount_point) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; if (std::strncmp(mount_point->data, DOWNLOAD_MOUNT_POINT, sizeof(mount_point->data)) != 0) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; const auto directory = ResolvePath_nid_no_patch(DOWNLOAD_MOUNT_POINT); @@ -104,6 +123,7 @@ int APS5_VABI sceAppContentDownloadDataFormat(const AppContentMountPoint* mount_ } int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMountPoint* mountPoint, size_t* availableSpaceKb) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mountPoint || !availableSpaceKb) return SCE_APP_CONTENT_ERROR_PARAMETER; if (std::strncmp(mountPoint->data, DOWNLOAD_MOUNT_POINT, sizeof(mountPoint->data)) != 0) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; const std::uint64_t quotaKb = GetAppDownloadDataSizeMiB_nid_postfix() * 1024u; @@ -122,15 +142,23 @@ int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMount return 0; } -int APS5_VABI sceAppContentInitialize(const AppContentInitParam* init_param, AppContentBootParam* boot_param) { - (void)init_param; - if (!boot_param) return SCE_APP_CONTENT_ERROR_PARAMETER; +int APS5_VABI sceAppContentInitialize(const AppContentInitParam* initParam, AppContentBootParam* bootParam) { + std::lock_guard lock(initializationMutex); + const auto sdkVersion = compiledSdkVersion(); + if (sdkVersion >= 0x01500000) { + if (initialized.load()) return SCE_APP_CONTENT_ERROR_BUSY; + if (initParam == nullptr || bootParam == nullptr) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (!std::all_of(std::begin(initParam->reserved), std::end(initParam->reserved), [](char value) { return value == 0; }) || !std::all_of(std::begin(bootParam->reserved1), std::end(bootParam->reserved1), [](char value) { return value == 0; }) || !std::all_of(std::begin(bootParam->reserved2), std::end(bootParam->reserved2), [](char value) { return value == 0; })) return SCE_APP_CONTENT_ERROR_PARAMETER; + } + if (bootParam == nullptr) return SCE_APP_CONTENT_ERROR_PARAMETER; Temporary(); - std::memset(boot_param, 0, sizeof(*boot_param)); + std::memset(bootParam, 0, sizeof(*bootParam)); + initialized = true; return 0; } int APS5_VABI sceAppContentTemporaryDataFormat(const AppContentMountPoint* mountPoint) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mountPoint) return SCE_APP_CONTENT_ERROR_PARAMETER; auto& state = Temporary(); std::lock_guard lock(state.mutex); @@ -140,6 +168,7 @@ int APS5_VABI sceAppContentTemporaryDataFormat(const AppContentMountPoint* mount } int APS5_VABI sceAppContentTemporaryDataGetAvailableSpaceKb(const AppContentMountPoint* mountPoint, size_t* availableSpaceKb) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mountPoint || !availableSpaceKb) return SCE_APP_CONTENT_ERROR_PARAMETER; auto& state = Temporary(); std::lock_guard lock(state.mutex); @@ -149,6 +178,7 @@ int APS5_VABI sceAppContentTemporaryDataGetAvailableSpaceKb(const AppContentMoun } int APS5_VABI sceAppContentTemporaryDataMount2(uint32_t option, AppContentMountPoint* mountPoint) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mountPoint || option > TEMPORARY_DATA_OPTION_FORMAT) return SCE_APP_CONTENT_ERROR_PARAMETER; auto& state = Temporary(); std::lock_guard lock(state.mutex); @@ -163,6 +193,7 @@ int APS5_VABI sceAppContentTemporaryDataMount2(uint32_t option, AppContentMountP } int APS5_VABI sceAppContentTemporaryDataUnmount(const AppContentMountPoint* mountPoint) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mountPoint) return SCE_APP_CONTENT_ERROR_PARAMETER; auto& state = Temporary(); std::lock_guard lock(state.mutex); @@ -174,18 +205,21 @@ int APS5_VABI sceAppContentTemporaryDataUnmount(const AppContentMountPoint* moun int APS5_VABI sceAppContentAddcontEnqueueDownload(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentAddcontDelete(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentGetAddcontDownloadProgress(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, void* progress) { + if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label || !progress) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp index 4cf0721f55..f8c3cc5427 100644 --- a/core/libs/tests/GuestAppContent.cpp +++ b/core/libs/tests/GuestAppContent.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include "ProcessParametersFixture.hpp" extern "C" { int APS5_VABI sceAppContentInitialize(const AppContentInitParam*, AppContentBootParam*); @@ -34,6 +36,25 @@ static constexpr int ErrorNotMounted = static_cast(0x80D90004); static void Require(bool value) { if (!value) std::abort(); } int main() { + static std::array process{}; + Require(ProcessParametersFixture::Install(process.data(), process.size())); + std::uint32_t sdkVersion = 0x01500000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + constexpr int notInitialized = static_cast(0x80D90001u); + Require(sceAppContentAddcontMount(0, nullptr, nullptr) == notInitialized); + Require(sceAppContentAddcontUnmount(nullptr) == notInitialized); + Require(sceAppContentGetAddcontInfo(0, nullptr, nullptr) == notInitialized); + Require(sceAppContentGetAddcontInfoList(0, nullptr, 0, nullptr) == notInitialized); + Require(sceAppContentAppParamGetInt(0, nullptr) == notInitialized); + Require(sceAppContentTemporaryDataMount2(0, nullptr) == notInitialized); + Require(sceAppContentTemporaryDataUnmount(nullptr) == notInitialized); + Require(sceAppContentTemporaryDataFormat(nullptr) == notInitialized); + Require(sceAppContentTemporaryDataGetAvailableSpaceKb(nullptr, nullptr) == notInitialized); + Require(sceAppContentDownloadDataFormat(nullptr) == notInitialized); + Require(sceAppContentDownloadDataGetAvailableSpaceKb(nullptr, nullptr) == notInitialized); + Require(sceAppContentAddcontDelete(0, nullptr) == notInitialized); + Require(sceAppContentAddcontEnqueueDownload(0, nullptr) == notInitialized); + Require(sceAppContentGetAddcontDownloadProgress(0, nullptr, nullptr) == notInitialized); const auto original = std::filesystem::current_path(); const auto directory = original / ("appcontent-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); Require(std::filesystem::create_directory(directory)); @@ -45,6 +66,26 @@ int main() { R"("userDefinedParam1":23566,"userDefinedParam2":-7,"userDefinedParam3":0,"userDefinedParam4":2147483647})"; Require(static_cast(param)); } + AppContentInitParam init{}; + AppContentBootParam boot{}; + Require(sceAppContentInitialize(nullptr, &boot) == ErrorParameter); + Require(sceAppContentInitialize(&init, nullptr) == ErrorParameter); + init.reserved[31] = 1; + Require(sceAppContentInitialize(&init, &boot) == ErrorParameter); + init.reserved[31] = 0; + boot.reserved1[3] = 1; + Require(sceAppContentInitialize(&init, &boot) == ErrorParameter); + boot.reserved1[3] = 0; + boot.reserved2[31] = 1; + Require(sceAppContentInitialize(&init, &boot) == ErrorParameter); + boot.reserved2[31] = 0; + boot.attr = 0xffffffffu; + Require(sceAppContentInitialize(&init, &boot) == 0); + Require(boot.attr == 0); + Require(sceAppContentInitialize(nullptr, nullptr) == ErrorBusy); + sdkVersion = 0x01400000; + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(sceAppContentInitialize(nullptr, &boot) == 0); AppContentMountPoint download{}; std::memcpy(download.data, "/download0", 11); size_t availableKb = 12345; @@ -110,7 +151,6 @@ int main() { std::filesystem::create_directory(directory / "temp0"); { std::ofstream file(directory / "temp0" / "old.txt"); file << "previous run"; } - AppContentBootParam boot{}; Require(sceAppContentInitialize(nullptr, &boot) == 0); Require(access_nid_postfix("/temp0/old.txt", 0) == -1); Require(sceAppContentTemporaryDataUnmount(nullptr) == ErrorParameter); diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index 373aa6fdba..93d5099c7b 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -9,7 +9,7 @@ - [libSceAppContent pending exports](../../../core/libs/prx/libSceAppContent/Export.cpp) - 17 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 -- [Initialization](../../../core/libs/prx/libSceAppContent/Export.cpp) does not track native service readiness or initialization. Native operations reject uninitialized use with `0x80D90001`; SDK 0x01500000 and later also reject repeated initialization and require zeroed initialization fields. Native entitlement operations additionally validate label bytes 17 through 19 from SDK 0x03500000. These checks and SDK-dependent service-error translation are not implemented. +- [Initialization](../../../core/libs/prx/libSceAppContent/Export.cpp) - service readiness and boot attributes are not modeled; successful initialization returns a zero boot record. Initialization-state guards, SDK 0x01500000 repeated-initialization errors and reserved-field checks are recovered. Native entitlement-label validation and SDK-dependent service-error translation remain incomplete. - [Additional content](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) is not installed or mounted: `sceAppContentAddcontMount` answers `NOT_FOUND` for every entitlement label and `sceAppContentAddcontUnmount` for every mount point, as no add-content mount is ever handed out; `sceAppContentGetAddcontInfo` answers `DRM_NO_ENTITLEMENT` for every label and `sceAppContentGetAddcontInfoList` reports 0 entries; `sceAppContentAddcontEnqueueDownload` answers `PARAMETER` for a null label and `NOT_FOUND` otherwise, since no download is ever queued; `sceAppContentAddcontDelete` answers `PARAMETER` for a null label and `NOT_FOUND` otherwise, since no additional content is ever installed. `sceAppContentGetAddcontDownloadProgress` answers `PARAMETER` for a null label or progress and `NOT_FOUND` otherwise, as [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.cpp#L2328-L2341) does for a label that is not installed additional content, and never writes the progress. Its 16-byte layout (`DataSize`, then `DownloadedSize`, both 64-bit) follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppContent/AppContent.cs#L45-L54) and is unused here. The signatures of `sceAppContentAddcontEnqueueDownload` and `sceAppContentGetAddcontDownloadProgress` follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppContent/AppContent.cs#L140-L148), and the signature of `sceAppContentAddcontDelete` follows [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.h#L91-L92); dlc_emu is not a console measurement and no console source gives the error codes, so the signatures and the error codes are unverified - [Temporary app-content storage](../../../core/libs/prx/libSceAppContent/Export.cpp) tracks one `/temp0` mount, preserves its files on unmount, and rejects new path resolutions until remount. Mount, format, space-query and unmount errors follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/sys/sys_path.pas); the pointer ABI is also declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/AppContent/AppContent.cs). Existing open host descriptors remain usable after unmount; console behaviour with open files is unverified. Host filesystem failures still throw. From f55e936f92fd381e67988b15294a0725594ed7e7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:38:12 +0300 Subject: [PATCH 483/523] fix(appcontent): validate entitlement padding by sdk version --- core/libs/prx/libSceAppContent/Export.cpp | 9 +++++++++ core/libs/tests/GuestAppContent.cpp | 18 ++++++++++++++++++ docs/dev/debt/libSceAppContent.md | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index 24f6127080..8bfb24b850 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -43,6 +43,10 @@ std::uint32_t compiledSdkVersion() { return version; } +bool validEntitlementLabel(const NpUnifiedEntitlementLabel& label, std::uint32_t minimumSdkVersion) { + return compiledSdkVersion() < minimumSdkVersion || std::all_of(std::begin(label.padding), std::end(label.padding), [](char value) { return value == 0; }); +} + struct TemporaryData { std::mutex mutex; const std::filesystem::path directory = ResolvePath_nid_no_patch(TEMPORARY_MOUNT_POINT); @@ -73,6 +77,7 @@ int APS5_VABI sceAppContentAddcontMount(uint32_t service_label, const NpUnifiedE if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label || !mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (!validEntitlementLabel(*entitlement_label, 0x01500000)) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } @@ -86,6 +91,7 @@ int APS5_VABI sceAppContentGetAddcontInfo(uint32_t service_label, const NpUnifie if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label || !info) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (!validEntitlementLabel(*entitlement_label, 0x01500000)) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT; } @@ -208,6 +214,7 @@ int APS5_VABI sceAppContentAddcontEnqueueDownload(uint32_t service_label, const if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (!validEntitlementLabel(*entitlement_label, 0x01500000)) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } @@ -215,6 +222,7 @@ int APS5_VABI sceAppContentAddcontDelete(uint32_t service_label, const NpUnified if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (!validEntitlementLabel(*entitlement_label, 0x03500000)) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } @@ -222,6 +230,7 @@ int APS5_VABI sceAppContentGetAddcontDownloadProgress(uint32_t service_label, co if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; (void)service_label; if (!entitlement_label || !progress) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (!validEntitlementLabel(*entitlement_label, 0x03500000)) return SCE_APP_CONTENT_ERROR_PARAMETER; return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentAddcontEnqueueDownloadByEntitlemetId(void) { diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp index f8c3cc5427..468467e521 100644 --- a/core/libs/tests/GuestAppContent.cpp +++ b/core/libs/tests/GuestAppContent.cpp @@ -200,6 +200,24 @@ int main() { Require(sceAppContentGetAddcontInfo(0, nullptr, info) == ErrorParameter); Require(sceAppContentGetAddcontInfo(0, &label, nullptr) == ErrorParameter); + for (const std::uint32_t version : {0x014fffffu, 0x01500000u, 0x034fffffu, 0x03500000u}) { + std::memcpy(process.data() + 16, &version, sizeof(version)); + for (char& padding : label.padding) { + padding = 1; + const int earlyError = version >= 0x01500000 ? ErrorParameter : ErrorNotFound; + const int infoError = version >= 0x01500000 ? ErrorParameter : ErrorDrmNoEntitlement; + const int lateError = version >= 0x03500000 ? ErrorParameter : ErrorNotFound; + Require(sceAppContentAddcontMount(0, &label, &mountPoint) == earlyError); + Require(sceAppContentGetAddcontInfo(0, &label, info) == infoError); + Require(sceAppContentAddcontEnqueueDownload(0, &label) == earlyError); + Require(sceAppContentAddcontDelete(0, &label) == lateError); + Require(sceAppContentGetAddcontDownloadProgress(0, &label, progress) == lateError); + Require(std::memcmp(info, untouchedInfo, sizeof(info)) == 0); + Require(std::memcmp(progress, progressBefore, sizeof(progress)) == 0); + padding = 0; + } + } + uint32_t hitNum = 0x5a5a5a5a; Require(sceAppContentGetAddcontInfoList(0, nullptr, 0, &hitNum) == 0); Require(hitNum == 0); diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index 93d5099c7b..371a027f51 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -9,7 +9,7 @@ - [libSceAppContent pending exports](../../../core/libs/prx/libSceAppContent/Export.cpp) - 17 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 -- [Initialization](../../../core/libs/prx/libSceAppContent/Export.cpp) - service readiness and boot attributes are not modeled; successful initialization returns a zero boot record. Initialization-state guards, SDK 0x01500000 repeated-initialization errors and reserved-field checks are recovered. Native entitlement-label validation and SDK-dependent service-error translation remain incomplete. +- [Initialization](../../../core/libs/prx/libSceAppContent/Export.cpp) - service readiness and boot attributes are not modeled; successful initialization returns a zero boot record. Initialization-state guards, SDK 0x01500000 repeated-initialization errors and reserved-field checks are recovered. SDK-dependent service-error translation remains incomplete. - [Additional content](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) is not installed or mounted: `sceAppContentAddcontMount` answers `NOT_FOUND` for every entitlement label and `sceAppContentAddcontUnmount` for every mount point, as no add-content mount is ever handed out; `sceAppContentGetAddcontInfo` answers `DRM_NO_ENTITLEMENT` for every label and `sceAppContentGetAddcontInfoList` reports 0 entries; `sceAppContentAddcontEnqueueDownload` answers `PARAMETER` for a null label and `NOT_FOUND` otherwise, since no download is ever queued; `sceAppContentAddcontDelete` answers `PARAMETER` for a null label and `NOT_FOUND` otherwise, since no additional content is ever installed. `sceAppContentGetAddcontDownloadProgress` answers `PARAMETER` for a null label or progress and `NOT_FOUND` otherwise, as [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.cpp#L2328-L2341) does for a label that is not installed additional content, and never writes the progress. Its 16-byte layout (`DataSize`, then `DownloadedSize`, both 64-bit) follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppContent/AppContent.cs#L45-L54) and is unused here. The signatures of `sceAppContentAddcontEnqueueDownload` and `sceAppContentGetAddcontDownloadProgress` follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/AppContent/AppContent.cs#L140-L148), and the signature of `sceAppContentAddcontDelete` follows [dlc_emu](https://github.com/drakmor/dlc_emu/blob/fcf69b16d46a9850578bce19dc2c1b4dc867cbb5/src/dlc_modules/dlc_content.h#L91-L92); dlc_emu is not a console measurement and no console source gives the error codes, so the signatures and the error codes are unverified - [Temporary app-content storage](../../../core/libs/prx/libSceAppContent/Export.cpp) tracks one `/temp0` mount, preserves its files on unmount, and rejects new path resolutions until remount. Mount, format, space-query and unmount errors follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/sys/sys_path.pas); the pointer ABI is also declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/AppContent/AppContent.cs). Existing open host descriptors remain usable after unmount; console behaviour with open files is unverified. Host filesystem failures still throw. From 5f980378c42836650b70dd77e40949cf2e755a19 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:39:41 +0300 Subject: [PATCH 484/523] fix(font): restore device cache ownership and detach outputs --- core/libs/prx/libSceFont/src/Library.cpp | 41 +++++++++++++++--------- core/libs/tests/GuestFont.cpp | 10 ++++-- docs/dev/debt/libSceFont.md | 1 - 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Library.cpp b/core/libs/prx/libSceFont/src/Library.cpp index a3e01002c3..4d8adbac7e 100644 --- a/core/libs/prx/libSceFont/src/Library.cpp +++ b/core/libs/prx/libSceFont/src/Library.cpp @@ -378,26 +378,37 @@ int APS5_VABI sceFontClearDeviceCache(FontLibrary library) { } int APS5_VABI sceFontDettachDeviceCacheBuffer(FontLibrary library, void** buffer, std::uint32_t* size) { + const auto clearOutputs = [&] { + if (buffer) *buffer = nullptr; + if (size) *size = 0; + }; auto* lib = static_cast(library); - if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY; std::uint32_t previous = 0; - if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY; + if (!lib || lib->magic != LIBRARY_MAGIC || !AcquireLibraryLock(lib, previous)) { + clearOutputs(); + return SCE_FONT_ERROR_INVALID_LIBRARY; + } std::uint32_t* current = AcquireDeviceCache(lib); - if (!current) { - ReleaseDeviceCache(lib, nullptr); - ReleaseLibraryLock(lib, previous); - return SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER; - } - if (size) *size = current[0]; - if (buffer) { - *buffer = current; - } else if ((lib->flags & 1u) != 0) { - lib->iface->dealloc(lib->alloc_ctx, current); - } - lib->flags &= ~1u; ReleaseDeviceCache(lib, nullptr); + int result = SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER; + if (current) { + const std::uint32_t capacity = current[0]; + std::memset(current + 1, 0, 5 * sizeof(std::uint32_t)); + if ((lib->flags & 1u) == 0) { + if (buffer) *buffer = current; + if (size) *size = capacity; + ReleaseLibraryLock(lib, previous); + return SCE_FONT_OK; + } + result = SCE_FONT_ERROR_FATAL; + if (lib->iface && lib->iface->dealloc) { + lib->iface->dealloc(lib->alloc_ctx, current); + result = SCE_FONT_OK; + } + } + clearOutputs(); ReleaseLibraryLock(lib, previous); - return SCE_FONT_OK; + return result; } int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, std::uint32_t fontMax, std::uint32_t formats) { diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index afa5e93d2c..54880a4991 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -257,9 +257,11 @@ int main() { Require(static_cast(coarseLibrary)->lock_word == 0); Require(sceFontDestroyLibrary(&coarseLibrary) == SCE_FONT_OK && coarseLibrary == nullptr); - void* cacheBuffer = nullptr; + void* cacheBuffer = ¬ALibrary; std::uint32_t cacheSize = 0; + cacheSize = 123; Require(sceFontDettachDeviceCacheBuffer(nullptr, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_INVALID_LIBRARY); + Require(cacheBuffer == nullptr && cacheSize == 0); Require(sceFontDettachDeviceCacheBuffer(¬ALibrary, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_INVALID_LIBRARY); Require(sceFontClearDeviceCache(library) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER); Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER); @@ -270,13 +272,15 @@ int main() { Require(sceFontAttachDeviceCacheBuffer(library, callerCache.data(), static_cast(callerCache.size())) == SCE_FONT_ERROR_ALREADY_ATTACHED); Require(sceFontClearDeviceCache(library) == SCE_FONT_OK); Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_OK && cacheBuffer == callerCache.data() && cacheSize == callerCache.size()); + for (std::size_t offset = 4; offset < 24; ++offset) Require(callerCache[offset] == 0); Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER); Require(sceFontAttachDeviceCacheBuffer(library, nullptr, 0x2000) == SCE_FONT_OK); cacheBuffer = nullptr; cacheSize = 0; - Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_OK && cacheBuffer != nullptr && cacheSize == 0x2000); - Release(nullptr, cacheBuffer); + const int allocationsBeforeDetach = allocations; + Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_OK && cacheBuffer == nullptr && cacheSize == 0); + Require(allocations == allocationsBeforeDetach - 1); Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER); Require(sceFontAttachDeviceCacheBuffer(library, nullptr, 0x2000) == SCE_FONT_OK); diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index b387ddb5d3..ced51def02 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -5,7 +5,6 @@ - [sceFontSetTypographicDesign and sceFontGetTypographicDesign](../../../core/libs/prx/libSceFont/src/Style.cpp): signatures are confirmed by the supplied PS5 library. Native feature ranges, defaults and driver-dependent availability are not implemented; the host still accepts arbitrary values and stores them without affecting shaping or rendering. - [sceFontSetScriptLanguage and sceFontGetScriptLanguage](../../../core/libs/prx/libSceFont/src/Style.cpp): native script/language ranges, handle-error precedence and clearing of failed getter outputs are implemented. Valid driver queries and updates throw; the previous arbitrary-value map and fabricated zero default are no longer used. Driver selection and actual language-dependent shaping remain unimplemented. - [sceFontGetRenderScaledKerning](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale -- [sceFontDettachDeviceCacheBuffer](../../../core/libs/prx/libSceFont/src/Library.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title. It detaches the cache buffer attached by `sceFontAttachDeviceCacheBuffer`, returns the buffer and its size, and reports `SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER` when no cache buffer is attached - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` - [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title; the outputs are inferred from the names. The resolution is the face's units per EM and the scale pixel the driver's face scale (units per EM / 64 for FreeType), the pixel scale a font has when opened. Either output may be null, as in `sceFontGetResolutionDpi` - [Writing-line ABI and allocation](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): PS5 layouts, creation forms, memory validation, allocator callbacks and error handling are recovered. The 2208-byte line and 64-byte orders use caller memory. Host registry bookkeeping still allocates separately; memory lifetime and concurrent destruction require caller synchronization. From 512ae7f80f7558a74471248f2af1011f50de3074 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:41:42 +0300 Subject: [PATCH 485/523] fix(font): use 32-bit resolution in driver callbacks --- core/libs/prx/libSceFont/include/FontDriver.hpp | 2 +- core/libs/prx/libSceFont/src/Open.cpp | 5 +++-- core/libs/prx/libSceFontFt/src/Driver.cpp | 2 +- core/libs/tests/GuestFont.cpp | 16 ++++++++++++++++ docs/dev/debt/libSceFont.md | 2 +- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceFont/include/FontDriver.hpp b/core/libs/prx/libSceFont/include/FontDriver.hpp index 889c367dda..35b8d3d533 100644 --- a/core/libs/prx/libSceFont/include/FontDriver.hpp +++ b/core/libs/prx/libSceFont/include/FontDriver.hpp @@ -202,7 +202,7 @@ using DriverTermFunction = int (APS5_VABI*)(FontLibNative* library); using DriverSupportFunction = int (APS5_VABI*)(FontLibNative* library, std::uint32_t formats); using DriverOpenFunction = int (APS5_VABI*)(FontLibNative* library, std::uint32_t mode, const void* fontAddress, std::uint32_t fontSize, std::uint32_t subFontIndex, std::uint32_t uniqueWord, FontObj** inoutFontObj); using DriverCloseFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t flags); -using DriverScaleFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint16_t* outUnitsPerEm, float* outScale); +using DriverScaleFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* outUnitsPerEm, float* outScale); using DriverMetricFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t metricId, std::uint16_t* outMetric); using DriverGlyphsCountFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* outCount); using DriverGlyphIndexFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t codepoint, std::uint32_t* outGlyphIndex); diff --git a/core/libs/prx/libSceFont/src/Open.cpp b/core/libs/prx/libSceFont/src/Open.cpp index 21e0272990..8b8480ddd8 100644 --- a/core/libs/prx/libSceFont/src/Open.cpp +++ b/core/libs/prx/libSceFont/src/Open.cpp @@ -82,7 +82,8 @@ void InitializeOpenedHandle(FontHandleNative* font, FontLibNative* library, Font driver->metric(obj, 0xEA00, metric); font->metricB = metric[0]; float scale = 0.0f; - driver->scale(obj, metric, &scale); + std::uint32_t resolution = 0; + driver->scale(obj, &resolution, &scale); font->style = {}; font->style.dpi_x = 0x48; font->style.dpi_y = 0x48; @@ -515,7 +516,7 @@ int APS5_VABI sceFontGetCharGlyphCode(FontHandle fontHandle, std::uint32_t code, } int APS5_VABI sceFontGetFontResolution(FontHandle fontHandle, std::uint32_t* pResolution, float* pScalePixel) { - std::uint16_t unitsPerEm = 0; + std::uint32_t unitsPerEm = 0; float scalePixel = 0.0f; const int rc = CallOpenFontObject(fontHandle, [&](const SysDriver& driver, FontObj* obj) { if (!pResolution && !pScalePixel) return SCE_FONT_ERROR_INVALID_PARAMETER; diff --git a/core/libs/prx/libSceFontFt/src/Driver.cpp b/core/libs/prx/libSceFontFt/src/Driver.cpp index 152a72f8d7..6ed7e5203e 100644 --- a/core/libs/prx/libSceFontFt/src/Driver.cpp +++ b/core/libs/prx/libSceFontFt/src/Driver.cpp @@ -269,7 +269,7 @@ int APS5_VABI LibraryCloseFont(FontObj* obj, std::uint32_t flags) { return SCE_FONT_OK; } -int APS5_VABI LibraryGetFaceScale(FontObj* obj, std::uint16_t* outUnitsPerEm, float* outScale) { +int APS5_VABI LibraryGetFaceScale(FontObj* obj, std::uint32_t* outUnitsPerEm, float* outScale) { if (!obj || !outUnitsPerEm || !outScale) return SCE_FONT_ERROR_FATAL; const FT_Face face = FaceOf(obj); if (!face) return SCE_FONT_ERROR_FATAL; diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index 54880a4991..b466265979 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -108,6 +108,12 @@ static std::uint32_t APS5_VABI CoarsePixelResolution() { return 16; } +static int APS5_VABI WideFontResolution(Font::FontObj*, std::uint32_t* resolution, float* scale) { + *resolution = 0x12345678; + *scale = 12.5f; + return SCE_FONT_OK; +} + using FontFixture::BuildFont; using FontFixture::Words; @@ -355,6 +361,16 @@ int main() { Require(sceFontGetAttribute(otherFont, 0, &attribute) == SCE_FONT_OK && attribute == 7); Require(sceFontGetAttribute(font, 0, &attribute) == SCE_FONT_OK && attribute == 5); Require(sceFontCloseFont(otherFont) == SCE_FONT_OK); + auto* resolutionLibrary = static_cast(library); + const Font::SysDriver* originalDriver = resolutionLibrary->sys_driver; + Font::SysDriver wideResolutionDriver = *originalDriver; + wideResolutionDriver.scale = WideFontResolution; + resolutionLibrary->sys_driver = &wideResolutionDriver; + std::uint32_t wideResolution = 0; + float wideScale = 0; + Require(sceFontGetFontResolution(font, &wideResolution, &wideScale) == SCE_FONT_OK); + Require(wideResolution == 0x12345678 && wideScale == 12.5f); + resolutionLibrary->sys_driver = originalDriver; std::uint32_t hDpi = 1; std::uint32_t vDpi = 1; Require(sceFontGetResolutionDpi(font, &hDpi, &vDpi) == SCE_FONT_OK && hDpi == 72 && vDpi == 72); diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index ced51def02..b75f406bbe 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -6,7 +6,7 @@ - [sceFontSetScriptLanguage and sceFontGetScriptLanguage](../../../core/libs/prx/libSceFont/src/Style.cpp): native script/language ranges, handle-error precedence and clearing of failed getter outputs are implemented. Valid driver queries and updates throw; the previous arbitrary-value map and fabricated zero default are no longer used. Driver selection and actual language-dependent shaping remain unimplemented. - [sceFontGetRenderScaledKerning](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` -- [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signature taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), not confirmed on a title; the outputs are inferred from the names. The resolution is the face's units per EM and the scale pixel the driver's face scale (units per EM / 64 for FreeType), the pixel scale a font has when opened. Either output may be null, as in `sceFontGetResolutionDpi` +- [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp): the 32-bit resolution output, float scale and nullable outputs are confirmed by the PS5 library and FontFt driver. Native system font sets return resolution 1000; the host still uses its selected face resolution because multi-face system font sets are not modeled. - [Writing-line ABI and allocation](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): PS5 layouts, creation forms, memory validation, allocator callbacks and error handling are recovered. The 2208-byte line and 64-byte orders use caller memory. Host registry bookkeeping still allocates separately; memory lifetime and concurrent destruction require caller synchronization. - [sceFontWritingRefersRenderStepCharacter](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the second argument is an out pointer to the writing's letter step, as in [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1882-L1896) and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings. Measured on PS4 firmware 11.00 `libSceFont.sprx` (not on a PS5): exactly 8 bytes are written, the address of a step stored inside the writing, and a null writing writes null; shadPS4 and prosper treat the argument as a struct, which the measurement contradicts. The letter step lives in the writing, so the pointer stays valid as long as the writing does, and its contents are rebuilt by each call of this function. On the PS4 library repeated calls on one step walk the characters it covers and then return null; here every call returns the step's one character, so the second call differs. Assumed: each step covers one character and one glyph. PPSA06699 (Hot Shots Golf 2) dereferences the returned pointer for its character counts. - [sceFontWritingLineGetOrderingSpace](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): PS5 confirms output order, calculations and zeroed outputs on invalid handles. A non-null head output with a null tail output causes an unchecked native write; the host throws instead. With no head output, the tail output remains untouched. From 34123d4e290ff498915f0891b4c8b8aef5ee9225 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:42:44 +0300 Subject: [PATCH 486/523] fix(font): restore encoded glyph identifiers and validation --- core/libs/prx/libSceFont/src/Open.cpp | 25 +++++++++++++++++++++---- core/libs/tests/GuestFont.cpp | 12 ++++++++++-- docs/dev/debt/libSceFont.md | 2 +- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceFont/src/Open.cpp b/core/libs/prx/libSceFont/src/Open.cpp index 8b8480ddd8..36e7e2263d 100644 --- a/core/libs/prx/libSceFont/src/Open.cpp +++ b/core/libs/prx/libSceFont/src/Open.cpp @@ -493,6 +493,11 @@ int APS5_VABI sceFontGetLibrary(FontHandle fontHandle, FontLibrary* pLibrary) { } int APS5_VABI sceFontGetFontGlyphsCount(FontHandle fontHandle, std::uint32_t* glyphsCount) { + const auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != HANDLE_MAGIC) { + if (glyphsCount) *glyphsCount = 0; + return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + } if (!glyphsCount) return SCE_FONT_ERROR_INVALID_PARAMETER; *glyphsCount = 0; const int rc = CallOpenFontObject(fontHandle, [&](const SysDriver& driver, FontObj* obj) { @@ -504,14 +509,26 @@ int APS5_VABI sceFontGetFontGlyphsCount(FontHandle fontHandle, std::uint32_t* gl } int APS5_VABI sceFontGetCharGlyphCode(FontHandle fontHandle, std::uint32_t code, std::uint32_t* glyphCode) { + if (glyphCode) *glyphCode = 0; + const auto* font = GetNativeFont(fontHandle); + if (!font || font->magic != HANDLE_MAGIC) return SCE_FONT_ERROR_INVALID_FONT_HANDLE; + if (code == 0) return SCE_FONT_ERROR_NO_SUPPORT_CODE; if (!glyphCode) return SCE_FONT_ERROR_INVALID_PARAMETER; - *glyphCode = 0; + if ((code & 0x80000000u) != 0 && (code & 0x3fff0000u) != 0) return SCE_FONT_ERROR_NO_SUPPORT_CODE; + std::uint32_t index = 0; const int rc = CallOpenFontObject(fontHandle, [&](const SysDriver& driver, FontObj* obj) { + if ((code & 0x80000000u) != 0) { + if (!driver.glyphs_count) return SCE_FONT_ERROR_FATAL; + std::uint32_t count = 0; + const int result = driver.glyphs_count(obj, &count); + if (result != SCE_FONT_OK) return result; + index = code & 0x3fffffffu; + return index < count ? SCE_FONT_OK : SCE_FONT_ERROR_NO_SUPPORT_GLYPH; + } if (!driver.glyph_index) return SCE_FONT_ERROR_FATAL; - if (code == 0) return SCE_FONT_ERROR_NO_SUPPORT_CODE; - return driver.glyph_index(obj, code, glyphCode); + return driver.glyph_index(obj, code, &index); }); - if (rc != SCE_FONT_OK) *glyphCode = 0; + if (rc == SCE_FONT_OK) *glyphCode = index | 0x80000000u; return rc; } diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index b466265979..cd0085e158 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -423,8 +423,16 @@ int main() { font = nullptr; Require(sceFontOpenFontMemory(library, kerningData.data(), static_cast(kerningData.size()), nullptr, &font) == SCE_FONT_OK && font != nullptr); Require(sceFontGetFontGlyphsCount(font, &glyphsCount) == SCE_FONT_OK && glyphsCount == 3); - Require(sceFontGetCharGlyphCode(font, 'A', &glyphCode) == SCE_FONT_OK && glyphCode == 1); - Require(sceFontGetCharGlyphCode(font, 'V', &glyphCode) == SCE_FONT_OK && glyphCode == 2); + Require(sceFontGetCharGlyphCode(font, 'A', &glyphCode) == SCE_FONT_OK && glyphCode == 0x80000001u); + Require(sceFontGetCharGlyphCode(font, 'V', &glyphCode) == SCE_FONT_OK && glyphCode == 0x80000002u); + Require(sceFontGetCharGlyphCode(nullptr, 0, nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + Require(sceFontGetFontGlyphsCount(nullptr, nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + Require(sceFontGetCharGlyphCode(font, 0, nullptr) == SCE_FONT_ERROR_NO_SUPPORT_CODE); + Require(sceFontGetCharGlyphCode(font, 0x80000000u, &glyphCode) == SCE_FONT_OK && glyphCode == 0x80000000u); + Require(sceFontGetCharGlyphCode(font, 0x80000002u, &glyphCode) == SCE_FONT_OK && glyphCode == 0x80000002u); + Require(sceFontGetCharGlyphCode(font, 0xc0000001u, &glyphCode) == SCE_FONT_OK && glyphCode == 0x80000001u); + Require(sceFontGetCharGlyphCode(font, 0x80000003u, &glyphCode) == SCE_FONT_ERROR_NO_SUPPORT_GLYPH && glyphCode == 0); + Require(sceFontGetCharGlyphCode(font, 0x80010001u, &glyphCode) == SCE_FONT_ERROR_NO_SUPPORT_CODE && glyphCode == 0); glyphCode = 1; Require(sceFontGetCharGlyphCode(font, 'B', &glyphCode) == SCE_FONT_ERROR_NO_SUPPORT_GLYPH && glyphCode == 0); glyphCode = 1; diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index b75f406bbe..bd2a0098c2 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -5,7 +5,7 @@ - [sceFontSetTypographicDesign and sceFontGetTypographicDesign](../../../core/libs/prx/libSceFont/src/Style.cpp): signatures are confirmed by the supplied PS5 library. Native feature ranges, defaults and driver-dependent availability are not implemented; the host still accepts arbitrary values and stores them without affecting shaping or rendering. - [sceFontSetScriptLanguage and sceFontGetScriptLanguage](../../../core/libs/prx/libSceFont/src/Style.cpp): native script/language ranges, handle-error precedence and clearing of failed getter outputs are implemented. Valid driver queries and updates throw; the previous arbitrary-value map and fabricated zero default are no longer used. Driver selection and actual language-dependent shaping remain unimplemented. - [sceFontGetRenderScaledKerning](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale -- [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp) (libSceFont) - signatures taken from the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and matching the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings, not confirmed on a title. The count is the face's glyph count and the glyph code its glyph index, as the library's driver reports them; a code with no glyph returns `NO_SUPPORT_GLYPH` and code 0 `NO_SUPPORT_CODE`, as `sceFontRenderCharGlyphImage` does. The argument checks and the cleared output follow `sceFontGetPixelResolution` +- [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp): signatures, error precedence, failed-output clearing and external-font glyph-code encoding are confirmed by the PS5 library. Multi-face system-font glyph counts, face-selection bits and typographic glyph substitutions are not modeled. - [sceFontGetFontResolution](../../../core/libs/prx/libSceFont/src/Open.cpp): the 32-bit resolution output, float scale and nullable outputs are confirmed by the PS5 library and FontFt driver. Native system font sets return resolution 1000; the host still uses its selected face resolution because multi-face system font sets are not modeled. - [Writing-line ABI and allocation](../../../core/libs/prx/libSceFont/src/WritingLine.cpp): PS5 layouts, creation forms, memory validation, allocator callbacks and error handling are recovered. The 2208-byte line and 64-byte orders use caller memory. Host registry bookkeeping still allocates separately; memory lifetime and concurrent destruction require caller synchronization. - [sceFontWritingRefersRenderStepCharacter](../../../core/libs/prx/libSceFont/src/Text.cpp) (libSceFont) - the second argument is an out pointer to the writing's letter step, as in [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/d2413fc2ebd91d1b7234197c2dd5bd48e1cb6a51/src/libs/libFont.cpp#L1882-L1896) and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Font/SceFont.Engine.cs) bindings. Measured on PS4 firmware 11.00 `libSceFont.sprx` (not on a PS5): exactly 8 bytes are written, the address of a step stored inside the writing, and a null writing writes null; shadPS4 and prosper treat the argument as a struct, which the measurement contradicts. The letter step lives in the writing, so the pointer stays valid as long as the writing does, and its contents are rebuilt by each call of this function. On the PS4 library repeated calls on one step walk the characters it covers and then return null; here every call returns the step's one character, so the second call differs. Assumed: each step covers one character and one glyph. PPSA06699 (Hot Shots Golf 2) dereferences the returned pointer for its character counts. From 2ee602e66f18bd52f1b81636eb4485d73705ccaf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:44:06 +0300 Subject: [PATCH 487/523] fix(ssl): restore alpn clearing and validation --- core/libs/prx/libSceSsl/Export.cpp | 22 ++++++++++++++++++---- core/libs/tests/GuestSsl.cpp | 14 +++++++++++--- docs/dev/debt/libSceSsl.md | 2 +- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 4f697ed914..874dad4779 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -1040,7 +1040,9 @@ const char* APS5_VABI sceSslGetAlpnSelected(int sslConnectionId) { std::shared_ptr session; { std::lock_guard lock(State().mutex); - session = RequireConnection(__func__, sslConnectionId).session; + const auto* connection = FindConnection(sslConnectionId); + if (!connection) return nullptr; + session = connection->session; } if (!session) return nullptr; std::lock_guard sessionLock(session->mutex); @@ -1049,12 +1051,24 @@ const char* APS5_VABI sceSslGetAlpnSelected(int sslConnectionId) { } int APS5_VABI sceSslSetAlpn(int sslConnectionId, const char* proto) { - if (!proto) throw std::invalid_argument("sceSslSetAlpn: null protocol list"); std::lock_guard lock(State().mutex); auto* connection = FindConnection(sslConnectionId); if (!connection) return ErrorInvalidId; - if (!*proto) return ErrorBadLength; - connection->alpn = proto; + connection->alpn.clear(); + if (!proto) return 0; + std::size_t length = 0; + while (length < 65535 && proto[length] != 0) ++length; + if (length >= 65535) return ErrorBadLength; + std::size_t tokenLength = 0; + for (std::size_t index = 0; index <= length; ++index) { + if (index == length || proto[index] == ',') { + if (tokenLength == 0 || tokenLength > 255) return ErrorBadLength; + tokenLength = 0; + } else { + ++tokenLength; + } + } + connection->alpn.assign(proto, length); return 0; } diff --git a/core/libs/tests/GuestSsl.cpp b/core/libs/tests/GuestSsl.cpp index 4becc68d8c..12c079d7eb 100644 --- a/core/libs/tests/GuestSsl.cpp +++ b/core/libs/tests/GuestSsl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { int APS5_VABI sceSslInit_nid_postfix(std::size_t); @@ -129,7 +130,14 @@ int main() { Require(sceSslGetAlpnSelected(connection) == nullptr); Require(sceSslSetAlpn(connection, "http/1.1") == 0); Require(sceSslSetAlpn(connection, "") == badLength); - Require(Throws([&] { sceSslSetAlpn(connection, nullptr); })); + Require(sceSslSetAlpn(connection, ",h2") == badLength); + Require(sceSslSetAlpn(connection, "h2,") == badLength); + Require(sceSslSetAlpn(connection, "h2,,http/1.1") == badLength); + Require(sceSslSetAlpn(connection, std::string(255, 'a').c_str()) == 0); + Require(sceSslSetAlpn(connection, std::string(256, 'a').c_str()) == badLength); + Require(sceSslSetAlpn(connection, std::string(65535, 'a').c_str()) == badLength); + Require(sceSslSetAlpn(0, nullptr) == invalidId); + Require(sceSslSetAlpn(connection, nullptr) == 0); Require(sceSslGetAlpnSelected(connection) == nullptr); char buffer[4]{}; Require(sceSslConnect(connection) == badDescriptor); @@ -144,7 +152,7 @@ int main() { Require(sceSslDisableVerifyOption(unknown, serverVerify) == invalidId); Require(sceSslSetMinSslVersion(unknown, 5) == invalidId); Require(sceSslSetAlpn(unknown, "http/1.1") == invalidId); - Require(Throws([&] { sceSslGetAlpnSelected(unknown); })); + Require(sceSslGetAlpnSelected(unknown) == nullptr); Require(sceSslConnect(unknown) == invalidId); Require(sceSslRead(unknown, buffer, sizeof(buffer), 0) == invalidId); Require(sceSslWrite(unknown, buffer, sizeof(buffer), 0) == invalidId); @@ -158,7 +166,7 @@ int main() { Require(sceSslDisableVerifyOption(connection, serverVerify) == invalidId); Require(sceSslSetMinSslVersion(connection, 5) == invalidId); Require(sceSslSetAlpn(connection, "http/1.1") == invalidId); - Require(Throws([&] { sceSslGetAlpnSelected(connection); })); + Require(sceSslGetAlpnSelected(connection) == nullptr); Require(sceSslConnect(connection) == invalidId); Require(sceSslClose(connection) == invalidId); Require(sceSslCreateConnection(context, 0, "example.com") == invalidId); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 71ef1ff74e..f704fcef79 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -10,5 +10,5 @@ - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown - [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. `sceSslGetSerialNumber` copies into a caller-allocated output buffer or queries the required length when the buffer is null or capacity is zero; a buffer smaller than the serial number returns `SSL_ERROR_INVALID_ARG` (0x8095177a). Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. An unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. The error codes, the zero-capacity length query, the NUL-terminated truncation of name entry strings, and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate -- [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list throws (not measured). `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and throw from `sceSslGetAlpnSelected`, whose console return was not measured +- [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1–255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - static inspection of PPSA28002 confirms the context-first four-argument call with a caller-owned buffer and an in/out 64-bit length. Mbed TLS serializes PEM including its trailing NUL. Buffers smaller than the required encoding throw; an unknown context or foreign or expired certificate handle throws. The exact console PEM length policy, error precedence and truncation behavior have not been measured on a PS5. Existing serial/name, validity and fingerprint readers and their documented uncertainties remain unchanged From 83f1f63e85ba8451c77723737d4cbcd50676ece9 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:46:06 +0300 Subject: [PATCH 488/523] fix(ssl): restore serial number truncation and errors --- core/libs/prx/libSceSsl/Export.cpp | 16 +++++++--------- core/libs/tests/GuestSslLoad.cpp | 15 ++++++++------- core/libs/tests/GuestSslPs5Readers.cpp | 4 ++-- docs/dev/debt/libSceSsl.md | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 874dad4779..cb6bf5ad97 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -991,19 +991,17 @@ int APS5_VABI sceSslClose(int sslConnectionId) { } int APS5_VABI sceSslGetSerialNumber(int sslCtxId, void* sslCert, unsigned char* sboData, std::size_t* sboLen) { - if (!sboLen) return ErrorInvalidArg; std::lock_guard lock(State().mutex); + if (!State().contexts.contains(sslCtxId)) return ErrorInvalidId; + if (!sslCert) return ErrorInvalidValue; const auto* crt = RequireCertificate(__func__, sslCtxId, sslCert); - const auto reqLen = crt->serial.len; - if (!sboData || *sboLen == 0) { - *sboLen = reqLen; + if (!sboData) { + if (sboLen) *sboLen = crt->serial.len; return 0; } - if (*sboLen < reqLen) { - *sboLen = reqLen; - return ErrorInvalidArg; - } - std::memcpy(sboData, crt->serial.p, reqLen); + if (!sboLen) throw std::invalid_argument("sceSslGetSerialNumber: output buffer requires a length pointer"); + const auto copyLength = std::min(*sboLen, crt->serial.len); + if (copyLength != 0) std::memcpy(sboData, crt->serial.p, copyLength); return 0; } diff --git a/core/libs/tests/GuestSslLoad.cpp b/core/libs/tests/GuestSslLoad.cpp index 28b894ddb4..30a01dddce 100644 --- a/core/libs/tests/GuestSslLoad.cpp +++ b/core/libs/tests/GuestSslLoad.cpp @@ -182,20 +182,21 @@ void CertificateReaders() { Check(sceSslGetSerialNumber(id, cert, nullptr, &querySize) == 0 && querySize == 3, "query serial size failed"); querySize = 0; unsigned char shortBuf[2]{}; - Check(sceSslGetSerialNumber(id, cert, shortBuf, &querySize) == 0 && querySize == 3, "zero-len serial query failed"); + Check(sceSslGetSerialNumber(id, cert, shortBuf, &querySize) == 0 && querySize == 0, "zero-len serial copy changed length"); querySize = sizeof(shortBuf); - Check(sceSslGetSerialNumber(id, cert, shortBuf, &querySize) == static_cast(0x8095177a) && querySize == 3, - "short serial buffer accepted"); + Check(sceSslGetSerialNumber(id, cert, shortBuf, &querySize) == 0 && querySize == 2 && shortBuf[0] == 0 && shortBuf[1] == 0x80, + "short serial copy mismatch"); unsigned char serialData[8]{}; std::size_t serialLen = sizeof(serialData); Check(sceSslGetSerialNumber(id, cert, serialData, &serialLen) == 0, "get serial failed"); Check(serialLen == sizeof(serialData) && serialData[3] == 0, "a large enough serial buffer changed the caller's length"); Check(serialData[0] == 0x00 && serialData[1] == 0x80 && serialData[2] == 0xa1, "serial bytes mismatch"); - Check(sceSslGetSerialNumber(id, cert, serialData, nullptr) == static_cast(0x8095177a), "null serial length accepted"); - Throws([&] { sceSslGetSerialNumber(id, nullptr, serialData, &serialLen); }); + Throws([&] { sceSslGetSerialNumber(id, cert, serialData, nullptr); }); + Check(sceSslGetSerialNumber(id, cert, nullptr, nullptr) == 0, "empty serial query failed"); + Check(sceSslGetSerialNumber(id, nullptr, serialData, &serialLen) == static_cast(0x8095f007), "null certificate error mismatch"); void* fake = &serialLen; Throws([&] { sceSslGetSerialNumber(id, fake, serialData, &serialLen); }); - Throws([&] { sceSslGetSerialNumber(9999, cert, serialData, &serialLen); }); + Check(sceSslGetSerialNumber(9999, cert, serialData, &serialLen) == InvalidId, "unknown context error mismatch"); void* subject = sceSslGetSubjectName(id, cert); Check(subject != nullptr, "get subject failed"); @@ -301,7 +302,7 @@ void CertificateReaders() { Check(sceSslFreeCaList(id, &handles) == 0, "free ca list failed"); Check(sceSslTerm_nid_postfix(id) == 0, "term failed"); - Throws([&] { sceSslGetSerialNumber(id, cert, serialData, &serialLen); }); + Check(sceSslGetSerialNumber(id, cert, serialData, &serialLen) == InvalidId, "terminated context error mismatch"); Throws([&] { sceSslGetSubjectName(id, cert); }); Throws([&] { sceSslGetIssuerName(id, cert); }); Throws([&] { sceSslGetNotBefore(id, cert, &begin); }); diff --git a/core/libs/tests/GuestSslPs5Readers.cpp b/core/libs/tests/GuestSslPs5Readers.cpp index caffe1cf3e..17d445a3dd 100644 --- a/core/libs/tests/GuestSslPs5Readers.cpp +++ b/core/libs/tests/GuestSslPs5Readers.cpp @@ -55,8 +55,8 @@ void Readers() { Check(serial == std::array{0x00, 0x80, 0xa1, 0xa5, 0xa5}, "serial copy changed its bytes or guards"); serial.fill(0xa5); length = 2; - Check(sceSslGetSerialNumber(context, certificate, serial.data(), &length) == static_cast(0x8095177a) && length == 3, "short serial buffer did not report its required size"); - Check(std::all_of(serial.begin(), serial.end(), [](auto byte) { return byte == 0xa5; }), "short serial buffer was modified"); + Check(sceSslGetSerialNumber(context, certificate, serial.data(), &length) == 0 && length == 2, "short serial copy changed length"); + Check(serial == std::array{0x00, 0x80, 0xa5, 0xa5, 0xa5}, "short serial copy changed guard bytes"); Throws([&] { sceSslGetSerialNumber(other, certificate, nullptr, &length); }); auto* subject = sceSslGetSubjectName(context, certificate); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index f704fcef79..280c7e4589 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -8,7 +8,7 @@ - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown -- [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. `sceSslGetSerialNumber` copies into a caller-allocated output buffer or queries the required length when the buffer is null or capacity is zero; a buffer smaller than the serial number returns `SSL_ERROR_INVALID_ARG` (0x8095177a). Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. An unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. The error codes, the zero-capacity length query, the NUL-terminated truncation of name entry strings, and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 +- [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. Native `sceSslGetSerialNumber` copies at most the caller capacity without changing it and queries the required length only when the buffer is null. Unknown contexts return `SSL_ERROR_INVALID_ID`; null certificates return `SSL_ERROR_INVALID_VALUE`. A non-null buffer with a null length would cause a native unchecked read; the host throws. Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. Except for the recovered serial-reader context check, an unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. The remaining readers' error codes, the NUL-terminated truncation of name entry strings, and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate - [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1–255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - static inspection of PPSA28002 confirms the context-first four-argument call with a caller-owned buffer and an in/out 64-bit length. Mbed TLS serializes PEM including its trailing NUL. Buffers smaller than the required encoding throw; an unknown context or foreign or expired certificate handle throws. The exact console PEM length policy, error precedence and truncation behavior have not been measured on a PS5. Existing serial/name, validity and fingerprint readers and their documented uncertainties remain unchanged From 060a3b6bf9cb1e1aff8f0cf869bb4f4cd25c301f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:47:09 +0300 Subject: [PATCH 489/523] fix(ssl): restore certificate name copy bounds and errors --- core/libs/prx/libSceSsl/Export.cpp | 27 ++++++++++++-------------- core/libs/tests/GuestSslLoad.cpp | 27 +++++++++++++++++--------- core/libs/tests/GuestSslPs5Readers.cpp | 2 +- docs/dev/debt/libSceSsl.md | 4 ++-- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index cb6bf5ad97..62fff3dcc2 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -1084,37 +1084,34 @@ void* APS5_VABI sceSslGetIssuerName(int sslCtxId, void* sslCert) { } int APS5_VABI sceSslGetNameEntryCount(int sslCtxId, void* certName) { - if (!certName) return ErrorInvalidArg; auto& state = State(); std::lock_guard lock(state.mutex); + if (!state.contexts.contains(sslCtxId)) return ErrorInvalidId; + if (!certName) return ErrorInvalidValue; auto& context = GetContext(sslCtxId); const auto found = context.certNames.find(certName); if (found == context.certNames.end()) throw std::invalid_argument("sceSslGetNameEntryCount: unknown cert name"); return static_cast(found->second->entries.size()); } -int APS5_VABI sceSslGetNameEntryInfo(int sslCtxId, void* certName, int entryNum, char* oidname, std::size_t maxOidnameLen, - char* value, std::size_t maxValueLen, std::size_t* valueLen) { - if (!certName) return ErrorInvalidArg; +int APS5_VABI sceSslGetNameEntryInfo(int sslCtxId, void* certName, int entryNum, char* oidname, std::size_t maxOidnameLen, char* value, std::size_t maxValueLen, std::size_t* valueLen) { auto& state = State(); std::lock_guard lock(state.mutex); + if (!state.contexts.contains(sslCtxId)) return ErrorInvalidId; + if (!certName || !oidname || !value || !valueLen) return ErrorInvalidValue; auto& context = GetContext(sslCtxId); const auto found = context.certNames.find(certName); if (found == context.certNames.end()) throw std::invalid_argument("sceSslGetNameEntryInfo: unknown cert name"); const auto& entries = found->second->entries; - if (entryNum < 0 || static_cast(entryNum) >= entries.size()) return ErrorInvalidArg; + if (entryNum < 0 || static_cast(entryNum) >= entries.size()) return ErrorInvalidValue; const auto& entry = entries[static_cast(entryNum)]; - if (oidname && maxOidnameLen > 0) { - const auto copyLen = std::min(entry.oid.size(), maxOidnameLen - 1); - std::memcpy(oidname, entry.oid.data(), copyLen); - oidname[copyLen] = '\0'; + const auto copyLength = std::min(entry.value.size(), maxValueLen); + if (copyLength != 0) std::memcpy(value, entry.value.data(), copyLength); + *valueLen = copyLength; + if (maxOidnameLen != 0) { + std::memset(oidname, 0, maxOidnameLen); + std::memcpy(oidname, entry.oid.data(), std::min(entry.oid.size(), maxOidnameLen)); } - if (value && maxValueLen > 0) { - const auto copyLen = std::min(entry.value.size(), maxValueLen - 1); - std::memcpy(value, entry.value.data(), copyLen); - value[copyLen] = '\0'; - } - if (valueLen) *valueLen = entry.value.size(); return 0; } diff --git a/core/libs/tests/GuestSslLoad.cpp b/core/libs/tests/GuestSslLoad.cpp index 30a01dddce..be75ff9102 100644 --- a/core/libs/tests/GuestSslLoad.cpp +++ b/core/libs/tests/GuestSslLoad.cpp @@ -211,9 +211,9 @@ void CertificateReaders() { Check(sceSslGetNameEntryCount(id, subject) == 3, "subject entry count mismatch"); Check(sceSslGetNameEntryCount(id, issuer) == 3, "issuer entry count mismatch"); - Check(sceSslGetNameEntryCount(id, nullptr) == static_cast(0x8095177a), "null cert name count accepted"); + Check(sceSslGetNameEntryCount(id, nullptr) == static_cast(0x8095f007), "null cert name count accepted"); Throws([&] { sceSslGetNameEntryCount(id, fake); }); - Throws([&] { sceSslGetNameEntryCount(9999, subject); }); + Check(sceSslGetNameEntryCount(9999, subject) == InvalidId, "unknown name context error mismatch"); char oid[32]{}; char val[64]{}; @@ -227,14 +227,23 @@ void CertificateReaders() { char smallVal[4]{}; std::size_t fullLen = 0; - Check(sceSslGetNameEntryInfo(id, subject, 1, nullptr, 0, smallVal, sizeof(smallVal), &fullLen) == 0, "truncated query failed"); - Check(std::strcmp(smallVal, "Any") == 0 && fullLen == 12, "buffer truncation mismatch"); + Check(sceSslGetNameEntryInfo(id, subject, 1, oid, 0, smallVal, sizeof(smallVal), &fullLen) == 0, "truncated query failed"); + Check(std::memcmp(smallVal, "AnyP", 4) == 0 && fullLen == 4, "buffer truncation mismatch"); + char shortOid[5] = {'x', 'x', 'x', 'x', 'x'}; + char shortValue[5] = {'y', 'y', 'y', 'y', 'y'}; + Check(sceSslGetNameEntryInfo(id, subject, 1, shortOid, 4, shortValue, 4, &fullLen) == 0, "bounded entry query failed"); + Check(std::memcmp(shortOid, "orga", 4) == 0 && shortOid[4] == 'x', "oid copy or guard mismatch"); + Check(std::memcmp(shortValue, "AnyP", 4) == 0 && shortValue[4] == 'y' && fullLen == 4, "value copy or guard mismatch"); + Check(sceSslGetNameEntryInfo(id, subject, 1, shortOid, 0, shortValue, 0, &fullLen) == 0 && fullLen == 0, "zero capacity query failed"); + Check(sceSslGetNameEntryInfo(id, subject, 1, nullptr, 0, shortValue, 0, &fullLen) == static_cast(0x8095f007), "null oid accepted"); + Check(sceSslGetNameEntryInfo(id, subject, 1, shortOid, 0, nullptr, 0, &fullLen) == static_cast(0x8095f007), "null value accepted"); + Check(sceSslGetNameEntryInfo(id, subject, 1, shortOid, 0, shortValue, 0, nullptr) == static_cast(0x8095f007), "null value length accepted"); - Check(sceSslGetNameEntryInfo(id, subject, -1, oid, sizeof(oid), val, sizeof(val), &vlen) == static_cast(0x8095177a), "negative index accepted"); - Check(sceSslGetNameEntryInfo(id, subject, 3, oid, sizeof(oid), val, sizeof(val), &vlen) == static_cast(0x8095177a), "out of bounds index accepted"); - Check(sceSslGetNameEntryInfo(id, nullptr, 0, oid, sizeof(oid), val, sizeof(val), &vlen) == static_cast(0x8095177a), "null certName accepted"); + Check(sceSslGetNameEntryInfo(id, subject, -1, oid, sizeof(oid), val, sizeof(val), &vlen) == static_cast(0x8095f007), "negative index accepted"); + Check(sceSslGetNameEntryInfo(id, subject, 3, oid, sizeof(oid), val, sizeof(val), &vlen) == static_cast(0x8095f007), "out of bounds index accepted"); + Check(sceSslGetNameEntryInfo(id, nullptr, 0, oid, sizeof(oid), val, sizeof(val), &vlen) == static_cast(0x8095f007), "null certName accepted"); Throws([&] { sceSslGetNameEntryInfo(id, fake, 0, oid, sizeof(oid), val, sizeof(val), &vlen); }); - Throws([&] { sceSslGetNameEntryInfo(9999, subject, 0, oid, sizeof(oid), val, sizeof(val), &vlen); }); + Check(sceSslGetNameEntryInfo(9999, subject, 0, oid, sizeof(oid), val, sizeof(val), &vlen) == InvalidId, "unknown entry context error mismatch"); Check(sceSslFreeSslCertName(id, subject) == 0, "free subject name failed"); Check(sceSslFreeSslCertName(id, issuer) == 0, "free issuer name failed"); @@ -327,7 +336,7 @@ void CertificateReaders() { Throws([&] { sceSslGetFingerprint(id2, cert2, 3, sha256); }); Throws([&] { sceSslGetPem(id2, cert2, pemBuf.data(), &writtenLen); }); Check(sceSslTerm_nid_postfix(id2) == 0, "term failed"); - Throws([&] { sceSslGetNameEntryCount(id2, subject2); }); + Check(sceSslGetNameEntryCount(id2, subject2) == InvalidId, "terminated name context error mismatch"); Throws([&] { sceSslFreeSslCertName(id2, subject2); }); } diff --git a/core/libs/tests/GuestSslPs5Readers.cpp b/core/libs/tests/GuestSslPs5Readers.cpp index 17d445a3dd..c6e302579c 100644 --- a/core/libs/tests/GuestSslPs5Readers.cpp +++ b/core/libs/tests/GuestSslPs5Readers.cpp @@ -89,7 +89,7 @@ void Readers() { Check(sceSslFreeSslCertName(context, subject) == 0, "name free failed"); Throws([&] { sceSslGetNameEntryCount(context, subject); }); Check(sceSslTerm_nid_postfix(context) == 0, "context term failed"); - Throws([&] { sceSslGetNameEntryCount(context, issuer); }); + Check(sceSslGetNameEntryCount(context, issuer) == static_cast(0x8095f006), "terminated context count error mismatch"); Check(sceSslTerm_nid_postfix(other) == 0, "other context term failed"); } diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 280c7e4589..18e5b1ac6e 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -8,7 +8,7 @@ - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown -- [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. Native `sceSslGetSerialNumber` copies at most the caller capacity without changing it and queries the required length only when the buffer is null. Unknown contexts return `SSL_ERROR_INVALID_ID`; null certificates return `SSL_ERROR_INVALID_VALUE`. A non-null buffer with a null length would cause a native unchecked read; the host throws. Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. Except for the recovered serial-reader context check, an unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. The remaining readers' error codes, the NUL-terminated truncation of name entry strings, and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 +- [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. Native `sceSslGetSerialNumber` copies at most the caller capacity without changing it and queries the required length only when the buffer is null. Unknown contexts return `SSL_ERROR_INVALID_ID`; null certificates return `SSL_ERROR_INVALID_VALUE`. A non-null buffer with a null length would cause a native unchecked read; the host throws. Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. Except for the recovered serial and name-reader context checks, an unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. Native name readers require a valid context and non-null name and output pointers; invalid indices return `SSL_ERROR_INVALID_VALUE`. Entry values copy up to capacity without adding a terminator and report the copied length. OID names are zero-padded when shorter than capacity and can fill it without a terminator. The remaining readers' error codes and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate -- [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1–255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module +- [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1-255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - static inspection of PPSA28002 confirms the context-first four-argument call with a caller-owned buffer and an in/out 64-bit length. Mbed TLS serializes PEM including its trailing NUL. Buffers smaller than the required encoding throw; an unknown context or foreign or expired certificate handle throws. The exact console PEM length policy, error precedence and truncation behavior have not been measured on a PS5. Existing serial/name, validity and fingerprint readers and their documented uncertainties remain unchanged From e37638505d17311c5d5e87297d6a9651b03c2a49 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:48:46 +0300 Subject: [PATCH 490/523] fix(ssl): restore native pem encoding and buffer semantics --- core/libs/prx/libSceSsl/Export.cpp | 37 +++++++++++++++----------- core/libs/tests/GuestSslLoad.cpp | 12 ++++----- core/libs/tests/GuestSslPs5Readers.cpp | 16 +++++++---- docs/dev/debt/libSceSsl.md | 2 +- 4 files changed, 39 insertions(+), 28 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 62fff3dcc2..4c22b2f5b4 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -1152,25 +1152,30 @@ int APS5_VABI sceSslGetFingerprint(int sslCtxId, void* sslCert, int hashType, un } int APS5_VABI sceSslGetPem(int sslCtxId, void* sslCert, char* buf, std::size_t* bufLen) { - if (!bufLen) return ErrorInvalidArg; + if (!sslCert || !bufLen) return ErrorInvalidValue; std::lock_guard lock(State().mutex); - auto& context = GetContext(sslCtxId); + if (!State().contexts.contains(sslCtxId)) return ErrorInvalidId; const auto* crt = RequireCertificate(__func__, sslCtxId, sslCert); - PoolScope scope(context.pool); - std::size_t required = 0; - const auto encode = [&](unsigned char* output, std::size_t capacity, std::size_t* written) { - return mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE-----\n", "-----END CERTIFICATE-----\n", crt->raw.p, crt->raw.len, output, capacity, written); - }; - const int sizing = encode(nullptr, 0, &required); - if (sizing != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) CheckBackend(sizing, "SSL PEM sizing failed"); - if (!buf) { - *bufLen = required; - return 0; + const std::size_t encodedLength = Add(crt->raw.len, 2) / 3 * 4; + const std::size_t lineCount = Add(encodedLength, 63) / 64; + const std::size_t required = Add(57, Add(encodedLength, lineCount * 2)); + const std::size_t capacity = *bufLen; + if (buf && capacity != 0) std::memset(buf, 0, capacity); + *bufLen = required; + if (!buf) return 0; + if (capacity < required) return static_cast(0x8095f008); + constexpr char header[] = "-----BEGIN CERTIFICATE-----\r\n"; + constexpr char footer[] = "-----END CERTIFICATE-----\r\n"; + std::memcpy(buf, header, sizeof(header) - 1); + std::size_t offset = sizeof(header) - 1; + for (std::size_t position = 0; position < crt->raw.len; position += 48) { + std::size_t written = 0; + CheckBackend(mbedtls_base64_encode(reinterpret_cast(buf + offset), capacity - offset, &written, crt->raw.p + position, std::min(48, crt->raw.len - position)), "SSL PEM encoding failed"); + offset += written; + buf[offset++] = '\r'; + buf[offset++] = '\n'; } - if (*bufLen < required) throw std::length_error("SSL PEM buffer is too small"); - std::size_t written = 0; - CheckBackend(encode(reinterpret_cast(buf), required, &written), "SSL PEM encoding failed"); - *bufLen = written; + std::memcpy(buf + offset, footer, sizeof(footer)); return 0; } diff --git a/core/libs/tests/GuestSslLoad.cpp b/core/libs/tests/GuestSslLoad.cpp index be75ff9102..a0b79105d1 100644 --- a/core/libs/tests/GuestSslLoad.cpp +++ b/core/libs/tests/GuestSslLoad.cpp @@ -297,14 +297,14 @@ void CertificateReaders() { std::size_t writtenLen = pemBuf.size(); Check(sceSslGetPem(id, cert, pemBuf.data(), &writtenLen) == 0, "write pem failed"); Check(writtenLen == pemLen, "written pem length mismatch"); - Check(std::strstr(pemBuf.data(), "-----BEGIN CERTIFICATE-----\n") != nullptr, "pem header missing"); - Check(std::strstr(pemBuf.data(), "-----END CERTIFICATE-----\n") != nullptr, "pem footer missing"); + Check(std::strstr(pemBuf.data(), "-----BEGIN CERTIFICATE-----\r\n") != nullptr, "pem header missing"); + Check(std::strstr(pemBuf.data(), "-----END CERTIFICATE-----\r\n") != nullptr, "pem footer missing"); char smallPem[8]{}; std::size_t smallPemLen = sizeof(smallPem); - Throws([&] { sceSslGetPem(id, cert, smallPem, &smallPemLen); }); - Check(sceSslGetPem(id, cert, pemBuf.data(), nullptr) == static_cast(0x8095177a), "null pem len accepted"); - Throws([&] { sceSslGetPem(id, nullptr, pemBuf.data(), &writtenLen); }); + Check(sceSslGetPem(id, cert, smallPem, &smallPemLen) == static_cast(0x8095f008) && smallPemLen == pemLen, "short PEM size error mismatch"); + Check(sceSslGetPem(id, cert, pemBuf.data(), nullptr) == static_cast(0x8095f007), "null pem len accepted"); + Check(sceSslGetPem(id, nullptr, pemBuf.data(), &writtenLen) == static_cast(0x8095f007), "null PEM certificate error mismatch"); Throws([&] { sceSslGetPem(id, fake, pemBuf.data(), &writtenLen); }); Check(sceSslClose(1) == InvalidId, "closing an unknown connection did not report an invalid id"); @@ -317,7 +317,7 @@ void CertificateReaders() { Throws([&] { sceSslGetNotBefore(id, cert, &begin); }); Throws([&] { sceSslGetNotAfter(id, cert, &limit); }); Throws([&] { sceSslGetFingerprint(id, cert, 3, sha256); }); - Throws([&] { sceSslGetPem(id, cert, pemBuf.data(), &writtenLen); }); + Check(sceSslGetPem(id, cert, pemBuf.data(), &writtenLen) == InvalidId, "terminated PEM context error mismatch"); const int id2 = sceSslInit_nid_postfix(1024 * 1024); Check(sceSslLoadCert(id2, 1, &pointer, nullptr, nullptr) == 0, "CA load failed"); diff --git a/core/libs/tests/GuestSslPs5Readers.cpp b/core/libs/tests/GuestSslPs5Readers.cpp index c6e302579c..a9feda8d5a 100644 --- a/core/libs/tests/GuestSslPs5Readers.cpp +++ b/core/libs/tests/GuestSslPs5Readers.cpp @@ -9,6 +9,7 @@ #include #include #include +#include extern "C" { int APS5_VABI sceSslInit_nid_postfix(std::uint64_t); @@ -71,15 +72,20 @@ void Readers() { Check(sceSslGetNameEntryInfo(context, subject, 1, oid.data(), oid.size(), value.data(), value.size(), &length) == 0, "PS5 eight-argument name query failed"); Check(std::strcmp(oid.data(), "organizationName") == 0 && std::strcmp(value.data(), "AnyPS5 Tests") == 0 && length == 12, "name output or full-width length changed"); + std::string expectedPem; + for (const char byte : std::string(RootPem)) { + if (byte == '\n') expectedPem += '\r'; + expectedPem += byte; + } length = SIZE_MAX; - Check(sceSslGetPem(context, certificate, nullptr, &length) == 0 && length == std::strlen(RootPem) + 1, "PS5 PEM size query failed"); + Check(sceSslGetPem(context, certificate, nullptr, &length) == 0 && length == expectedPem.size() + 1, "PS5 PEM size query failed"); std::vector pem(length + 2, static_cast(0x5a)); - Check(sceSslGetPem(context, certificate, pem.data(), &length) == 0 && length == std::strlen(RootPem) + 1, "PS5 PEM copy failed"); - Check(std::strcmp(pem.data(), RootPem) == 0 && pem[length] == 0x5a && pem[length + 1] == 0x5a, "PEM copy changed bytes or guards"); + Check(sceSslGetPem(context, certificate, pem.data(), &length) == 0 && length == expectedPem.size() + 1, "PS5 PEM copy failed"); + Check(std::strcmp(pem.data(), expectedPem.c_str()) == 0 && pem[length] == 0x5a && pem[length + 1] == 0x5a, "PEM copy changed bytes or guards"); std::array small{'x', 'x', 'x', 'x'}; length = 2; - Throws([&] { sceSslGetPem(context, certificate, small.data(), &length); }); - Check(std::all_of(small.begin(), small.end(), [](char byte) { return byte == 'x'; }), "short PEM buffer was modified"); + Check(sceSslGetPem(context, certificate, small.data(), &length) == static_cast(0x8095f008) && length == expectedPem.size() + 1, "short PEM error or size mismatch"); + Check(small == std::array{0, 0, 'x', 'x'}, "short PEM clearing or guards mismatch"); Throws([&] { sceSslGetPem(other, certificate, nullptr, &length); }); Check(sceSslUnloadCert(context) == 0, "unload failed"); Check(sceSslGetSerialNumber(context, certificate, nullptr, &length) == 0 && length == 3, "CA snapshot lost its certificate on unload"); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 18e5b1ac6e..bec37f0eab 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -11,4 +11,4 @@ - [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. Native `sceSslGetSerialNumber` copies at most the caller capacity without changing it and queries the required length only when the buffer is null. Unknown contexts return `SSL_ERROR_INVALID_ID`; null certificates return `SSL_ERROR_INVALID_VALUE`. A non-null buffer with a null length would cause a native unchecked read; the host throws. Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. Except for the recovered serial and name-reader context checks, an unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. Native name readers require a valid context and non-null name and output pointers; invalid indices return `SSL_ERROR_INVALID_VALUE`. Entry values copy up to capacity without adding a terminator and report the copied length. OID names are zero-padded when shorter than capacity and can fill it without a terminator. The remaining readers' error codes and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate - [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1-255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module -- [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - static inspection of PPSA28002 confirms the context-first four-argument call with a caller-owned buffer and an in/out 64-bit length. Mbed TLS serializes PEM including its trailing NUL. Buffers smaller than the required encoding throw; an unknown context or foreign or expired certificate handle throws. The exact console PEM length policy, error precedence and truncation behavior have not been measured on a PS5. Existing serial/name, validity and fingerprint readers and their documented uncertainties remain unchanged +- [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp): native argument validation, CRLF line endings, 64-character Base64 lines, NUL-inclusive length and buffer clearing on insufficient capacity are recovered. Foreign and expired certificate handles throw instead of performing native unchecked pointer access. DER reserialization differences for noncanonical certificates remain unverified. From d33bde8ed37fb91fe868de92e632103a472d10d8 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:49:56 +0300 Subject: [PATCH 491/523] fix(ssl): restore validity and fingerprint validation order --- core/libs/prx/libSceSsl/Export.cpp | 11 +++++++---- core/libs/tests/GuestSslLoad.cpp | 27 +++++++++++++++------------ docs/dev/debt/libSceSsl.md | 2 +- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 4c22b2f5b4..b41820f8b5 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -1116,23 +1116,27 @@ int APS5_VABI sceSslGetNameEntryInfo(int sslCtxId, void* certName, int entryNum, } int APS5_VABI sceSslGetNotBefore(int sslCtxId, void* sslCert, SceSsl::RtcTick* begin) { - if (!begin) return ErrorInvalidArg; std::lock_guard lock(State().mutex); + if (!State().contexts.contains(sslCtxId)) return ErrorInvalidId; + if (!sslCert || !begin) return ErrorInvalidValue; const auto* crt = RequireCertificate(__func__, sslCtxId, sslCert); begin->tick = TimeToTick(crt->valid_from); return 0; } int APS5_VABI sceSslGetNotAfter(int sslCtxId, void* sslCert, SceSsl::RtcTick* limit) { - if (!limit) return ErrorInvalidArg; std::lock_guard lock(State().mutex); + if (!State().contexts.contains(sslCtxId)) return ErrorInvalidId; + if (!sslCert || !limit) return ErrorInvalidValue; const auto* crt = RequireCertificate(__func__, sslCtxId, sslCert); limit->tick = TimeToTick(crt->valid_to); return 0; } int APS5_VABI sceSslGetFingerprint(int sslCtxId, void* sslCert, int hashType, unsigned char* fingerprint) { - if (!fingerprint) return ErrorInvalidArg; + std::lock_guard lock(State().mutex); + if (!State().contexts.contains(sslCtxId)) return ErrorInvalidId; + if (!sslCert || !fingerprint) return ErrorInvalidValue; mbedtls_md_type_t mdType; switch (hashType) { case 0: mdType = MBEDTLS_MD_MD5; break; @@ -1145,7 +1149,6 @@ int APS5_VABI sceSslGetFingerprint(int sslCtxId, void* sslCert, int hashType, un } const auto* info = mbedtls_md_info_from_type(mdType); if (!info) return ErrorInvalidArg; - std::lock_guard lock(State().mutex); const auto* crt = RequireCertificate(__func__, sslCtxId, sslCert); if (mbedtls_md(info, crt->raw.p, crt->raw.len, fingerprint) != 0) return ErrorInvalidArg; return 0; diff --git a/core/libs/tests/GuestSslLoad.cpp b/core/libs/tests/GuestSslLoad.cpp index a0b79105d1..3c29135d77 100644 --- a/core/libs/tests/GuestSslLoad.cpp +++ b/core/libs/tests/GuestSslLoad.cpp @@ -253,20 +253,23 @@ void CertificateReaders() { Throws([&] { sceSslFreeSslCertName(9999, subject); }); SceSsl::RtcTick begin{}; + Check(sceSslGetNotBefore(0, nullptr, nullptr) == InvalidId, "date validation precedence mismatch"); + Check(sceSslGetNotAfter(0, nullptr, nullptr) == InvalidId, "date validation precedence mismatch"); + Check(sceSslGetFingerprint(0, nullptr, -1, nullptr) == InvalidId, "fingerprint validation precedence mismatch"); Check(sceSslGetNotBefore(id, cert, &begin) == 0, "get not before failed"); Check(begin.tick == 63927094442000000ULL, "not before tick mismatch"); - Check(sceSslGetNotBefore(id, cert, nullptr) == static_cast(0x8095177a), "null begin accepted"); - Throws([&] { sceSslGetNotBefore(id, nullptr, &begin); }); + Check(sceSslGetNotBefore(id, cert, nullptr) == static_cast(0x8095f007), "null begin accepted"); + Check(sceSslGetNotBefore(id, nullptr, &begin) == static_cast(0x8095f007), "null certificate date error mismatch"); Throws([&] { sceSslGetNotBefore(id, fake, &begin); }); - Throws([&] { sceSslGetNotBefore(9999, cert, &begin); }); + Check(sceSslGetNotBefore(9999, cert, &begin) == InvalidId, "invalid date context error mismatch"); SceSsl::RtcTick limit{}; Check(sceSslGetNotAfter(id, cert, &limit) == 0, "get not after failed"); Check(limit.tick == 64242454442000000ULL, "not after tick mismatch"); - Check(sceSslGetNotAfter(id, cert, nullptr) == static_cast(0x8095177a), "null limit accepted"); - Throws([&] { sceSslGetNotAfter(id, nullptr, &limit); }); + Check(sceSslGetNotAfter(id, cert, nullptr) == static_cast(0x8095f007), "null limit accepted"); + Check(sceSslGetNotAfter(id, nullptr, &limit) == static_cast(0x8095f007), "null certificate date error mismatch"); Throws([&] { sceSslGetNotAfter(id, fake, &limit); }); - Throws([&] { sceSslGetNotAfter(9999, cert, &limit); }); + Check(sceSslGetNotAfter(9999, cert, &limit) == InvalidId, "invalid date context error mismatch"); unsigned char md5[16]{}; Check(sceSslGetFingerprint(id, cert, 0, md5) == 0, "get md5 fingerprint failed"); @@ -283,12 +286,12 @@ void CertificateReaders() { constexpr unsigned char expectedSha256[32] = {0x3d, 0x7d, 0xac, 0xd3, 0x04, 0x90, 0x42, 0xe1, 0x57, 0x19, 0x87, 0x27, 0x54, 0x45, 0xae, 0xed, 0xc4, 0xea, 0xd6, 0xd4, 0x20, 0xa0, 0x7f, 0xc4, 0xf5, 0x26, 0xc1, 0xa0, 0x85, 0x87, 0x5a, 0x4c}; Check(std::memcmp(sha256, expectedSha256, sizeof(sha256)) == 0, "sha256 digest mismatch"); - Check(sceSslGetFingerprint(id, cert, 3, nullptr) == static_cast(0x8095177a), "null fingerprint buffer accepted"); + Check(sceSslGetFingerprint(id, cert, 3, nullptr) == static_cast(0x8095f007), "null fingerprint buffer accepted"); Check(sceSslGetFingerprint(id, cert, -1, sha256) == static_cast(0x8095177a), "negative hash type accepted"); Check(sceSslGetFingerprint(id, cert, 99, sha256) == static_cast(0x8095177a), "invalid hash type accepted"); - Throws([&] { sceSslGetFingerprint(id, nullptr, 3, sha256); }); + Check(sceSslGetFingerprint(id, nullptr, 3, sha256) == static_cast(0x8095f007), "null fingerprint certificate error mismatch"); Throws([&] { sceSslGetFingerprint(id, fake, 3, sha256); }); - Throws([&] { sceSslGetFingerprint(9999, cert, 3, sha256); }); + Check(sceSslGetFingerprint(9999, cert, 3, sha256) == InvalidId, "invalid fingerprint context error mismatch"); std::size_t pemLen = 0; Check(sceSslGetPem(id, cert, nullptr, &pemLen) == 0, "query pem size failed"); @@ -314,9 +317,9 @@ void CertificateReaders() { Check(sceSslGetSerialNumber(id, cert, serialData, &serialLen) == InvalidId, "terminated context error mismatch"); Throws([&] { sceSslGetSubjectName(id, cert); }); Throws([&] { sceSslGetIssuerName(id, cert); }); - Throws([&] { sceSslGetNotBefore(id, cert, &begin); }); - Throws([&] { sceSslGetNotAfter(id, cert, &limit); }); - Throws([&] { sceSslGetFingerprint(id, cert, 3, sha256); }); + Check(sceSslGetNotBefore(id, cert, &begin) == InvalidId, "invalid date context error mismatch"); + Check(sceSslGetNotAfter(id, cert, &limit) == InvalidId, "invalid date context error mismatch"); + Check(sceSslGetFingerprint(id, cert, 3, sha256) == InvalidId, "invalid fingerprint context error mismatch"); Check(sceSslGetPem(id, cert, pemBuf.data(), &writtenLen) == InvalidId, "terminated PEM context error mismatch"); const int id2 = sceSslInit_nid_postfix(1024 * 1024); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index bec37f0eab..8e7603a4a1 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -8,7 +8,7 @@ - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown -- [sceSslGetSerialNumber, sceSslGetSubjectName, sceSslGetIssuerName, sceSslGetNameEntryCount, sceSslGetNameEntryInfo, sceSslFreeSslCertName, sceSslGetNotBefore, sceSslGetNotAfter, sceSslGetFingerprint](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and context ownership follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L293-L350), with hash types matching its `SceSslFingerprintHashAlgorithm` enum. Operations take a context handle as their first argument and operate on certificate and name handles scoped to that context. Native `sceSslGetSerialNumber` copies at most the caller capacity without changing it and queries the required length only when the buffer is null. Unknown contexts return `SSL_ERROR_INVALID_ID`; null certificates return `SSL_ERROR_INVALID_VALUE`. A non-null buffer with a null length would cause a native unchecked read; the host throws. Mbed TLS parses the certificate's serial number, validity period (converted to microseconds since Gregorian year 0001 epoch), fingerprint digests and X.509 subject and issuer distinguished names, writing the attribute long names the console writes (`commonName`, `organizationName`, `countryName`, measured on 13.40 through the verify callback's chain handles; numeric strings for attributes Mbed TLS cannot name) and value strings with `valueLen` set to the value's length. Except for the recovered serial and name-reader context checks, an unknown context, unknown or expired certificate handle or invalid certificate name handle throws; null output pointers, unsupported hash types or out-of-range entry indices return `SSL_ERROR_INVALID_ARG` (0x8095177a). `sceSslGetSerialNumber` with a buffer large enough leaves `*sboLen` as the caller set it, as measured on 13.40 with a 32-byte buffer. Native name readers require a valid context and non-null name and output pointers; invalid indices return `SSL_ERROR_INVALID_VALUE`. Entry values copy up to capacity without adding a terminator and report the copied length. OID names are zero-padded when shorter than capacity and can fill it without a terminator. The remaining readers' error codes and the lifetime of name handles (released only by `sceSslFreeSslCertName` or when the context is terminated) have not been measured on a PS5 +- [Certificate readers](../../../core/libs/prx/libSceSsl/Export.cpp): foreign and expired certificate/name handles throw instead of following native unchecked pointers. Name handles still use host-owned copies; native ownership and release semantics are not modeled. Numeric names for attributes Mbed TLS cannot identify may differ from native names. Fingerprint stack-space validation and backend digest failures are not reproduced. Signatures, valid-context/null-argument precedence, serial truncation, name copy bounds, validity timestamps and supported hash selectors are confirmed by the supplied module. - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate - [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1-255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp): native argument validation, CRLF line endings, 64-character Base64 lines, NUL-inclusive length and buffer clearing on insufficient capacity are recovered. Foreign and expired certificate handles throw instead of performing native unchecked pointer access. DER reserialization differences for noncanonical certificates remain unverified. From 31925119d51a84e17a8b8b3a249313daec0158f3 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:51:00 +0300 Subject: [PATCH 492/523] fix(ssl): bind certificate name views to certificate lifetime --- core/libs/prx/libSceSsl/Export.cpp | 25 +++++++++++++++---------- core/libs/tests/GuestSslLoad.cpp | 24 +++++++++++++----------- core/libs/tests/GuestSslPs5Readers.cpp | 4 ++-- docs/dev/debt/libSceSsl.md | 2 +- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index b41820f8b5..9d6e172d16 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -222,6 +222,9 @@ struct CertNameEntry { }; struct CertName { + void* certificate = nullptr; + bool subject = false; + std::weak_ptr owner; std::vector entries; }; @@ -582,10 +585,17 @@ const char* LongAttributeName(const char* shortName) { void* ExtractCertName(const char* function, int sslCtxId, void* sslCert, bool subject) { std::lock_guard lock(State().mutex); + if (!sslCert || !State().contexts.contains(sslCtxId)) return nullptr; auto& context = GetContext(sslCtxId); const auto* crt = RequireCertificate(function, sslCtxId, sslCert); + for (const auto& [handle, stored] : context.certNames) { + if (stored->certificate == sslCert && stored->subject == subject) return handle; + } const auto* name = subject ? &crt->subject : &crt->issuer; auto certName = std::make_unique(); + certName->certificate = sslCert; + certName->subject = subject; + certName->owner = static_cast(sslCert)->owner; for (const auto* node = name; node; node = node->next) { if (!node->oid.p || node->oid.len == 0) continue; std::string oid; @@ -1006,14 +1016,9 @@ int APS5_VABI sceSslGetSerialNumber(int sslCtxId, void* sslCert, unsigned char* } int APS5_VABI sceSslFreeSslCertName(int sslCtxId, void* certName) { - if (!certName) return ErrorInvalidArg; - auto& state = State(); - std::lock_guard lock(state.mutex); - auto& context = GetContext(sslCtxId); - const auto found = context.certNames.find(certName); - if (found == context.certNames.end()) throw std::invalid_argument("sceSslFreeSslCertName: unknown cert name"); - context.certNames.erase(found); - return 0; + std::lock_guard lock(State().mutex); + if (!State().contexts.contains(sslCtxId)) return ErrorInvalidId; + return certName ? 0 : ErrorInvalidValue; } int APS5_VABI sceSslEnableVerifyOption(int sslConnectionId, std::uint32_t option) { @@ -1090,7 +1095,7 @@ int APS5_VABI sceSslGetNameEntryCount(int sslCtxId, void* certName) { if (!certName) return ErrorInvalidValue; auto& context = GetContext(sslCtxId); const auto found = context.certNames.find(certName); - if (found == context.certNames.end()) throw std::invalid_argument("sceSslGetNameEntryCount: unknown cert name"); + if (found == context.certNames.end() || found->second->owner.expired()) throw std::invalid_argument("sceSslGetNameEntryCount: unknown or expired cert name"); return static_cast(found->second->entries.size()); } @@ -1101,7 +1106,7 @@ int APS5_VABI sceSslGetNameEntryInfo(int sslCtxId, void* certName, int entryNum, if (!certName || !oidname || !value || !valueLen) return ErrorInvalidValue; auto& context = GetContext(sslCtxId); const auto found = context.certNames.find(certName); - if (found == context.certNames.end()) throw std::invalid_argument("sceSslGetNameEntryInfo: unknown cert name"); + if (found == context.certNames.end() || found->second->owner.expired()) throw std::invalid_argument("sceSslGetNameEntryInfo: unknown or expired cert name"); const auto& entries = found->second->entries; if (entryNum < 0 || static_cast(entryNum) >= entries.size()) return ErrorInvalidValue; const auto& entry = entries[static_cast(entryNum)]; diff --git a/core/libs/tests/GuestSslLoad.cpp b/core/libs/tests/GuestSslLoad.cpp index 3c29135d77..904b4764a7 100644 --- a/core/libs/tests/GuestSslLoad.cpp +++ b/core/libs/tests/GuestSslLoad.cpp @@ -200,14 +200,15 @@ void CertificateReaders() { void* subject = sceSslGetSubjectName(id, cert); Check(subject != nullptr, "get subject failed"); + Check(sceSslGetSubjectName(id, cert) == subject, "subject handle identity changed"); void* issuer = sceSslGetIssuerName(id, cert); Check(issuer != nullptr, "get issuer failed"); - Throws([&] { sceSslGetSubjectName(id, nullptr); }); - Throws([&] { sceSslGetIssuerName(id, nullptr); }); + Check(sceSslGetSubjectName(id, nullptr) == nullptr, "invalid name query returned a handle"); + Check(sceSslGetIssuerName(id, nullptr) == nullptr, "invalid name query returned a handle"); Throws([&] { sceSslGetSubjectName(id, fake); }); Throws([&] { sceSslGetIssuerName(id, fake); }); - Throws([&] { sceSslGetSubjectName(9999, cert); }); - Throws([&] { sceSslGetIssuerName(9999, cert); }); + Check(sceSslGetSubjectName(9999, cert) == nullptr, "invalid name query returned a handle"); + Check(sceSslGetIssuerName(9999, cert) == nullptr, "invalid name query returned a handle"); Check(sceSslGetNameEntryCount(id, subject) == 3, "subject entry count mismatch"); Check(sceSslGetNameEntryCount(id, issuer) == 3, "issuer entry count mismatch"); @@ -247,10 +248,11 @@ void CertificateReaders() { Check(sceSslFreeSslCertName(id, subject) == 0, "free subject name failed"); Check(sceSslFreeSslCertName(id, issuer) == 0, "free issuer name failed"); - Check(sceSslFreeSslCertName(id, nullptr) == static_cast(0x8095177a), "null cert name free accepted"); - Throws([&] { sceSslFreeSslCertName(id, subject); }); - Throws([&] { sceSslFreeSslCertName(id, fake); }); - Throws([&] { sceSslFreeSslCertName(9999, subject); }); + Check(sceSslGetNameEntryCount(id, subject) == 3, "name release invalidated a live certificate name"); + Check(sceSslFreeSslCertName(id, nullptr) == static_cast(0x8095f007), "null cert name free accepted"); + Check(sceSslFreeSslCertName(id, subject) == 0, "non-null name release failed"); + Check(sceSslFreeSslCertName(id, fake) == 0, "non-null name release failed"); + Check(sceSslFreeSslCertName(9999, subject) == InvalidId, "invalid name release context error mismatch"); SceSsl::RtcTick begin{}; Check(sceSslGetNotBefore(0, nullptr, nullptr) == InvalidId, "date validation precedence mismatch"); @@ -315,8 +317,8 @@ void CertificateReaders() { Check(sceSslFreeCaList(id, &handles) == 0, "free ca list failed"); Check(sceSslTerm_nid_postfix(id) == 0, "term failed"); Check(sceSslGetSerialNumber(id, cert, serialData, &serialLen) == InvalidId, "terminated context error mismatch"); - Throws([&] { sceSslGetSubjectName(id, cert); }); - Throws([&] { sceSslGetIssuerName(id, cert); }); + Check(sceSslGetSubjectName(id, cert) == nullptr, "invalid name query returned a handle"); + Check(sceSslGetIssuerName(id, cert) == nullptr, "invalid name query returned a handle"); Check(sceSslGetNotBefore(id, cert, &begin) == InvalidId, "invalid date context error mismatch"); Check(sceSslGetNotAfter(id, cert, &limit) == InvalidId, "invalid date context error mismatch"); Check(sceSslGetFingerprint(id, cert, 3, sha256) == InvalidId, "invalid fingerprint context error mismatch"); @@ -340,7 +342,7 @@ void CertificateReaders() { Throws([&] { sceSslGetPem(id2, cert2, pemBuf.data(), &writtenLen); }); Check(sceSslTerm_nid_postfix(id2) == 0, "term failed"); Check(sceSslGetNameEntryCount(id2, subject2) == InvalidId, "terminated name context error mismatch"); - Throws([&] { sceSslFreeSslCertName(id2, subject2); }); + Check(sceSslFreeSslCertName(id2, subject2) == InvalidId, "invalid name release context error mismatch"); } } diff --git a/core/libs/tests/GuestSslPs5Readers.cpp b/core/libs/tests/GuestSslPs5Readers.cpp index a9feda8d5a..3075a601e9 100644 --- a/core/libs/tests/GuestSslPs5Readers.cpp +++ b/core/libs/tests/GuestSslPs5Readers.cpp @@ -66,7 +66,7 @@ void Readers() { Check(sceSslGetNameEntryCount(context, subject) == 3 && sceSslGetNameEntryCount(context, issuer) == 3, "PS5 name entry count failed"); Throws([&] { sceSslGetSubjectName(other, certificate); }); Throws([&] { sceSslGetNameEntryCount(other, subject); }); - Throws([&] { sceSslFreeSslCertName(other, subject); }); + Check(sceSslFreeSslCertName(other, subject) == 0, "non-null name release checked ownership"); std::array oid{}, value{}; length = SIZE_MAX; Check(sceSslGetNameEntryInfo(context, subject, 1, oid.data(), oid.size(), value.data(), value.size(), &length) == 0, "PS5 eight-argument name query failed"); @@ -91,7 +91,7 @@ void Readers() { Check(sceSslGetSerialNumber(context, certificate, nullptr, &length) == 0 && length == 3, "CA snapshot lost its certificate on unload"); Check(sceSslFreeCaList(context, &list) == 0, "CA list free failed"); Throws([&] { sceSslGetSerialNumber(context, certificate, nullptr, &length); }); - Check(sceSslGetNameEntryCount(context, subject) == 3, "owned name depended on freed certificate storage"); + Throws([&] { sceSslGetNameEntryCount(context, subject); }); Check(sceSslFreeSslCertName(context, subject) == 0, "name free failed"); Throws([&] { sceSslGetNameEntryCount(context, subject); }); Check(sceSslTerm_nid_postfix(context) == 0, "context term failed"); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 8e7603a4a1..e6423c45be 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -8,7 +8,7 @@ - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown -- [Certificate readers](../../../core/libs/prx/libSceSsl/Export.cpp): foreign and expired certificate/name handles throw instead of following native unchecked pointers. Name handles still use host-owned copies; native ownership and release semantics are not modeled. Numeric names for attributes Mbed TLS cannot identify may differ from native names. Fingerprint stack-space validation and backend digest failures are not reproduced. Signatures, valid-context/null-argument precedence, serial truncation, name copy bounds, validity timestamps and supported hash selectors are confirmed by the supplied module. +- [Certificate readers](../../../core/libs/prx/libSceSsl/Export.cpp): foreign and expired certificate/name handles throw instead of following native unchecked pointers. Name views track certificate lifetime and preserve identity; the native no-op name release is implemented. View storage remains allocated until context destruction. Numeric names for attributes Mbed TLS cannot identify may differ from native names. Fingerprint stack-space validation and backend digest failures are not reproduced. Signatures, valid-context/null-argument precedence, serial truncation, name copy bounds, validity timestamps and supported hash selectors are confirmed by the supplied module. - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate - [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1-255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp): native argument validation, CRLF line endings, 64-character Base64 lines, NUL-inclusive length and buffer clearing on insufficient capacity are recovered. Foreign and expired certificate handles throw instead of performing native unchecked pointer access. DER reserialization differences for noncanonical certificates remain unverified. From b4ac752769504360b60830632e06212eb3998308 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:52:46 +0300 Subject: [PATCH 493/523] fix(ssl): implement native peek reads and flag validation --- core/libs/prx/libSceSsl/Export.cpp | 27 ++++++++++++++++++++++++++- core/libs/tests/GuestSslTransport.cpp | 4 ++++ docs/dev/debt/libSceSsl.md | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 9d6e172d16..a1bab7e2af 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -296,6 +296,9 @@ struct Session { std::vector alpnStrings; std::vector alpnList; std::string alpnSelected; + Block peekBuffer; + std::size_t peekOffset = 0; + std::size_t peekSize = 0; bool handshaken = false; bool verifying = false; bool verified = false; @@ -915,6 +918,7 @@ int APS5_VABI sceSslConnect(int sslConnectionId) { } int APS5_VABI sceSslRead(int sslConnectionId, void* buf, size_t len, int flags) { + if (static_cast(flags) > 3) return ErrorInvalidValue; if (!buf && len) throw std::invalid_argument("sceSslRead: null buffer"); std::shared_ptr session; if (const int code = Establish(__func__, sslConnectionId, session)) { @@ -932,7 +936,28 @@ int APS5_VABI sceSslRead(int sslConnectionId, void* buf, size_t len, int flags) const auto total = std::min(len, INT_MAX); std::size_t received = 0; while (received < total) { - const int result = mbedtls_ssl_read(&session->ssl, bytes + received, total - received); + if (session->peekOffset < session->peekSize) { + const auto count = std::min(total - received, session->peekSize - session->peekOffset); + std::memcpy(bytes + received, session->peekBuffer.get() + session->peekOffset, count); + received += count; + if (flags == 3) break; + session->peekOffset += count; + if (session->peekOffset == session->peekSize) { + session->peekOffset = 0; + session->peekSize = 0; + session->peekBuffer.reset(); + } + if (flags != 0) break; + continue; + } + if (flags == 3 && !session->peekBuffer) session->peekBuffer = Allocate(MBEDTLS_SSL_IN_CONTENT_LEN); + const int result = flags == 3 + ? mbedtls_ssl_read(&session->ssl, session->peekBuffer.get(), MBEDTLS_SSL_IN_CONTENT_LEN) + : mbedtls_ssl_read(&session->ssl, bytes + received, total - received); + if (result > 0 && flags == 3) { + session->peekSize = static_cast(result); + continue; + } if (result > 0) { received += static_cast(result); if (flags != 0) break; diff --git a/core/libs/tests/GuestSslTransport.cpp b/core/libs/tests/GuestSslTransport.cpp index 93fcb1b901..927dc47414 100644 --- a/core/libs/tests/GuestSslTransport.cpp +++ b/core/libs/tests/GuestSslTransport.cpp @@ -459,6 +459,10 @@ int main(int argc, char** argv) { Require(sceSslWrite(connection, buffer.data(), 1, 1) == ErrorInvalidValue, "unknown write flags were accepted"); char request[] = "ping"; Require(sceSslWrite(connection, request, 4, 0) == 4, "write the request"); + Require(sceSslRead(connection, buffer.data(), buffer.size(), 4) == ErrorInvalidValue, "unknown read flags were accepted"); + Require(sceSslRead(0, nullptr, 1, -1) == ErrorInvalidValue, "read flags did not precede other validation"); + Require(sceSslRead(connection, buffer.data(), 2, 3) == 2 && std::memcmp(buffer.data(), "po", 2) == 0, "short peek failed"); + Require(sceSslRead(connection, buffer.data(), 4, 3) == 4 && std::memcmp(buffer.data(), "pong", 4) == 0, "repeated larger peek consumed bytes"); Require(sceSslRead(connection, buffer.data(), 2, 1) == 2 && std::memcmp(buffer.data(), "po", 2) == 0, "a partial read did not return the available bytes"); Require(sceSslRead(connection, buffer.data(), buffer.size(), 0) == 2 && std::memcmp(buffer.data(), "ng", 2) == 0, "a full read did not stop at the end of the stream"); Require(sceSslRead(connection, buffer.data(), buffer.size(), 1) == 0, "close notify did not end the stream"); diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index e6423c45be..877af46e33 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -6,7 +6,7 @@ - [sceSslDisableOption, sceSslDisableOptionInternal, sceSslDisableOptionInternalInsecure, sceSslEnableOption, sceSslEnableOptionInternal, sceSslGetNanoSSLModuleId, sceSslGetPeerCert, sceSslGetSslError, sceSslLoadRootCACert and sceSslSetSslVersion](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv); no credible prototypes were found. They are declared without parameters solely to resolve known imports, not as an ABI claim; callers must not call them until signatures are verified. - [sceSslRecv, sceSslReuseConnection and sceSslSend](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - the list is the intersection of the export table of the console's libSceSsl (function symbols only), the pinned aerolib names and the functions not yet defined anywhere in the repo. Names and NIDs appear in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceSsl.c) and [verified NID name database](https://github.com/zecoxao/sce_symbols/blob/2883963a0a514ba6e77407a08ef4730a65e88254/aerolib.csv). Their parameter shapes follow [SharpProspero's pinned bindings](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs#L222-L262), and are not measured on a PS5. -- [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with any other flags (`MsgFlagPartialRead` 1 measured, 2 behaved the same) returns what the first record delivers; a non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts +- [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with flags 1 or 2 returns what the first record delivers. Native flags 3 peek without consuming data; values outside 0 through 3 return `SSL_ERROR_INVALID_VALUE` before connection lookup. A non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown - [Certificate readers](../../../core/libs/prx/libSceSsl/Export.cpp): foreign and expired certificate/name handles throw instead of following native unchecked pointers. Name views track certificate lifetime and preserve identity; the native no-op name release is implemented. View storage remains allocated until context destruction. Numeric names for attributes Mbed TLS cannot identify may differ from native names. Fingerprint stack-space validation and backend digest failures are not reproduced. Signatures, valid-context/null-argument precedence, serial truncation, name copy bounds, validity timestamps and supported hash selectors are confirmed by the supplied module. - [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate From b35e344585442798da921cdeab560500cb39c17c Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:55:13 +0300 Subject: [PATCH 494/523] fix(systemservice): restore browser launch signature and validation --- core/libs/prx/libSceSystemService/Export.cpp | 11 +++++++---- core/libs/tests/GuestSystemService.cpp | 20 ++++++++++++-------- docs/dev/debt/libSceSystemService.md | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libSceSystemService/Export.cpp b/core/libs/prx/libSceSystemService/Export.cpp index 08e4239f19..65eae0e1e4 100644 --- a/core/libs/prx/libSceSystemService/Export.cpp +++ b/core/libs/prx/libSceSystemService/Export.cpp @@ -371,10 +371,13 @@ int APS5_VABI sceSystemServiceLaunchPlayerDialog(const void* param) { return SYSTEM_SERVICE_OK; } -int APS5_VABI sceSystemServiceLaunchWebBrowser(const char* uri, void* param) { - (void)uri; - (void)param; - return SCE_KERNEL_ERROR_EOPNOTSUPP; +int APS5_VABI sceSystemServiceLaunchWebBrowser(const char* uri) { + if (!uri) return static_cast(0x80a10003); + std::size_t length = 0; + while (length < 6016 && uri[length] != 0) ++length; + if (length > 6015) return static_cast(0x80a10007); + if (std::strncmp(uri, "http:", 5) != 0 && std::strncmp(uri, "https:", 6) != 0 && std::strncmp(uri, "psno:", 5) != 0) return static_cast(0x80a10008); + return SCE_KERNEL_ERROR_EOPNOTSUPP; } int APS5_VABI sceSystemServiceLaunchApp(const char* titleId, const char* const* arguments, const void* param) { diff --git a/core/libs/tests/GuestSystemService.cpp b/core/libs/tests/GuestSystemService.cpp index 3913b7dc1c..36e6e11853 100644 --- a/core/libs/tests/GuestSystemService.cpp +++ b/core/libs/tests/GuestSystemService.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,7 @@ extern "C" int APS5_VABI sceSystemServiceDisableMusicPlayer(void); extern "C" int APS5_VABI sceSystemServiceReenableMusicPlayer(void); extern "C" int APS5_VABI sceSystemServiceDisableMediaPlay(void); extern "C" int APS5_VABI sceSystemServiceReenableMediaPlay(void); -extern "C" int APS5_VABI sceSystemServiceLaunchWebBrowser(const char* uri, void* param); +extern "C" int APS5_VABI sceSystemServiceLaunchWebBrowser(const char* uri); extern "C" int APS5_VABI sceSystemServiceShowControllerSettings(void); extern "C" int APS5_VABI sceSystemServiceShowDisplaySafeAreaSettings(void); extern "C" int APS5_VABI sceSystemServiceOpenChallengeActivity(void); @@ -96,13 +97,16 @@ int main() { Require(sceSystemServiceShowControllerSettings() == SYSTEM_SERVICE_OK); Require(sceSystemServiceShowDisplaySafeAreaSettings() == SYSTEM_SERVICE_OK); Require(sceSystemServiceOpenChallengeActivity() == SYSTEM_SERVICE_OK); - Require(sceSystemServiceLaunchWebBrowser("http://127.0.0.1:8780/video?v=0", nullptr) == browserUnavailable); - unsigned char browserParam[64]; - std::memset(browserParam, 0x5a, sizeof(browserParam)); - Require(sceSystemServiceLaunchWebBrowser("https://example.com/", browserParam) == browserUnavailable); - for (unsigned char byte : browserParam) Require(byte == 0x5a); - Require(sceSystemServiceLaunchWebBrowser("", nullptr) == browserUnavailable); - Require(sceSystemServiceLaunchWebBrowser(nullptr, nullptr) == browserUnavailable); + Require(sceSystemServiceLaunchWebBrowser("http:") == browserUnavailable); + Require(sceSystemServiceLaunchWebBrowser("https:") == browserUnavailable); + Require(sceSystemServiceLaunchWebBrowser("psno:") == browserUnavailable); + Require(sceSystemServiceLaunchWebBrowser("") == static_cast(0x80a10008)); + Require(sceSystemServiceLaunchWebBrowser("HTTP:") == static_cast(0x80a10008)); + Require(sceSystemServiceLaunchWebBrowser(nullptr) == static_cast(0x80a10003)); + std::string browserUri = "http:" + std::string(6010, 'a'); + Require(sceSystemServiceLaunchWebBrowser(browserUri.c_str()) == browserUnavailable); + browserUri += 'a'; + Require(sceSystemServiceLaunchWebBrowser(browserUri.c_str()) == static_cast(0x80a10007)); Require(LaunchAppThrows("PPSA99109", nullptr, nullptr)); Require(LaunchAppThrows(nullptr, nullptr, nullptr)); const char* launchArguments[] = {"--backup", nullptr}; diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 8abccbf202..e697b107ae 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -13,7 +13,7 @@ - [sceSystemServiceAddLocalProcessForPsmKit, sceSystemServiceKillLocalProcessForPsmKit](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 runs one process and starts or stops no other, so both throw without reading their arguments. The parameters follow Forever Launcher's own firmware 5.50 probes (`ps5/probes/localproc` in [bizkut/ps5-forever](https://github.com/bizkut/ps5-forever)): a path, a null-terminated argv and a 0x90-byte parameter block in, a handle out; the kill takes that handle. Their results on the console are unknown. Forever Launcher (PPSA99030) imports both and stops at load without them. - [sceSystemServiceLaunchApp](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 runs one title and cannot start another, so every call throws without reading its arguments. The (title ID, null-terminated argv, launch parameters) signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L141-L148), whose `AppLauncher` Prospero Explorer (PPSA99109) and Prospero Multi Tools (PPSA99110) import it through and which passes a 0x20-byte launch context or null; the console's argument checks and errors are unknown -- [sceSystemServiceLaunchWebBrowser](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the console's error for an unavailable web browser is unknown; it returns the kernel's `EOPNOTSUPP` (0x8002002D) for any arguments. The (uri, param) signature follows [RommPS](https://github.com/s0liton/RommPS/blob/754d4d60f25938a5136b9e2efcd074313c4386c5/platform/ps5/app/rommps/examples/rommps/kit/credits.cpp#L86-L95), which passes a URL and a null param and only tests the result for zero; the param layout and the console's argument checks are unknown +- [sceSystemServiceLaunchWebBrowser](../../../core/libs/prx/libSceSystemService/Export.cpp): the native export takes one URI argument. Null input, the 6015-byte length limit and supported scheme checks are recovered. The host still returns kernel `EOPNOTSUPP` for valid input; a missing browser service's native error is unresolved. - [sceSystemServiceParamGetString](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the system name (parameter 6) is the fixed `PS5`, not the console's name. As in a payload process on a PS5 (firmware 13.60), the name is written NUL-padded to the whole buffer, and a buffer under 65 bytes, a null buffer or any other parameter returns `SYSTEM_SERVICE_ERROR_PARAMETER` without writing. Parameters 0 to 40, 100 to 110, 1000 to 1010, -1 and `INT_MAX` were tried there; the others are assumed to fail the same way. PPSA26344 at `0xA87935` passes parameter 6 with a 65-byte buffer - [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - console language numbers 0 to 29 follow the PS4 SDK enum in [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/06fcf7fd108fa080035cdd3259ed22eb07e0508c/include/orbis/_types/sys_service.h#L54-L83) (21 Arabic). 30 (Ukrainian) is not in it and follows PPSA99505 ([Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/app/i18n.cpp#L29-L60)) and PPSA99002 ([ProsperoLight](https://github.com/blackbearreloaded/ProsperoLight/blob/b708bbde3457bef1073a9ab6959957cb691e8e2f/include/i18n.hpp#L252-L309)), whose numbers agree with the enum. Not confirmed on a console, and neither title starts on `main` yet. Parameter 400 returns the same number as parameter 1: PPSA01325 asks for 400 first, asks for 1 when that fails, and uses either answer as an index into the same 31 languages; in the PS5 10.01 and 12.60 `libSceSystemService` modules parameter 400 reads the same registry value as parameter 1, without parameter 1's mapping of newer language numbers by SDK version, and returns 0x80a10003 when an imported check that is not identified returns 1; the 4.03 module has no parameter 400. Neither the mapping nor that error is modelled, and the parameter's name is not known. Without `ANYPS5_LANGUAGE` the language is English (United States); the date format, time format and enter button stay fixed - [sceSystemServiceParamGetInt](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the time zone (parameter 4) and summer time (parameter 5) follow the host's local time zone at the moment of the call. Titles compute the local time as UTC plus the time zone in minutes plus 60 minutes when summer time is 1 ([Apollo](https://github.com/bucanero/apollo-ps4/blob/252a9d5a187c463994263009851a179f4f910c49/source/exec_cmd.c#L70-L92), [GoldHEN's frame logger](https://github.com/GoldHEN/GoldHEN_Plugins_Repository/blob/6c3a06f53d9caebadfb0aeed9bb94119012010a7/plugin_src/frame_logger/source/main.c#L60-L82), and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/413d2e3518fb435f40971d238fe0ecae7ee551da/src/SharpProspero/Platform/SystemParameters.cs#L75-L79) calls them the offset from UTC in minutes and whether summer time is in effect), so the time zone is the host's current offset east of UTC less the summer time shift while summer time is in effect: 60 minutes on Linux, and on Windows the C runtime's own summer time bias (`_get_dstbias`), which is also what shifted its local time. For a host zone whose summer time is not one hour, Linux keeps the sum right and the standard offset wrong, and Windows the opposite; how the console reports such a zone is unknown. libkernel's `sceKernelConvertUtcToLocaltime`, `sceKernelConvertLocaltimeToUtc` and `sceKernelGettimezone` still use UTC From 2ee0131006bc97239c98d4f4fdbd1084769891e4 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:55:51 +0300 Subject: [PATCH 495/523] fix(vrsetupdialog): restore result type and error precedence --- core/libs/prx/libSceVrSetupDialog/Export.cpp | 18 +++++++----------- core/libs/tests/GuestVrSetupDialog.cpp | 12 ++++++++---- docs/dev/debt/libSceVrSetupDialog.md | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceVrSetupDialog/Export.cpp b/core/libs/prx/libSceVrSetupDialog/Export.cpp index 73e247dafe..2aa6aad360 100644 --- a/core/libs/prx/libSceVrSetupDialog/Export.cpp +++ b/core/libs/prx/libSceVrSetupDialog/Export.cpp @@ -12,13 +12,10 @@ constexpr int COMMON_DIALOG_STATUS_NONE = 0; constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; -constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); constexpr int VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x8A7C0001u); constexpr int VR_SETUP_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x8A7C0002u); constexpr int VR_SETUP_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x8A7C0003u); -constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int VR_SETUP_DIALOG_ERROR_BUSY = static_cast(0x8A7C0005u); -constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; @@ -50,14 +47,13 @@ int APS5_VABI sceVrSetupDialogGetStatus(void) { return g_status.load(); } -int APS5_VABI sceVrSetupDialogGetResult(void* result) { - const int status = g_status.load(); - if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; - if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; - if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &canceled, sizeof(canceled)); - return 0; +int APS5_VABI sceVrSetupDialogGetResult(std::int32_t* result) { + if (result == nullptr) return VR_SETUP_DIALOG_ERROR_INVALID_ARGUMENT; + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return VR_SETUP_DIALOG_ERROR_NOT_INITIALIZED; + *result = COMMON_DIALOG_RESULT_USER_CANCELED; + if (status != COMMON_DIALOG_STATUS_FINISHED) return VR_SETUP_DIALOG_ERROR_BUSY; + return 0; } int APS5_VABI sceVrSetupDialogClose(void) { diff --git a/core/libs/tests/GuestVrSetupDialog.cpp b/core/libs/tests/GuestVrSetupDialog.cpp index b879875b89..decf0c81e2 100644 --- a/core/libs/tests/GuestVrSetupDialog.cpp +++ b/core/libs/tests/GuestVrSetupDialog.cpp @@ -8,7 +8,7 @@ int APS5_VABI sceVrSetupDialogInitialize(void); int APS5_VABI sceVrSetupDialogOpen(const VrSetupDialogParam* param); int APS5_VABI sceVrSetupDialogUpdateStatus(void); int APS5_VABI sceVrSetupDialogGetStatus(void); -int APS5_VABI sceVrSetupDialogGetResult(void* result); +int APS5_VABI sceVrSetupDialogGetResult(std::int32_t* result); int APS5_VABI sceVrSetupDialogClose(void); int APS5_VABI sceVrSetupDialogTerminate(void); } @@ -22,8 +22,8 @@ constexpr int kStatusInitialized = 1; constexpr int kStatusFinished = 3; constexpr int kErrNotInitialized = static_cast(0x8A7C0001); constexpr int kErrAlreadyInitialized = static_cast(0x8A7C0002); -constexpr int kErrNotFinished = static_cast(0x80B80005); -constexpr int kErrArgNull = static_cast(0x80B8000D); +constexpr int kErrNotFinished = static_cast(0x8A7C0005); +constexpr int kErrArgNull = static_cast(0x8A7C0003); constexpr int kResultUserCanceled = 1; } @@ -36,7 +36,9 @@ int main() { Require(sceVrSetupDialogTerminate() == kErrNotInitialized); Require(sceVrSetupDialogOpen(¶m) == kErrNotInitialized); Require(sceVrSetupDialogClose() == kErrNotInitialized); - Require(sceVrSetupDialogGetResult(result) == static_cast(0x80B80003u)); + Require(sceVrSetupDialogGetResult(nullptr) == kErrArgNull); + Require(sceVrSetupDialogGetResult(result) == kErrNotInitialized); + Require(result[0] == -1 && result[1] == -1); Require(sceVrSetupDialogGetStatus() == kStatusNone); Require(sceVrSetupDialogUpdateStatus() == kStatusNone); @@ -47,6 +49,7 @@ int main() { Require(sceVrSetupDialogOpen(nullptr) == static_cast(0x8A7C0003u)); Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); + Require(result[0] == kResultUserCanceled && result[1] == -1); param.size = 55; Require(sceVrSetupDialogOpen(¶m) == static_cast(0x8A7C0003u)); @@ -78,5 +81,6 @@ int main() { Require(sceVrSetupDialogClose() == 0); Require(sceVrSetupDialogUpdateStatus() == kStatusInitialized); Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); + Require(result[0] == kResultUserCanceled && result[1] == -1); Require(sceVrSetupDialogTerminate() == 0); } diff --git a/docs/dev/debt/libSceVrSetupDialog.md b/docs/dev/debt/libSceVrSetupDialog.md index 138686c11e..6890166ca4 100644 --- a/docs/dev/debt/libSceVrSetupDialog.md +++ b/docs/dev/debt/libSceVrSetupDialog.md @@ -2,4 +2,4 @@ ### Silent stubs -- [libSceVrSetupDialog](../../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). Initialization does not check the native process eligibility record, and the 46-byte setup payload remains opaque. Open validates the recovered 56-byte parameter header. GetResult is absent from the supplied module, so its signature and cancellation result remain inferred. +- [libSceVrSetupDialog](../../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). Initialization does not check the native process eligibility record, and the 46-byte setup payload remains opaque. Open validates the recovered 56-byte parameter header. GetResult writes one 32-bit result after initialization, including when returning the not-finished error; its error codes and validation order are recovered. From 2411c836ced99b6410ed06b5636ba1ec03c3be5f Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:58:31 +0300 Subject: [PATCH 496/523] fix(videorecording): type native video output mode query --- core/libs/prx/libSceVideoRecordingP/Export.cpp | 2 +- docs/dev/debt/libSceVideoRecordingP.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceVideoRecordingP/Export.cpp b/core/libs/prx/libSceVideoRecordingP/Export.cpp index 93cf413444..621cce7d32 100644 --- a/core/libs/prx/libSceVideoRecordingP/Export.cpp +++ b/core/libs/prx/libSceVideoRecordingP/Export.cpp @@ -110,7 +110,7 @@ int APS5_VABI sceVideoRecordingGetSupportInfo(VideoRecordingSupportInfo* info) { return 0; } -int APS5_VABI sceVideoRecordingGetVideoOutMode(void* mode) { +int APS5_VABI sceVideoRecordingGetVideoOutMode(std::uint32_t* mode) { (void)mode; NotImplemented_nid_no_patch(__func__); return 0; diff --git a/docs/dev/debt/libSceVideoRecordingP.md b/docs/dev/debt/libSceVideoRecordingP.md index ec40ae9fd8..5d8096130d 100644 --- a/docs/dev/debt/libSceVideoRecordingP.md +++ b/docs/dev/debt/libSceVideoRecordingP.md @@ -5,7 +5,7 @@ - [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - declared with `void(void* param)` signature following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L67-L69); unimplemented and throws `NotImplemented_nid_no_patch` - [sceVideoRecordingCloseByHandle, sceVideoRecordingGetInfoByHandle](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - native handles are context pointers. Recording state and per-handle queries remain unimplemented. - [sceVideoRecordingCopyBGRAtoNV12](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the destination, source, 32-bit width and height arguments are confirmed. Pixel conversion remains unimplemented. -- [sceVideoRecordingGetSupportInfo, sceVideoRecordingGetVideoOutMode](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - GetSupportInfo writes an 18-byte payload whose fields remain unresolved. GetVideoOutMode is absent from the supplied recording modules, so its signature remains unverified. Both exports remain unimplemented. +- [sceVideoRecordingGetSupportInfo, sceVideoRecordingGetVideoOutMode](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - GetSupportInfo writes an 18-byte payload whose fields remain unresolved. GetVideoOutMode takes a four-byte output confirmed by the native recording assembly and Avcap2 IPC descriptor; the mode values remain unresolved. Both exports remain unimplemented. - [sceVideoRecordingInit, sceVideoRecordingTerm](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - parameterless initialization and termination stubs; unimplemented and throw `NotImplemented_nid_no_patch` - [sceVideoRecordingInitialize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - initialization stub taking an optional parameter structure pointer; unverified and throws `NotImplemented_nid_no_patch` - [sceVideoRecordingOpen, sceVideoRecordingOpen2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the 32-byte and 40-byte parameter variants, four argument registers and 32-bit heap size are recovered. Mode, output mode, option and format semantics remain incomplete. QueryHddSize2 returns a signed 64-bit size or error; recording and sizing exports remain unimplemented. From f2ee3065f1aa71384eabebb5cbd13b7758bac209 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 12:59:49 +0300 Subject: [PATCH 497/523] fix(videorecording): define query parameter output layout --- core/libs/prx/libSceVideoRecordingP/Export.cpp | 16 +++++++++++++++- docs/dev/debt/libSceVideoRecordingP.md | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceVideoRecordingP/Export.cpp b/core/libs/prx/libSceVideoRecordingP/Export.cpp index 621cce7d32..9a930b3476 100644 --- a/core/libs/prx/libSceVideoRecordingP/Export.cpp +++ b/core/libs/prx/libSceVideoRecordingP/Export.cpp @@ -11,6 +11,20 @@ struct VideoRecordingContext; +struct VideoRecordingQueryParam { + std::uint64_t size; + std::uint32_t reserved8; + std::int32_t mode; + std::uint8_t reserved16[12]; + std::uint32_t option; + std::uint8_t reserved32[32]; +}; + +static_assert(sizeof(VideoRecordingQueryParam) == 64); +static_assert(offsetof(VideoRecordingQueryParam, mode) == 12); +static_assert(offsetof(VideoRecordingQueryParam, option) == 28); + + struct VideoRecordingSupportInfo { std::uint8_t data[18]; }; @@ -54,7 +68,7 @@ constexpr int SceVideoRecordingStatusNone = 0; extern "C" { -void APS5_VABI _sceVideoRecordingQueryParam_nid_postfix(void* param) { +void APS5_VABI _sceVideoRecordingQueryParam_nid_postfix(VideoRecordingQueryParam* param) { (void)param; NotImplemented_nid_no_patch("_sceVideoRecordingQueryParam"); } diff --git a/docs/dev/debt/libSceVideoRecordingP.md b/docs/dev/debt/libSceVideoRecordingP.md index 5d8096130d..484e8d00ae 100644 --- a/docs/dev/debt/libSceVideoRecordingP.md +++ b/docs/dev/debt/libSceVideoRecordingP.md @@ -2,12 +2,12 @@ ### Unknown function info -- [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - declared with `void(void* param)` signature following [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Video/VideoRecording.cs#L67-L69); unimplemented and throws `NotImplemented_nid_no_patch` +- [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp): the supplied module reads a 64-bit size and writes a 32-bit mode at offset 12 for sizes at least 16, plus a 32-bit option at offset 28 for sizes at least 64. Mode/option meanings remain unresolved; the protected implementation is unchanged. - [sceVideoRecordingCloseByHandle, sceVideoRecordingGetInfoByHandle](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - native handles are context pointers. Recording state and per-handle queries remain unimplemented. - [sceVideoRecordingCopyBGRAtoNV12](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the destination, source, 32-bit width and height arguments are confirmed. Pixel conversion remains unimplemented. - [sceVideoRecordingGetSupportInfo, sceVideoRecordingGetVideoOutMode](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - GetSupportInfo writes an 18-byte payload whose fields remain unresolved. GetVideoOutMode takes a four-byte output confirmed by the native recording assembly and Avcap2 IPC descriptor; the mode values remain unresolved. Both exports remain unimplemented. - [sceVideoRecordingInit, sceVideoRecordingTerm](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - parameterless initialization and termination stubs; unimplemented and throw `NotImplemented_nid_no_patch` -- [sceVideoRecordingInitialize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) (libSceVideoRecordingP) - initialization stub taking an optional parameter structure pointer; unverified and throws `NotImplemented_nid_no_patch` +- [sceVideoRecordingInitialize](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp): the native initializer consumes no caller arguments; the former optional parameter is not read by the Avcap2 initializer or recording setup. Its unused parameter slot is retained because the protected body references it. - [sceVideoRecordingOpen, sceVideoRecordingOpen2](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the 32-byte and 40-byte parameter variants, four argument registers and 32-bit heap size are recovered. Mode, output mode, option and format semantics remain incomplete. QueryHddSize2 returns a signed 64-bit size or error; recording and sizing exports remain unimplemented. - [Handle and support queries](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - native recording handles are context pointers, and GetSupportInfo writes 18 bytes. Support payload field semantics remain incomplete. RestModeProcessStart, RestModeProcessGetStatus and SendCommand consume no caller arguments in the supplied native module; their existing unused parameter slots remain because the protected bodies reference those parameter names. From 90e4e930aeca7d2847b4e667d6dd962fe7740b2b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:04:51 +0300 Subject: [PATCH 498/523] fix(audiopropagation): define source and system diagnostic layouts --- .../AudioPropagation.hpp | 45 ++++++++++++++++++- docs/dev/debt/libSceAudioPropagation.md | 4 +- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp index 99db7ef6e3..8323b813c9 100644 --- a/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp +++ b/core/libs/prx/libSceAudioPropagation/AudioPropagation.hpp @@ -47,8 +47,49 @@ static_assert(sizeof(SystemOptions) == SystemOptionsSize); static_assert(offsetof(SystemOptions, frameSamples) == 32); static_assert(offsetof(SystemOptions, flags) == 44); -struct SourceInfo; -struct SystemInfo; +struct SourceInfo { + AudioPropagationHandle system; + float position[3]; + std::uint32_t reserved20; + float direction[3]; + std::uint32_t reserved36; + std::uint32_t attribute2Mode; + float attribute2Values[3]; + float attribute3Values[4]; + std::uint32_t pathCapacity; + std::uint32_t parameter76; +}; + +struct SystemInfo { + void* cpuMemory; + std::size_t cpuMemorySize; + std::uint32_t index; + std::uint32_t frameSamples; + std::uint32_t parameter24; + std::uint32_t maxSources; + std::uint32_t maxMaterials; + std::uint32_t pathCapacityPerSource; + float propagationSpeed; + std::uint32_t reserved44; +}; + +static_assert(sizeof(SourceInfo) == 80); +static_assert(offsetof(SourceInfo, position) == 8); +static_assert(offsetof(SourceInfo, direction) == 24); +static_assert(offsetof(SourceInfo, attribute2Mode) == 40); +static_assert(offsetof(SourceInfo, attribute2Values) == 44); +static_assert(offsetof(SourceInfo, attribute3Values) == 56); +static_assert(offsetof(SourceInfo, pathCapacity) == 72); +static_assert(offsetof(SourceInfo, parameter76) == 76); +static_assert(sizeof(SystemInfo) == 48); +static_assert(offsetof(SystemInfo, cpuMemorySize) == 8); +static_assert(offsetof(SystemInfo, index) == 16); +static_assert(offsetof(SystemInfo, frameSamples) == 20); +static_assert(offsetof(SystemInfo, parameter24) == 24); +static_assert(offsetof(SystemInfo, maxSources) == 28); +static_assert(offsetof(SystemInfo, maxMaterials) == 32); +static_assert(offsetof(SystemInfo, pathCapacityPerSource) == 36); +static_assert(offsetof(SystemInfo, propagationSpeed) == 40); struct ReportRegistration; struct ReportArguments { diff --git a/docs/dev/debt/libSceAudioPropagation.md b/docs/dev/debt/libSceAudioPropagation.md index 501b37adab..cff417667f 100644 --- a/docs/dev/debt/libSceAudioPropagation.md +++ b/docs/dev/debt/libSceAudioPropagation.md @@ -7,5 +7,5 @@ ### Unknown function info - [sceAudioPropagationSourceSetAudioPath](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): the native path lookup and update confirm a 64-bit path handle, a read-only 224-byte path description and a float gain in XMM0. The description contains four 48-byte points, a point count and flags; each point contains a descriptor, room handle, gain and three position floats. The last eight bytes of each point and of the description are not consumed by these paths and remain opaque. Protected implementation remains unchanged; allocation, ownership and rendering are not implemented. -- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options use a recovered 56-byte record; the field at byte 24 and final eight bytes remain semantically unresolved. CPU memory requirements and option limits are recovered; GPU memory queries throw because their platform-specific runtime condition is unresolved. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes one silent frame without native accumulation. Frame capacity and source-count limits follow the recovered options. Other formats and native validation precedence remain unimplemented. Source/system diagnostic fields still need reconstruction. -- [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; source/system diagnostic fields still need reconstruction. +- [libSceAudioPropagation](../../../core/libs/prx/libSceAudioPropagation/Export.cpp): system options use a recovered 56-byte record; the field at byte 24 and final eight bytes remain semantically unresolved. CPU memory requirements and option limits are recovered; GPU memory queries throw because their platform-specific runtime condition is unresolved. Descriptor validation, ray ownership and path generation are unimplemented. Native rendering accepts multiple mutable records; the host supports only format 2 and writes one silent frame without native accumulation. Frame capacity and source-count limits follow the recovered options. Other formats and native validation precedence remain unimplemented. +- [Initialization exports](../../../core/libs/prx/libSceAudioPropagation/Export.cpp) ending in `Init`, plus `sceAudioPropagationSystem`, are absent from the supplied native module and retain unresolved signatures. Query, locking, enumeration and report callback signatures are recovered; diagnostic layouts are recovered, but source attributes 2/3 and the constant-one fields still lack semantic names. The final system word and the fourth position/direction words have no confirmed semantics. From c54adfb288f522ae076f34bd44469cac385a14ea Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:08:56 +0300 Subject: [PATCH 499/523] fix(ult): restore thread diagnostic context and fiber callback type --- core/libs/SceTypes.hpp | 2 +- core/libs/prx/libSceFiber/Export.cpp | 4 ++-- core/libs/prx/libSceUlt/SceUltTypes.hpp | 26 ++++++++++++++++++++++++- core/libs/tests/Fiber.cpp | 1 + docs/dev/debt/libSceUlt.md | 2 +- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 95443d9e12..85bb99bedf 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -333,7 +333,7 @@ struct VaList { }; #pragma pack(pop) -using FiberEntry = void* (*)(std::uint64_t, void*); +using FiberEntry = void (APS5_VABI*)(std::uint64_t argOnInitialize, std::uint64_t argOnRun); struct FiberOptParam { std::uint32_t magic; diff --git a/core/libs/prx/libSceFiber/Export.cpp b/core/libs/prx/libSceFiber/Export.cpp index 81aec33202..54b0930521 100644 --- a/core/libs/prx/libSceFiber/Export.cpp +++ b/core/libs/prx/libSceFiber/Export.cpp @@ -37,7 +37,7 @@ static constexpr std::uint64_t FIBER_CONTEXT_FILL = 0xdeadbeefdeadbeefull; static constexpr std::size_t FIBER_HOST_STACK_MARGIN = 1u << 20; static constexpr std::size_t FIBER_GUEST_STACK_MIN = 64u << 10; -using GuestFiberEntry = void (APS5_VABI*)(std::uint64_t argOnInitialize, std::uint64_t argOnRun); +using GuestFiberEntry = FiberEntry; // A fiber that is switching away stays Suspending until its context is saved; whoever runs next // on that host thread publishes Suspended. Fibers migrate between threads, so a resumer must never @@ -515,7 +515,7 @@ int32_t APS5_VABI sceFiberGetInfo(FiberObject* object, FiberInfo* fiber_info) { auto* fiber = AsFiber(object); if (!fiber || !fiber_info) return object && fiber_info ? SCE_FIBER_ERROR_INVALID : SCE_FIBER_ERROR_NULL; if (fiber_info->size != sizeof(FiberInfo)) return SCE_FIBER_ERROR_INVALID; - fiber_info->entry = reinterpret_cast(fiber->entry); + fiber_info->entry = fiber->entry; fiber_info->arg_on_initialize = fiber->argOnInitialize; fiber_info->addr_context = SizedWindow(fiber); fiber_info->size_context = fiber->contextSize; diff --git a/core/libs/prx/libSceUlt/SceUltTypes.hpp b/core/libs/prx/libSceUlt/SceUltTypes.hpp index d8bff75850..4c77af1649 100644 --- a/core/libs/prx/libSceUlt/SceUltTypes.hpp +++ b/core/libs/prx/libSceUlt/SceUltTypes.hpp @@ -239,7 +239,31 @@ static_assert(offsetof(UltUlthreadRuntimeInfo, reserved60) == 60); static_assert(offsetof(UltUlthreadRuntimeInfo, workerStackSize) == 64); static_assert(offsetof(UltUlthreadRuntimeInfo, reserved) == 72); -struct UltUlthreadInfo; +struct UltUlthreadInfo { + std::uint64_t size; + void* object; + char name[32]; + std::uint32_t contextAttribute; + std::uint32_t reserved52; + void* runtime; + FiberInfo fiber; + std::byte reserved[64]; +}; + +static_assert(sizeof(FiberInfo) == 128); +static_assert(offsetof(FiberInfo, entry) == 8); +static_assert(offsetof(FiberInfo, arg_on_initialize) == 16); +static_assert(offsetof(FiberInfo, addr_context) == 24); +static_assert(offsetof(FiberInfo, size_context) == 32); +static_assert(offsetof(FiberInfo, name) == 40); +static_assert(offsetof(FiberInfo, size_context_margin) == 72); +static_assert(sizeof(UltUlthreadInfo) == 256); +static_assert(offsetof(UltUlthreadInfo, object) == 8); +static_assert(offsetof(UltUlthreadInfo, name) == 16); +static_assert(offsetof(UltUlthreadInfo, contextAttribute) == 48); +static_assert(offsetof(UltUlthreadInfo, runtime) == 56); +static_assert(offsetof(UltUlthreadInfo, fiber) == 64); +static_assert(offsetof(UltUlthreadInfo, reserved) == 192); struct UltMutexState { std::mutex _mutex; diff --git a/core/libs/tests/Fiber.cpp b/core/libs/tests/Fiber.cpp index 73b6a9afb1..2267ee2497 100644 --- a/core/libs/tests/Fiber.cpp +++ b/core/libs/tests/Fiber.cpp @@ -270,6 +270,7 @@ int main() { Require(sceFiberStartContextSizeCheck(1) == FiberErrorInvalid, "start with flags"); Require(_sceFiberInitializeImpl_nid_postfix(checked, "unchecked", CheckedEntry, 0, g_checkedContext.data(), g_checkedContext.size(), nullptr, 0) == 0, "initialize unchecked"); Require(sceFiberGetInfo(checked, &info) == 0 && info.size_context_margin == ~0ull, "no margin without size check"); + Require(info.entry == CheckedEntry, "info preserves the guest entry callback type"); Require(sceFiberFinalize(checked) == 0, "finalize unchecked"); Require(sceFiberStartContextSizeCheck(0) == 0, "start size check"); Require(sceFiberStartContextSizeCheck(0) == FiberErrorState, "start twice"); diff --git a/docs/dev/debt/libSceUlt.md b/docs/dev/debt/libSceUlt.md index 31ae1d7025..12002369d2 100644 --- a/docs/dev/debt/libSceUlt.md +++ b/docs/dev/debt/libSceUlt.md @@ -6,7 +6,7 @@ - [libSceUlt ulthreads](../../../core/libs/prx/libSceUlt/Export.cpp) - Runtime option defaults still omit the native header, stack size, worker affinity and priority fields; affinity depends on unresolved process configuration queries. Concurrent join ownership is not modeled: the native runtime distinguishes an ordinary running thread from one with another joiner. Host threads do not use the guest context or worker scheduler. - [libSceUlt queues](../../../core/libs/prx/libSceUlt/Export.cpp) - Queue-data pool destruction and shared slot limits still require comparison with the native node allocator. -- [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. The nested context in `UltUlthreadInfo` still requires reconstruction. +- [Ult factory and creation exports](../../../core/libs/prx/libSceUlt/Export.cpp) without a leading underscore are absent from the supplied module; their signatures remain unresolved. Diagnostic signatures and synchronization argument lists are recovered. - [sceUltMutexTryLock, the reader-writer lock and sceUltUlthreadGetSelf, sceUltUlthreadYield and sceUltUlthreadExit](../../../core/libs/prx/libSceUlt/src/ReaderWriterLock.cpp) (libSceUlt) - the signatures follow the PS Vita libult in [RPCS3](https://github.com/RPCS3/rpcs3/blob/13aa88c3c4d66ea8799e987f7d762d90943ab17b/rpcs3/Emu/PSP2/Modules/sceUlt.cpp) (a 256-byte lock and 128-byte option structures), not a PS5 binary. The rules are assumed: readers may take the lock while a writer waits, a thread that holds the write lock gets `ULT_ERROR_STATE` for any further lock instead of waiting on itself, a reader asking for the write lock waits on its own read lock, and the try forms return `ULT_ERROR_BUSY`. Outside an ulthread, `sceUltUlthreadGetSelf`, `sceUltUlthreadYield` and `sceUltUlthreadExit` return `ULT_ERROR_STATE`; inside one they act on the guest thread it runs on, since ulthreads are not scheduled cooperatively here ### Functional From cf717cd80f96dd8e8eb9f4423671357698e66cbd Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:10:21 +0300 Subject: [PATCH 500/523] fix(videorecording): restore info selectors and unopened errors --- core/libs/CMakeLists.txt | 2 +- .../libs/prx/libSceVideoRecordingP/Export.cpp | 47 ++++++++++--------- core/libs/tests/GuestOfflineImports.cpp | 26 ++++++++++ docs/dev/debt/libSceVideoRecordingP.md | 2 + 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 84952285be..f84fa93f40 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -384,7 +384,7 @@ if(BUILD_TESTING) add_test_executable(guest_offline_imports_tests tests/GuestOfflineImports.cpp) target_include_directories(guest_offline_imports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libc) + target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libSceVideoRecordingP libc) configure_windows_unwind(guest_offline_imports_tests) add_test(NAME guest_offline_imports COMMAND guest_offline_imports_tests) if(Python3_Interpreter_FOUND) diff --git a/core/libs/prx/libSceVideoRecordingP/Export.cpp b/core/libs/prx/libSceVideoRecordingP/Export.cpp index 9a930b3476..c3b67583f6 100644 --- a/core/libs/prx/libSceVideoRecordingP/Export.cpp +++ b/core/libs/prx/libSceVideoRecordingP/Export.cpp @@ -2,12 +2,6 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" -#include -#include -#include -#include -#include -#include struct VideoRecordingContext; @@ -61,8 +55,6 @@ static_assert(offsetof(VideoRecordingParam2, duration) == 20); static_assert(offsetof(VideoRecordingParam2, format) == 36); namespace { -std::mutex g_infoMutex; -std::map> g_info; constexpr int SceVideoRecordingStatusNone = 0; } @@ -95,14 +87,19 @@ int APS5_VABI sceVideoRecordingCopyBGRAtoNV12(void* dst, const void* src, uint32 return 0; } -int APS5_VABI sceVideoRecordingGetInfo(int32_t info, void* data, size_t size) { - if (data == nullptr) APS5_INVALID_ARG_EX; - std::lock_guard lock(g_infoMutex); - const auto found = g_info.find(info); - if (found == g_info.end()) throw std::logic_error(std::string(__func__) + ": info " + std::to_string(info) + " was never set"); - if (size < found->second.size()) APS5_INVALID_ARG_EX; - std::memcpy(data, found->second.data(), found->second.size()); - return 0; +int APS5_VABI sceVideoRecordingGetInfo(std::int32_t info, void*, std::size_t) { + switch (info) { + case 0xa001: + case 0xa002: + case 0xa003: + case 0xa005: + case 0xa00a: + case 0xa00b: + case 0xa00c: + return static_cast(0x80a80006); + default: + return static_cast(0x80a80003); + } } int APS5_VABI sceVideoRecordingGetInfoByHandle(VideoRecordingContext* handle, int32_t info, void* data, size_t size) { @@ -194,12 +191,18 @@ int APS5_VABI sceVideoRecordingSendCommand(int cmd, const void* param) { return 0; } -int APS5_VABI sceVideoRecordingSetInfo(int32_t info, const void* data, size_t size) { - if (data == nullptr && size != 0) APS5_INVALID_ARG_EX; - const auto* bytes = static_cast(data); - std::lock_guard lock(g_infoMutex); - g_info[info].assign(bytes, bytes + size); - return 0; +int APS5_VABI sceVideoRecordingSetInfo(std::int32_t info, const void* data, std::size_t size) { + switch (info) { + case 0x8002: + case 0x8003: + if (!data || size < sizeof(float)) return static_cast(0x80a80003); + return static_cast(0x80a80002); + case 0xa004: + case 0xa006: + return static_cast(0x80a80006); + default: + return static_cast(0x80a80003); + } } int APS5_VABI sceVideoRecordingStart(void) { diff --git a/core/libs/tests/GuestOfflineImports.cpp b/core/libs/tests/GuestOfflineImports.cpp index f89a850b63..54acdb9e0c 100644 --- a/core/libs/tests/GuestOfflineImports.cpp +++ b/core/libs/tests/GuestOfflineImports.cpp @@ -9,6 +9,8 @@ #include extern "C" { +int APS5_VABI sceVideoRecordingGetInfo(std::int32_t info, void* data, std::size_t size); +int APS5_VABI sceVideoRecordingSetInfo(std::int32_t info, const void* data, std::size_t size); int APS5_VABI sceNpEntitlementAccessAbortRequest(std::int64_t); int APS5_VABI sceNpEntitlementAccessDeleteRequest(std::int64_t); int APS5_VABI sceHttpSetCookieEnabled(int, int); @@ -34,6 +36,30 @@ int APS5_VABI sceRudpEnd(); static void Require(bool value) { if (!value) std::abort(); } int main() { + constexpr int recordingInvalid = static_cast(0x80a80003); + constexpr int recordingNotOpen = static_cast(0x80a80006); + constexpr int recordingUnavailable = static_cast(0x80a80002); + std::uint32_t recordingOutput = 0x12345678; + for (std::int32_t selector = 0; selector <= 0x10000; ++selector) { + const bool query = selector == 0xa001 || selector == 0xa002 || selector == 0xa003 || selector == 0xa005 || selector == 0xa00a || selector == 0xa00b || selector == 0xa00c; + const bool control = selector == 0xa004 || selector == 0xa006; + Require(sceVideoRecordingGetInfo(selector, &recordingOutput, sizeof(recordingOutput)) == (query ? recordingNotOpen : recordingInvalid)); + Require(recordingOutput == 0x12345678); + Require(sceVideoRecordingGetInfo(selector, nullptr, 0) == (query ? recordingNotOpen : recordingInvalid)); + Require(sceVideoRecordingSetInfo(selector, nullptr, 0) == (control ? recordingNotOpen : recordingInvalid)); + } + for (std::int32_t selector : {-1, INT32_MIN, INT32_MAX}) { + Require(sceVideoRecordingGetInfo(selector, nullptr, 0) == recordingInvalid); + Require(sceVideoRecordingSetInfo(selector, nullptr, 0) == recordingInvalid); + } + const float recordingVolume = 0.5f; + for (std::int32_t selector : {0x8002, 0x8003}) { + for (std::size_t size : {0u, 1u, 3u, 4u, 8u}) { + Require(sceVideoRecordingSetInfo(selector, nullptr, size) == recordingInvalid); + Require(sceVideoRecordingSetInfo(selector, &recordingVolume, size) == (size < sizeof(float) ? recordingInvalid : recordingUnavailable)); + } + } + for (std::int64_t requestId : {std::int64_t{0}, std::int64_t{1}, INT64_MAX}) { bool abortFailed = false; bool deleteFailed = false; diff --git a/docs/dev/debt/libSceVideoRecordingP.md b/docs/dev/debt/libSceVideoRecordingP.md index 484e8d00ae..0b82a50648 100644 --- a/docs/dev/debt/libSceVideoRecordingP.md +++ b/docs/dev/debt/libSceVideoRecordingP.md @@ -2,6 +2,8 @@ ### Unknown function info +- [sceVideoRecordingGetInfo, sceVideoRecordingSetInfo](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) support native validation and errors before a recording context exists. Open and initialization remain protected unimplemented exports; live recording queries, control and volume changes are unavailable. + - [_sceVideoRecordingQueryParam_nid_postfix](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp): the supplied module reads a 64-bit size and writes a 32-bit mode at offset 12 for sizes at least 16, plus a 32-bit option at offset 28 for sizes at least 64. Mode/option meanings remain unresolved; the protected implementation is unchanged. - [sceVideoRecordingCloseByHandle, sceVideoRecordingGetInfoByHandle](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - native handles are context pointers. Recording state and per-handle queries remain unimplemented. - [sceVideoRecordingCopyBGRAtoNV12](../../../core/libs/prx/libSceVideoRecordingP/Export.cpp) - the destination, source, 32-bit width and height arguments are confirmed. Pixel conversion remains unimplemented. From ee76c0cd98841ab4453a6e0a7f2344655a4343db Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:22:45 +0300 Subject: [PATCH 501/523] fix(kernel): restore coredump buffer validation and data type argument --- core/libs/prx/libkernel/System/src/Coredump.cpp | 12 ++++-------- core/libs/tests/GuestCoredumpAttach.cpp | 17 +++++++++-------- core/libs/tests/GuestCoredumpUserData.cpp | 10 +++++++--- docs/dev/debt/libkernel.md | 6 +++--- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/Coredump.cpp b/core/libs/prx/libkernel/System/src/Coredump.cpp index 3bc229914c..05cca26f98 100644 --- a/core/libs/prx/libkernel/System/src/Coredump.cpp +++ b/core/libs/prx/libkernel/System/src/Coredump.cpp @@ -69,7 +69,7 @@ int APS5_VABI sceCoredumpAttachMemoryRegionAsUserFile(uint32_t userValue, const return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } -int APS5_VABI sceCoredumpSetUserDataType(void) { +int APS5_VABI sceCoredumpSetUserDataType(std::int32_t type) { NotImplemented_nid_no_patch("Uxqkdta7wEg"); return 0; } @@ -83,21 +83,17 @@ void APS5_VABI sceCoredumpDebugTextOut(const char* str, int len) { } int64_t APS5_VABI sceCoredumpWriteUserData(const void* data, size_t size) { - requireRegionOutsideHandler(__func__, data, size); + if (!data || size > INT32_MAX) return SCE_COREDUMP_ERROR_INVALID_ARGUMENT; return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } int APS5_VABI sceCoredumpGetStopInfoCpu(void* info, size_t size) { - const auto address = reinterpret_cast(info); - if (info == nullptr || size != 16 || address % alignof(uint64_t) != 0 || size > UINTPTR_MAX - address) - throw std::runtime_error(std::string(__func__) + ": null, unaligned, wrapping or unsupported CPU stop-info buffer outside the core dump handler"); + if (!info || (size != 8 && size != 16)) return SCE_COREDUMP_ERROR_INVALID_ARGUMENT; return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } int APS5_VABI sceCoredumpGetThreadContextInfo(void* threadContextInfo, size_t threadContextInfoSize) { - const auto address = reinterpret_cast(threadContextInfo); - if (threadContextInfo == nullptr || threadContextInfoSize != 144 || address % alignof(uint64_t) != 0 || threadContextInfoSize > UINTPTR_MAX - address) - throw std::runtime_error(std::string(__func__) + ": null, unaligned, wrapping or unsupported thread context buffer outside the core dump handler"); + if (!threadContextInfo || threadContextInfoSize != 144) return SCE_COREDUMP_ERROR_INVALID_ARGUMENT; return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } diff --git a/core/libs/tests/GuestCoredumpAttach.cpp b/core/libs/tests/GuestCoredumpAttach.cpp index 43e32238e0..ab03f0b21c 100644 --- a/core/libs/tests/GuestCoredumpAttach.cpp +++ b/core/libs/tests/GuestCoredumpAttach.cpp @@ -52,6 +52,7 @@ int main() { Require(sceCoredumpAttachMemoryRegion(1, region.data(), region.size()) == NotInCoredumpHandler); Require(sceCoredumpAttachUserMemoryFile(UserFileTruncateIfExceedLimit | 2, region.data(), region.size()) == NotInCoredumpHandler); Require(sceCoredumpGetStopInfoCpu(region.data() + 16, 16) == NotInCoredumpHandler); + Require(sceCoredumpGetStopInfoCpu(region.data() + 16, 8) == NotInCoredumpHandler); Require(sceCoredumpGetThreadContextInfo(context.data() + 8, 144) == NotInCoredumpHandler); Require(region == original && context == originalContext); @@ -76,16 +77,16 @@ int main() { Require(Throws([&] { sceCoredumpAttachUserMemoryFile(1, nullptr, region.size()); }, "sceCoredumpAttachUserMemoryFile")); Require(Throws([&] { sceCoredumpAttachUserMemoryFile(1, region.data(), 0); }, "sceCoredumpAttachUserMemoryFile")); Require(Throws([&] { sceCoredumpAttachUserMemoryFile(1, region.data(), SIZE_MAX); }, "sceCoredumpAttachUserMemoryFile")); - Require(Throws([&] { sceCoredumpGetStopInfoCpu(nullptr, 16); }, "sceCoredumpGetStopInfoCpu")); - Require(Throws([&] { sceCoredumpGetStopInfoCpu(region.data() + 1, 16); }, "sceCoredumpGetStopInfoCpu")); - Require(Throws([&] { sceCoredumpGetStopInfoCpu(reinterpret_cast(UINTPTR_MAX - 7u), 16); }, "sceCoredumpGetStopInfoCpu")); + Require(sceCoredumpGetStopInfoCpu(nullptr, 16) == InvalidArgument); + Require(sceCoredumpGetStopInfoCpu(region.data() + 1, 16) == NotInCoredumpHandler); + Require(sceCoredumpGetStopInfoCpu(reinterpret_cast(UINTPTR_MAX - 7u), 16) == NotInCoredumpHandler); for (const size_t size : {size_t{0}, size_t{1}, size_t{15}, size_t{17}, SIZE_MAX}) - Require(Throws([&] { sceCoredumpGetStopInfoCpu(region.data() + 16, size); }, "sceCoredumpGetStopInfoCpu")); - Require(Throws([&] { sceCoredumpGetThreadContextInfo(nullptr, 144); }, "sceCoredumpGetThreadContextInfo")); - Require(Throws([&] { sceCoredumpGetThreadContextInfo(context.data() + 1, 144); }, "sceCoredumpGetThreadContextInfo")); - Require(Throws([&] { sceCoredumpGetThreadContextInfo(reinterpret_cast(UINTPTR_MAX - 7u), 144); }, "sceCoredumpGetThreadContextInfo")); + Require(sceCoredumpGetStopInfoCpu(region.data() + 16, size) == InvalidArgument); + Require(sceCoredumpGetThreadContextInfo(nullptr, 144) == InvalidArgument); + Require(sceCoredumpGetThreadContextInfo(context.data() + 1, 144) == NotInCoredumpHandler); + Require(sceCoredumpGetThreadContextInfo(reinterpret_cast(UINTPTR_MAX - 7u), 144) == NotInCoredumpHandler); for (const size_t size : {size_t{0}, size_t{1}, size_t{143}, size_t{145}, SIZE_MAX}) - Require(Throws([&] { sceCoredumpGetThreadContextInfo(context.data(), size); }, "sceCoredumpGetThreadContextInfo")); + Require(sceCoredumpGetThreadContextInfo(context.data(), size) == InvalidArgument); Require(sceCoredumpUnregisterCoredumpHandler() == 0); Require(sceCoredumpAttachMemoryRegion(1, region.data(), region.size()) == NotInCoredumpHandler); diff --git a/core/libs/tests/GuestCoredumpUserData.cpp b/core/libs/tests/GuestCoredumpUserData.cpp index 30776ebbcc..3578510707 100644 --- a/core/libs/tests/GuestCoredumpUserData.cpp +++ b/core/libs/tests/GuestCoredumpUserData.cpp @@ -17,6 +17,7 @@ int APS5_VABI sceCoredumpAttachMemoryRegionAsUserFile(uint32_t userValue, const } namespace { +constexpr int64_t InvalidArgument = static_cast(0x81180000u); constexpr int64_t NotInCoredumpHandler = static_cast(0x81180003u); bool handlerRan = false; int handlerContext = 0; @@ -74,9 +75,12 @@ int main() { sceCoredumpDebugTextOut(text, static_cast(sizeof(text) - 1)); Require(!handlerRan); - Require(Throws([&] { sceCoredumpWriteUserData(nullptr, region.size()); }, "sceCoredumpWriteUserData")); - Require(Throws([&] { sceCoredumpWriteUserData(region.data(), 0); }, "sceCoredumpWriteUserData")); - Require(Throws([&] { sceCoredumpWriteUserData(region.data(), SIZE_MAX); }, "sceCoredumpWriteUserData")); + Require(sceCoredumpWriteUserData(nullptr, region.size()) == InvalidArgument); + Require(sceCoredumpWriteUserData(nullptr, 0) == InvalidArgument); + Require(sceCoredumpWriteUserData(region.data(), 0) == NotInCoredumpHandler); + Require(sceCoredumpWriteUserData(region.data(), INT32_MAX) == NotInCoredumpHandler); + Require(sceCoredumpWriteUserData(region.data(), std::size_t{INT32_MAX} + 1) == InvalidArgument); + Require(sceCoredumpWriteUserData(region.data(), SIZE_MAX) == InvalidArgument); Require(Throws([&] { sceCoredumpDebugTextOut(nullptr, 4); }, "sceCoredumpDebugTextOut")); Require(Throws([&] { sceCoredumpDebugTextOut(text, -1); }, "sceCoredumpDebugTextOut")); Require(Throws([&] { sceCoredumpAttachUserFile(1, nullptr); }, "sceCoredumpAttachUserFile")); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 6a4136fc3c..3f835e75f8 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -83,10 +83,10 @@ - [seteuid, setegid, setuid, setgid, setreuid, setregid, setgroups, getgroups](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the guest process is assumed to run as root, with real, effective and saved user and group ids 0. As in FreeBSD's `sys_seteuid`, `sys_setegid`, `sys_setuid`, `sys_setgid`, `sys_setreuid` and `sys_setregid`, id 0 (or -1 for an unchanged `setreuid`/`setregid` id) is then accepted without a privilege check and changes no credential, so 0 is returned; any other id would change an id, which is not modelled, and throws. `setgroups` keeps the supplementary group list as `kern_setgroups` does (`NGROUPS_MAX` 1023, an empty list leaves the effective group as the only entry, a first entry other than 0 would change the effective group and throws) and `getgroups` reports it. The ids and privileges a title has on a console are unverified - [pthread_getname_np](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - a thread that has already been joined returns `ESRCH` (3) and its output buffer is left untouched, as measured on a PS5 (firmware 13.60) by the author of this entry; since [#4481](https://github.com/boykopovar/AnyPS5/pull/4481) the joined thread's object is retained, so the posix wrapper answers `ESRCH` for it instead of reading the retained name. A null name buffer is answered with `EFAULT` before that, an order the console table does not measure. The SCE sibling `scePthreadGetname` has no measured joined-thread behaviour on the console and still reads the retained object's name, so it is unchanged, as is the pre-existing dangling case of a thread that ended detached (its object is released at exit; see the scePthreadJoin entry) - [sceKernelGetOperationMode](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call and ignores the result. A homebrew payload on a PS5 (firmware 13.60) reads mode 2 and submode 0, nothing else is written, and the result register holds no status, so the function is taken to return nothing. Whether a title process reads the same values is unverified. Null outputs throw -- [sceCoredumpGetStopInfoCpu](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) takes `(void* info, size_t size)` as in the [Orbital type database](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json); the 16-byte, 8-byte-aligned block follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelSystem.cs), which places coredump reads inside the handler. The handler is never invoked here, so a well-formed request returns `NOT_IN_COREDUMP_HANDLER` without reading or writing the block. Its value `0x81180003` follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/coredump/coredump_error.h) and the NHL 27 (PPSA34063) call site reported in [#4275](https://github.com/boykopovar/AnyPS5/discussions/4275), not a PS5 measurement. The console validation order is unverified: null, unaligned or wrapping buffers and sizes other than 16 throw instead of guessing an argument error. Information from an active coredump handler remains unsupported -- [sceCoredumpGetThreadContextInfo](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel, NID `ShChva57wIM`) takes `(void* threadContextInfo, size_t threadContextInfoSize)` as in the [Orbital type database](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json); the 144-byte block of 18 64-bit registers follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelSystem.cs#L120-L141). It answers as `sceCoredumpGetStopInfoCpu`: the handler is never invoked here, so an aligned 144-byte request returns `NOT_IN_COREDUMP_HANDLER` without reading or writing the block, and null, unaligned or wrapping buffers and other sizes throw, as the console validation order is unverified. Planet Coaster (PPSA01736) imports it +- [sceCoredumpGetStopInfoCpu](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) has no active coredump context. Native null/size validation is recovered, including both 8-byte and 16-byte records; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output. Active-handler data retrieval remains unimplemented. +- [sceCoredumpGetThreadContextInfo](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) validates a nonnull 144-byte output using the native checks. Active-handler register capture remains unimplemented; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output. - [sceCoredumpAttachMemoryRegion, sceCoredumpAttachUserMemoryFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - both take `(uint32_t userValue, const void* mem, size_t size)`: for the first, the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Kernel/KernelSystem.cs) bindings agree; the second has only SharpProspero (the PS4 5.05 firmware does not export it, so Orbital has no entry) and is described there as attaching the region as an unnamed user file. Not confirmed on a title; the RED engine's PS4 crash handler ([Vangaurd](https://github.com/vurimad/Vangaurd/blob/main/source/imported/common/redSystem/src/errorHandlerImplErrorHooksOrbis.cpp)) calls the sibling `sceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, name)` from inside its handler. Attaching belongs inside the core dump handler, which the system runs on its own thread after the process stops (SharpProspero's remark, the RED engine's usage and `SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER`, "function was not called from registered core dump handler", in the Vita's SceCoredump error table, [RivetDB](https://github.com/CreepNT/RivetDB/blob/master/rivetdb.json)). The handler is never invoked here, so every call is outside it and returns that error; nothing is recorded, as there is no dump to attach to. Its value `0x81180003` comes from shadPS4 (PS4), not confirmed on a PS5: [fpPS4](https://github.com/red-prig/fpPS4/blob/master/kernel/ps4_libkernel.pas) has `NOT_REGISTERED` 0x81180001 and `ALREADY_REGISTERED` 0x81180002, shadPS4's `coredump_error.h` has `NOT_IN_COREDUMP_HANDLER` = 0x81180003, and the Vita table numbers `PARAM` 0, `NOT_REGISTERED` 1, `ALREADY_REGISTERED` 2 and `NOT_IN_COREDUMP_HANDLER` 3. Whether the console checks the region before the calling context is unknown, so a null, empty or wrapping region throws. `userValue` is not interpreted; SharpProspero names its bits 31 and 30 as truncation flags for the user file variants -- [sceCoredumpWriteUserData, sceCoredumpDebugTextOut](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - the signatures `ssize_t (const void* data, size_t size)` and `void (const char* str, int len)` follow the [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json) IDA type database and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelSystem.cs) bindings ("Repeated calls append", "The bytes written, or a negative error code"). User data is written from inside the core dump handler: the Vita's [`sceCoredumpWriteUserData`](https://github.com/vitasdk/vita-headers/blob/e66ebe90b73d1fa4cce005a5b2072cec27322544/include/psp2/coredump_nounlink.h) returns `0x800A0003` ("No user-data capture is active") outside it, and the RED engine's PS4 crash handler ([Vangaurd](https://github.com/vurimad/Vangaurd/blob/4f86d2b0e1a6c0d50526152c517a0292b1a68af4/source/imported/common/redSystem/src/errorHandlerImplErrorHooksOrbis.cpp)) makes all its coredump calls, `sceCoredumpDebugTextOut` included, from its handler. The handler is never invoked here, so `sceCoredumpWriteUserData` returns `SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER` (0x81180003 in shadPS4's [`coredump_error.h`](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/coredump/coredump_error.h), PS4 numbering) sign-extended to 64 bits and keeps nothing; whether the console checks the buffer before the calling context is unknown, so a null, empty or wrapping buffer throws. `sceCoredumpDebugTextOut` returns nothing; its text, which goes into the report on the console, is printed to stderr with the `[coredump]` prefix of `sceKernelDebugWriteCppExceptionInfo`, and a negative length or a null text with a nonzero length throws. No title is seen calling either: PPSA11386 imports `sceCoredumpWriteUserData` and does not call it before its stop (#2523) +- [sceCoredumpWriteUserData, sceCoredumpDebugTextOut](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) have no active coredump report. WriteUserData validates a nonnull buffer and size at most `INT32_MAX`, including zero; accepted requests return the sign-extended outside-handler error. DebugTextOut still prints to stderr instead of a report; its validation and report integration remain unverified. - [sceCoredumpAttachUserFile, sceCoredumpAttachMemoryRegionAsUserFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - the signatures `int (uint32_t userValue, const char* path)` and `int (uint32_t userValue, const void* mem, size_t size, const char* name)` follow the [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json) IDA type database and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelSystem.cs) bindings. The crash handler calls both from inside its core dump handler, `sceCoredumpAttachUserFile(priority, absolutePath)` and `sceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, "LoggerQueueCrashDump.txt")`. The handler is never invoked here, so both return `SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER` (0x81180003, PS4 numbering from shadPS4) and attach nothing; whether the console checks the arguments before the calling context is unknown, so a null path or name and a null, empty or wrapping region throw. An empty path or name is passed through to that error, and `userValue` is not interpreted. No title is seen calling either - [sceKernelAddTimerEvent](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - `usec` is taken as the period in microseconds; re-adding an existing timer keeps its pending expiration count and restarts the period from the time of the call, where FreeBSD 12+ `filt_timertouch` clears pending expirations on `EV_ADD`; `usec == 0` fires on every wait. None of this is confirmed on a PS5 title - [sceKernelWaitEqueue](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - a level-triggered user event (`sceKernelAddUserEvent`) is reported at most once per call, as in FreeBSD `kqueue_scan` (`sys/kern/kern_event.c`), which re-queues a reported knote without `EV_CLEAR` or `EV_DISPATCH` behind the scan marker, and kevent(2), where several triggers do not produce several kevents. Not measured on a console. Events are scanned in list order and the list is not rotated, so when `num` is smaller than the number of ready level events the later ones can starve, where FreeBSD moves a reported level knote to the tail. Several completions of one AMPR or VideoOut event within one call are returned as separate entries, where FreeBSD would aggregate them; the Orbis behaviour is unconfirmed From fe4016f612fa310852096af149d967308a16601d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:22:46 +0300 Subject: [PATCH 502/523] fix(voice): restore initialization and thread parameter validation --- core/libs/SceTypes.hpp | 51 +++++++++++++++++++++++++- core/libs/prx/libSceVoice/Export.cpp | 20 ++++++---- core/libs/tests/GuestVoice.cpp | 30 ++++++++++++++- core/libs/tests/GuestVoicePortAttr.cpp | 2 +- docs/dev/debt/libSceVoice.md | 4 +- 5 files changed, 94 insertions(+), 13 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 85bb99bedf..8b3eaa3f89 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -975,13 +975,60 @@ struct AudiodecM4aacInfo { std::int32_t i_result; }; +struct VoiceEvent { + std::int32_t type; + std::uint32_t reserved; + void* userData; + union { + struct { + std::uint16_t count; + std::uint16_t ports[7]; + } dataReady; + struct { + std::uint16_t port; + std::uint8_t state; + std::uint8_t reserved[13]; + } deviceState; + }; +}; + +using VoiceEventHandler = void (APS5_VABI*)(const VoiceEvent* event); + +struct VoiceThreadsParams { + std::uint32_t mode; + std::int32_t firstPriority; + std::uint64_t firstAffinity; + std::int32_t secondPriority; + std::uint32_t reserved20; + std::uint64_t secondAffinity; + std::int32_t thirdPriority; + std::uint32_t reserved36; + std::uint64_t thirdAffinity; +}; + +static_assert(sizeof(VoiceEvent) == 32); +static_assert(offsetof(VoiceEvent, userData) == 8); +static_assert(offsetof(VoiceEvent, dataReady) == 16); +static_assert(offsetof(VoiceEvent, deviceState) == 16); +static_assert(sizeof(VoiceThreadsParams) == 48); +static_assert(offsetof(VoiceThreadsParams, firstAffinity) == 8); +static_assert(offsetof(VoiceThreadsParams, secondPriority) == 16); +static_assert(offsetof(VoiceThreadsParams, secondAffinity) == 24); +static_assert(offsetof(VoiceThreadsParams, thirdPriority) == 32); +static_assert(offsetof(VoiceThreadsParams, thirdAffinity) == 40); + struct VoiceInitParam { std::int32_t app_type; - std::uint64_t on_event; + VoiceEventHandler on_event; void* user_data; - std::uint8_t reserved[32 - sizeof(std::int32_t) - sizeof(std::uint64_t) - sizeof(void*)]; + std::uint8_t reserved[16]; }; +static_assert(sizeof(VoiceInitParam) == 40); +static_assert(offsetof(VoiceInitParam, on_event) == 8); +static_assert(offsetof(VoiceInitParam, user_data) == 16); +static_assert(offsetof(VoiceInitParam, reserved) == 24); + struct VoicePortParam { std::int32_t port_type; std::uint16_t threshold; diff --git a/core/libs/prx/libSceVoice/Export.cpp b/core/libs/prx/libSceVoice/Export.cpp index b89fea488a..c070175dc4 100644 --- a/core/libs/prx/libSceVoice/Export.cpp +++ b/core/libs/prx/libSceVoice/Export.cpp @@ -68,20 +68,26 @@ bool IsInput(std::int32_t type) { return type == PortInDevice || type == PortInP extern "C" { -int APS5_VABI sceVoiceInit(VoiceInitParam* param, int32_t version) { - (void)version; - if (param == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceVoiceInit(const VoiceInitParam* param, std::int32_t version) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (voice.initialized) return VoiceErrorAlreadyInitialized; + if (voice.initialized) return static_cast(0x804e0802u); + if (!param || version != 100) return InvalidArgument; + if (param->app_type != 4 && param->app_type != 0x08000000 && param->app_type != 0x10000000 && param->app_type != 0x20000000) return InvalidArgument; voice.appType = param->app_type; voice.initialized = true; return 0; } -int APS5_VABI sceVoiceSetThreadsParams(void* params) { - if (params == nullptr) APS5_INVALID_ARG_EX; - return 0; +int APS5_VABI sceVoiceSetThreadsParams(const VoiceThreadsParams* params) { + auto& voice = State(); + std::lock_guard lock(voice.mutex); + if (!voice.initialized) return NotInitialized; + if (!params) return InvalidArgument; + if (voice.started) return static_cast(0x804e080bu); + if (params->mode == 0) return 0; + if (params->mode != 1) return InvalidArgument; + Fail(__func__, "worker priority and affinity are not implemented"); } int APS5_VABI sceVoiceStart(const VoiceStartParam* param) { diff --git a/core/libs/tests/GuestVoice.cpp b/core/libs/tests/GuestVoice.cpp index ac56c3d949..4f18783667 100644 --- a/core/libs/tests/GuestVoice.cpp +++ b/core/libs/tests/GuestVoice.cpp @@ -7,7 +7,8 @@ #include extern "C" { -int APS5_VABI sceVoiceInit(VoiceInitParam*, std::int32_t); +int APS5_VABI sceVoiceInit(const VoiceInitParam*, std::int32_t); +int APS5_VABI sceVoiceSetThreadsParams(const VoiceThreadsParams*); int APS5_VABI sceVoiceStart(const VoiceStartParam*); int APS5_VABI sceVoiceEnd_nid_postfix(); int APS5_VABI sceVoiceCreatePort(std::uint32_t*, const VoicePortParam*); @@ -89,12 +90,37 @@ int main() { Require(sceVoiceGetResourceInfo(nullptr) == notInitialized); Require(sceVoiceEnableChat() == notInitialized); Require(sceVoiceDisableChat() == notInitialized); + Require(sceVoiceSetThreadsParams(nullptr) == notInitialized); + VoiceThreadsParams threads{}; + Require(sceVoiceSetThreadsParams(&threads) == notInitialized); VoiceInitParam init{}; + Require(sceVoiceInit(nullptr, 100) == invalidArgument); + for (const auto mode : {-1, 0, 1, 3, 5, 0x07ffffff, 0x08000001, 0x10000001, 0x20000001, INT32_MAX}) { + init.app_type = mode; + Require(sceVoiceInit(&init, 100) == invalidArgument); + Require(sceVoiceGetResourceInfo(nullptr) == notInitialized); + } init.app_type = 0x10000000; + for (const auto version : {-1, 0, 99, 101, INT32_MAX}) { + Require(sceVoiceInit(&init, version) == invalidArgument); + Require(sceVoiceGetResourceInfo(nullptr) == notInitialized); + } VoiceStartParam start{}; Require(sceVoiceInit(&init, 100) == 0); - Require(sceVoiceInit(&init, 100) == AlreadyInitialized); + Require(sceVoiceInit(nullptr, 0) == static_cast(0x804e0802u)); + Require(sceVoiceInit(&init, 100) == static_cast(0x804e0802u)); + Require(sceVoiceSetThreadsParams(nullptr) == invalidArgument); + Require(sceVoiceSetThreadsParams(&threads) == 0); + threads.mode = 2; + Require(sceVoiceSetThreadsParams(&threads) == invalidArgument); + threads.mode = UINT32_MAX; + Require(sceVoiceSetThreadsParams(&threads) == invalidArgument); + threads.mode = 1; + RequireThrows([&] { sceVoiceSetThreadsParams(&threads); }); + threads.mode = 0; Require(sceVoiceStart(&start) == 0); + Require(sceVoiceSetThreadsParams(nullptr) == invalidArgument); + Require(sceVoiceSetThreadsParams(&threads) == static_cast(0x804e080bu)); Require(sceVoiceGetResourceInfo(nullptr) == invalidArgument); struct ResourceOutput { VoiceResourceInfo info; diff --git a/core/libs/tests/GuestVoicePortAttr.cpp b/core/libs/tests/GuestVoicePortAttr.cpp index 082c828ec5..e7d05b55d9 100644 --- a/core/libs/tests/GuestVoicePortAttr.cpp +++ b/core/libs/tests/GuestVoicePortAttr.cpp @@ -9,7 +9,7 @@ #include extern "C" { -int APS5_VABI sceVoiceInit(VoiceInitParam*, int32_t); +int APS5_VABI sceVoiceInit(const VoiceInitParam*, int32_t); int APS5_VABI sceVoiceEnd_nid_postfix(void); int APS5_VABI sceVoiceCreatePort(uint32_t*, const VoicePortParam*); int APS5_VABI sceVoiceDeletePort(uint32_t); diff --git a/docs/dev/debt/libSceVoice.md b/docs/dev/debt/libSceVoice.md index df5a8e4c45..db5a284c09 100644 --- a/docs/dev/debt/libSceVoice.md +++ b/docs/dev/debt/libSceVoice.md @@ -2,7 +2,7 @@ ### Functional -- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization still lacks native mode/version validation, allocator callbacks and backend creation; port creation does not enforce native resource limits. +- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization validates native modes and version 100, but still lacks event callback delivery and backend creation; port creation does not enforce native resource limits. - [sceVoiceEnableChat and sceVoiceDisableChat](../../../core/libs/prx/libSceVoice/Export.cpp): zero-argument signatures and the not-initialized error are confirmed. Initialized calls throw because the required audio chat backend is not implemented; they no longer report success for a local flag change. ### Unknown function info @@ -20,3 +20,5 @@ - [sceVoiceSetBitRate](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature - [sceVoiceUpdatePort](../../../core/libs/prx/libSceVoice/Export.cpp) (libSceVoice) - unknown signature - [sceVoiceGetPortAttr](../../../core/libs/prx/libSceVoice/Export.cpp): native initialization, port-range, pointer, attribute-size and port-type validation is implemented. Supported device attributes throw because the host has no device state or voice-activity processing. The former constant-zero voice-activity fallback is removed; failed calls leave output untouched. + +- [sceVoiceSetThreadsParams](../../../core/libs/prx/libSceVoice/Export.cpp): the 48-byte mode/priority/affinity layout and initialization, null, started-state and mode validation are recovered. Mode 1 throws because worker scheduling is not implemented; mode 0 is the native no-op. The 32-byte event callback union is recovered, but callback delivery and device-state byte semantics remain unimplemented. From f76db36d9f50f4a5ccf37fc0cfe2be3ac0306b53 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:23:01 +0300 Subject: [PATCH 503/523] fix(hmd2): restore buffer size query pair return type --- core/libs/prx/libSceHmd2/Export.cpp | 2 +- core/libs/prx/libSceHmd2/Hmd2.hpp | 5 +++++ core/libs/tests/GuestHmd2.cpp | 17 +++++++++++++++++ docs/dev/debt/libSceHmd2.md | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index d348fcefe5..bcc53524e0 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -293,7 +293,7 @@ int APS5_VABI sceHmd2ReprojectionInitialize(const Hmd2::ReprojectionInitializePa return 0; } -int APS5_VABI sceHmd2ReprojectionQueryBufferSizeAlign() { +Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionQueryBufferSizeAlign() { NotImplemented_nid_no_patch(__func__); return 0; } diff --git a/core/libs/prx/libSceHmd2/Hmd2.hpp b/core/libs/prx/libSceHmd2/Hmd2.hpp index c5ae98b060..4623331f91 100644 --- a/core/libs/prx/libSceHmd2/Hmd2.hpp +++ b/core/libs/prx/libSceHmd2/Hmd2.hpp @@ -60,9 +60,13 @@ static_assert(sizeof(FieldOfView) == 16); struct SizeAlign { std::uint64_t size; std::uint64_t align; + + constexpr SizeAlign(std::uint64_t size = 0, std::uint64_t align = 0) noexcept : size(size), align(align) {} }; static_assert(sizeof(SizeAlign) == 16); +static_assert(offsetof(SizeAlign, size) == 0); +static_assert(offsetof(SizeAlign, align) == 8); } @@ -76,6 +80,7 @@ std::int32_t APS5_VABI sceHmd2GetDeviceInformationByHandle(std::int32_t handle, std::int32_t APS5_VABI sceHmd2GetFieldOfView(std::int32_t handle, Hmd2::FieldOfView* fieldOfView); std::int32_t APS5_VABI sceHmd2GetFieldOfViewWithoutHandle(Hmd2::FieldOfView* fieldOfView); std::int32_t APS5_VABI sceHmd2SetVibration(std::int32_t handle, std::uint32_t level); +Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionQueryBufferSizeAlign(); Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionQueryDisplayBufferSizeAlign(); Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionQuerySeeThroughBufferSizeAlign(); Hmd2::SizeAlign APS5_VABI sceHmd2ReprojectionGetMirroringDisplayBufferSizeAlign(); diff --git a/core/libs/tests/GuestHmd2.cpp b/core/libs/tests/GuestHmd2.cpp index 4f05d57f02..e1bd7b8c52 100644 --- a/core/libs/tests/GuestHmd2.cpp +++ b/core/libs/tests/GuestHmd2.cpp @@ -10,6 +10,10 @@ #include #include +namespace Hmd2 { +struct ReprojectionOutput; +} + extern "C" { std::int32_t APS5_VABI Hmd2WriteStateTwo(void* a0); std::int32_t APS5_VABI Hmd2WriteSize0x2000Align0x100(std::uint64_t* a0, std::uint64_t* a1); @@ -55,6 +59,9 @@ std::int32_t APS5_VABI Hmd2BeforeInitializeError1003(); std::int32_t APS5_VABI Hmd2BeforeInitializeMinusOne(); std::int32_t APS5_VABI Hmd2BeforeInitializeValue0400(); std::int32_t APS5_VABI Hmd2BeforeInitializeValue20000(); +int APS5_VABI sceHmd2GazeActivateGazeTracking(std::int32_t mode); +int APS5_VABI sceHmd2ReprojectionGetCurrentOutput(Hmd2::ReprojectionOutput* output); +int APS5_VABI sceHmd2SetFieldOfView(const Hmd2::FieldOfView* fieldOfView); } namespace { @@ -299,6 +306,16 @@ int main() { InstallProcessParameters(); TestBeforeInitialize(); TestWithoutHeadset(); + bool threw = false; + try { + static_cast(sceHmd2ReprojectionQueryBufferSizeAlign()); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw, "buffer size query did not throw"); + Require(sceHmd2GazeActivateGazeTracking(0) == 0, "gaze activation before initialization"); + Require(sceHmd2ReprojectionGetCurrentOutput(nullptr) == Hmd2::ReprojectionNotInitialized, "reprojection before initialization"); + Require(sceHmd2SetFieldOfView(nullptr) == Hmd2::ParameterNull, "null field of view"); std::puts("HMD2 tests passed"); return 0; } diff --git a/docs/dev/debt/libSceHmd2.md b/docs/dev/debt/libSceHmd2.md index 20fdeb5810..25f1bc36a6 100644 --- a/docs/dev/debt/libSceHmd2.md +++ b/docs/dev/debt/libSceHmd2.md @@ -32,7 +32,7 @@ ### Functional - [sceHmd2Initialize](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the firmware also fails with 0x8a72001f when `sceKernelGetAppInfo` fails and with 0x8a72000a for some applications, depending on app info fields at 0x2c, 0x4b and 0x4d whose meaning is unknown, and then calls `sceMbusInit`; none of this is modelled -- [Reprojection size/alignment queries](../../../core/libs/prx/libSceHmd2/Export.cpp) - all five exports consume no arguments and return two uint64 values in RAX:RDX. Four host queries return values measured without a headset; native display sizes are calculated through the graphics layout service and see-through size follows internal configuration. QueryBufferSizeAlign changes calculation with runtime state; its protected body still throws and its declared scalar return type does not represent the native pair. Correcting that return type also requires changing its return statement, outside the permitted body edits. +- [Reprojection size/alignment queries](../../../core/libs/prx/libSceHmd2/Export.cpp) - all five exports consume no arguments and return two uint64 values in RAX:RDX. Four host queries return values measured without a headset; native display sizes are calculated through the graphics layout service and see-through size follows internal configuration. QueryBufferSizeAlign changes calculation with runtime state; its native pair return type is restored, while its protected body remains unchanged and throws. - [Hmd2BeforeInitializeZero](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers 0 to these 45 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `vDgpdS2N9Rw` (sceHmd2InternalRegisterVibrationCallback), `Ri35gKTZpvE` (sceHmd2InternalUnregisterVibrationCallback), `9oyW8puiKeI`, `1HZNE6jhBnQ` (sceHmd2InternalPowerEventInitialize), `cdwAKBZLXjs`, `Yub1MF58jfY`, `JYXAuQFnPyw` (sceHmd2InternalDfuOpen), `VuPKqCu3wzw` (sceHmd2InternalDfuClose), `GSThagVyW5U` (sceHmd2InternalCrashReportOpen), `J4qE5N937MY` (sceHmd2InternalCrashReportClose), `9KnNj-dyASg`, `Qta2bkstbFk`, `5ZcObnDRBXE`, `AvPNn0qx4WQ`, `jp0apqNGgi8` (sceHmd2SetIpdOffset), `-kZT7X7OMg0` (sceHmd2InternalRelocalizerOpen), `1ewLtng-SVk` (sceHmd2InternalRelocalizerClose), `B0b8DeIUGes` (sceHmd2InternalTrackingOpen), `HclLg4Oj5yw` (sceHmd2InternalTrackingClose), `VbEvrQXsZyY` (sceHmd2InternalLedDetectorOpen), `bB6MKZi5UCQ` (sceHmd2InternalLedDetectorClose), `OXQ1EDf02XU` (sceHmd2InternalImageOpen), `IAxfMTvFm0M` (sceHmd2InternalImageClose), `qTJtAtaLeno` (sceHmd2InternalGenDataOpen), `j7Q16UX6l34` (sceHmd2InternalGenDataClose), `pmmNM06rLqU`, `zdQfyDK9Ods` (sceHmd2InternalSlamTraceOpen), `aPRwp9GnljM` (sceHmd2InternalSlamTraceClose), `MjY1gBSaed0` (sceHmd2InternalGazeOpen), `evAMjJYHqW4` (sceHmd2InternalGazeClose), `r93FLEZ3BBk` (sceHmd2InternalGazeStart), `IdYURdEdNFo` (sceHmd2InternalGazeStop), `CVcL2Cu2AEI` (sceHmd2GazeActivateGazeTracking), `NmKIfVxr2s0`, `KeDLkkp62Gg`, `+aadu6-ra+A`, `FgnTiwwGSzk`, `PWAUwOTHbbo` (sceHmd2InternalCreateSharedMemory), `WzaEApHnsP8` (sceHmd2InternalIsGameVr2d), `dXzQI8QVY7Y` (sceHmd2InternalIsMiniAppVr2d), `cCvFKGqRh1k` (sceHmd2InternalIsCommonDlgVr2d), `uj4-2VEAFPU` (sceHmd2InternalIsCommonDlgMiniAppVr2d), `sIaGStI-iLM` (sceHmd2InternalMapSharedMemory), `JzwpZOemDO8`, `IsC4R4bZFVU`. Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2BeforeInitializeNotInitialized](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720002` to these 9 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `sXaPEPY-fLA` (sceHmd2InternalOpen), `3F5inHhKzXg` (sceHmd2InternalGetDeviceInformation), `sJAiB-6p55U`, `E8hDydLjsSs`, `xEQkSb2WJ7Y`, `aaP4dnR4q-0`, `lEU0TeUoGwI`, `cFVyjHBpUUU`, `zROtb46jTj4`. Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded - [Hmd2BeforeInitializeError0004](../../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - before `sceHmd2Initialize` the console answers `0x8a720004` to these 15 exports both with every argument null and with every argument pointing at a zeroed 16 KiB buffer, writing nothing: `kAMsjYHBbNQ` (sceHmd2InternalSetVolume), `5m73zmLpsQ0` (sceHmd2InternalResetViewPosition), `EPeH4xuQ2XQ` (sceHmd2InternalGetStatus), `TJ8lyRsjFY4` (sceHmd2InternalClearTrackerFts), `-XQVcLnyX68` (sceHmd2InternalSetSlamCommand), `giQwQoHhYYA` (sceHmd2InternalDfuStart), `zNc9IkgawMI`, `-tnG5UJPvAc` (sceHmd2GazeStartUserCalibration), `FPhFmG+jXuQ` (sceHmd2GazeStopUserCalibration), `CKlkvNngofQ`, `yL1CZaciFWE` (sceHmd2GazeSetUserCalibrationPoint), `qlzgyG3s0RY`, `kTIDrDLzMv0`, `9Gee0ef8a+U`, `ecZVjQdwmBI` (sceHmd2GazeSetEnabledEye). Measured from a payload process on firmware 13.60, so a game process before initialization is assumed to answer the same; the arguments and the behaviour after initialization are not measured, and the handler throws once `sceHmd2Initialize` has succeeded From b9fb0d289759aef378ae74da9111a629b06a1d52 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:24:25 +0300 Subject: [PATCH 504/523] fix(kernel): restore signed coredump attachment lengths --- .../prx/libkernel/System/src/Coredump.cpp | 15 +++++------- core/libs/tests/GuestCoredumpAttach.cpp | 23 ++++++++++++------- docs/dev/debt/libkernel.md | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/Coredump.cpp b/core/libs/prx/libkernel/System/src/Coredump.cpp index 05cca26f98..07cb01cbe8 100644 --- a/core/libs/prx/libkernel/System/src/Coredump.cpp +++ b/core/libs/prx/libkernel/System/src/Coredump.cpp @@ -19,11 +19,6 @@ static void requireRegionOutsideHandler(const char* function, const void* mem, s throw std::runtime_error(std::string(function) + ": null, empty or wrapping region outside the core dump handler"); } -static int attachOutsideCoredumpHandler(const char* function, const void* mem, size_t size) { - requireRegionOutsideHandler(function, mem, size); - return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; -} - static void requireTextOutsideHandler(const char* function, const char* argument, const char* text) { if (text == nullptr) throw std::runtime_error(std::string(function) + ": null " + argument + " outside the core dump handler"); } @@ -106,13 +101,15 @@ int APS5_VABI sceCoredumpConfigDumpMode(int mode) { return mode == 1 ? 0 : SCE_COREDUMP_ERROR_INVALID_ARGUMENT; } -int APS5_VABI sceCoredumpAttachUserMemoryFile(uint32_t userValue, const void* mem, size_t size) { +int APS5_VABI sceCoredumpAttachUserMemoryFile(std::uint32_t userValue, const void* mem, std::int32_t size) { (void)userValue; - return attachOutsideCoredumpHandler(__func__, mem, size); + (void)mem; + return size < 0 ? 0 : SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } -int APS5_VABI sceCoredumpAttachMemoryRegion(uint32_t userValue, const void* mem, size_t size) { +int APS5_VABI sceCoredumpAttachMemoryRegion(std::uint32_t userValue, const void* mem, std::int32_t size) { (void)userValue; - return attachOutsideCoredumpHandler(__func__, mem, size); + (void)mem; + return size < 0 ? 0 : SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } } diff --git a/core/libs/tests/GuestCoredumpAttach.cpp b/core/libs/tests/GuestCoredumpAttach.cpp index ab03f0b21c..d3d5466b57 100644 --- a/core/libs/tests/GuestCoredumpAttach.cpp +++ b/core/libs/tests/GuestCoredumpAttach.cpp @@ -10,8 +10,8 @@ extern "C" { int APS5_VABI sceCoredumpRegisterCoredumpHandler(uint64_t handler, size_t stackSize, uint64_t context); int APS5_VABI sceCoredumpUnregisterCoredumpHandler(void); -int APS5_VABI sceCoredumpAttachMemoryRegion(uint32_t userValue, const void* mem, size_t size); -int APS5_VABI sceCoredumpAttachUserMemoryFile(uint32_t userValue, const void* mem, size_t size); +int APS5_VABI sceCoredumpAttachMemoryRegion(uint32_t userValue, const void* mem, std::int32_t size); +int APS5_VABI sceCoredumpAttachUserMemoryFile(uint32_t userValue, const void* mem, std::int32_t size); int APS5_VABI sceCoredumpGetStopInfoCpu(void* info, size_t size); int APS5_VABI sceCoredumpGetThreadContextInfo(void* threadContextInfo, size_t threadContextInfoSize); int APS5_VABI sceCoredumpGetStopInfoGpu_Agc(void* info, size_t size); @@ -71,12 +71,12 @@ int main() { Require(region == original && context == originalContext); Require(!handlerRan); - Require(Throws([&] { sceCoredumpAttachMemoryRegion(1, nullptr, region.size()); }, "sceCoredumpAttachMemoryRegion")); - Require(Throws([&] { sceCoredumpAttachMemoryRegion(1, region.data(), 0); }, "sceCoredumpAttachMemoryRegion")); - Require(Throws([&] { sceCoredumpAttachMemoryRegion(1, region.data(), SIZE_MAX); }, "sceCoredumpAttachMemoryRegion")); - Require(Throws([&] { sceCoredumpAttachUserMemoryFile(1, nullptr, region.size()); }, "sceCoredumpAttachUserMemoryFile")); - Require(Throws([&] { sceCoredumpAttachUserMemoryFile(1, region.data(), 0); }, "sceCoredumpAttachUserMemoryFile")); - Require(Throws([&] { sceCoredumpAttachUserMemoryFile(1, region.data(), SIZE_MAX); }, "sceCoredumpAttachUserMemoryFile")); + Require(sceCoredumpAttachMemoryRegion(1, nullptr, region.size()) == NotInCoredumpHandler); + Require(sceCoredumpAttachMemoryRegion(1, region.data(), 0) == NotInCoredumpHandler); + Require(sceCoredumpAttachMemoryRegion(1, region.data(), -1) == 0); + Require(sceCoredumpAttachUserMemoryFile(1, nullptr, region.size()) == NotInCoredumpHandler); + Require(sceCoredumpAttachUserMemoryFile(1, region.data(), 0) == NotInCoredumpHandler); + Require(sceCoredumpAttachUserMemoryFile(1, region.data(), -1) == 0); Require(sceCoredumpGetStopInfoCpu(nullptr, 16) == InvalidArgument); Require(sceCoredumpGetStopInfoCpu(region.data() + 1, 16) == NotInCoredumpHandler); Require(sceCoredumpGetStopInfoCpu(reinterpret_cast(UINTPTR_MAX - 7u), 16) == NotInCoredumpHandler); @@ -88,6 +88,13 @@ int main() { for (const size_t size : {size_t{0}, size_t{1}, size_t{143}, size_t{145}, SIZE_MAX}) Require(sceCoredumpGetThreadContextInfo(context.data(), size) == InvalidArgument); + for (const auto length : {INT32_MIN, -1, 0, 1, INT32_MAX}) { + const int expected = length < 0 ? 0 : NotInCoredumpHandler; + Require(sceCoredumpAttachMemoryRegion(0, nullptr, length) == expected); + Require(sceCoredumpAttachUserMemoryFile(0, nullptr, length) == expected); + Require(sceCoredumpAttachMemoryRegion(0xffffffffu, region.data(), length) == expected); + Require(sceCoredumpAttachUserMemoryFile(0xffffffffu, region.data(), length) == expected); + } Require(sceCoredumpUnregisterCoredumpHandler() == 0); Require(sceCoredumpAttachMemoryRegion(1, region.data(), region.size()) == NotInCoredumpHandler); Require(sceCoredumpAttachUserMemoryFile(1, region.data(), region.size()) == NotInCoredumpHandler); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 3f835e75f8..d8c3a355a9 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -85,7 +85,7 @@ - [sceKernelGetOperationMode](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call and ignores the result. A homebrew payload on a PS5 (firmware 13.60) reads mode 2 and submode 0, nothing else is written, and the result register holds no status, so the function is taken to return nothing. Whether a title process reads the same values is unverified. Null outputs throw - [sceCoredumpGetStopInfoCpu](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) has no active coredump context. Native null/size validation is recovered, including both 8-byte and 16-byte records; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output. Active-handler data retrieval remains unimplemented. - [sceCoredumpGetThreadContextInfo](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) validates a nonnull 144-byte output using the native checks. Active-handler register capture remains unimplemented; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output. -- [sceCoredumpAttachMemoryRegion, sceCoredumpAttachUserMemoryFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - both take `(uint32_t userValue, const void* mem, size_t size)`: for the first, the IDA type database of [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json) and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Kernel/KernelSystem.cs) bindings agree; the second has only SharpProspero (the PS4 5.05 firmware does not export it, so Orbital has no entry) and is described there as attaching the region as an unnamed user file. Not confirmed on a title; the RED engine's PS4 crash handler ([Vangaurd](https://github.com/vurimad/Vangaurd/blob/main/source/imported/common/redSystem/src/errorHandlerImplErrorHooksOrbis.cpp)) calls the sibling `sceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, name)` from inside its handler. Attaching belongs inside the core dump handler, which the system runs on its own thread after the process stops (SharpProspero's remark, the RED engine's usage and `SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER`, "function was not called from registered core dump handler", in the Vita's SceCoredump error table, [RivetDB](https://github.com/CreepNT/RivetDB/blob/master/rivetdb.json)). The handler is never invoked here, so every call is outside it and returns that error; nothing is recorded, as there is no dump to attach to. Its value `0x81180003` comes from shadPS4 (PS4), not confirmed on a PS5: [fpPS4](https://github.com/red-prig/fpPS4/blob/master/kernel/ps4_libkernel.pas) has `NOT_REGISTERED` 0x81180001 and `ALREADY_REGISTERED` 0x81180002, shadPS4's `coredump_error.h` has `NOT_IN_COREDUMP_HANDLER` = 0x81180003, and the Vita table numbers `PARAM` 0, `NOT_REGISTERED` 1, `ALREADY_REGISTERED` 2 and `NOT_IN_COREDUMP_HANDLER` 3. Whether the console checks the region before the calling context is unknown, so a null, empty or wrapping region throws. `userValue` is not interpreted; SharpProspero names its bits 31 and 30 as truncation flags for the user file variants +- [sceCoredumpAttachMemoryRegion, sceCoredumpAttachUserMemoryFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) cannot attach memory to an active coredump. The native signed 32-bit length is recovered: negative lengths are ignored successfully; other calls return the outside-handler error without accessing memory. Capture and kernel-side record limits remain unimplemented. - [sceCoredumpWriteUserData, sceCoredumpDebugTextOut](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) have no active coredump report. WriteUserData validates a nonnull buffer and size at most `INT32_MAX`, including zero; accepted requests return the sign-extended outside-handler error. DebugTextOut still prints to stderr instead of a report; its validation and report integration remain unverified. - [sceCoredumpAttachUserFile, sceCoredumpAttachMemoryRegionAsUserFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - the signatures `int (uint32_t userValue, const char* path)` and `int (uint32_t userValue, const void* mem, size_t size, const char* name)` follow the [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json) IDA type database and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelSystem.cs) bindings. The crash handler calls both from inside its core dump handler, `sceCoredumpAttachUserFile(priority, absolutePath)` and `sceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, "LoggerQueueCrashDump.txt")`. The handler is never invoked here, so both return `SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER` (0x81180003, PS4 numbering from shadPS4) and attach nothing; whether the console checks the arguments before the calling context is unknown, so a null path or name and a null, empty or wrapping region throw. An empty path or name is passed through to that error, and `userValue` is not interpreted. No title is seen calling either - [sceKernelAddTimerEvent](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - `usec` is taken as the period in microseconds; re-adding an existing timer keeps its pending expiration count and restarts the period from the time of the call, where FreeBSD 12+ `filt_timertouch` clears pending expirations on `EV_ADD`; `usec == 0` fires on every wait. None of this is confirmed on a PS5 title From f4447aa2520b2a2e286f424d8c8668ef30eabddf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:26:44 +0300 Subject: [PATCH 505/523] fix(kernel): restore coredump registration and file flag validation --- .../prx/libkernel/System/src/Coredump.cpp | 35 ++++++++------- core/libs/tests/GuestCoredumpAttach.cpp | 9 ++++ core/libs/tests/GuestCoredumpUserData.cpp | 44 ++++++++++++++++--- docs/dev/debt/libkernel.md | 4 +- 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/Coredump.cpp b/core/libs/prx/libkernel/System/src/Coredump.cpp index 07cb01cbe8..f618d1e2ea 100644 --- a/core/libs/prx/libkernel/System/src/Coredump.cpp +++ b/core/libs/prx/libkernel/System/src/Coredump.cpp @@ -2,37 +2,41 @@ #include #include #include +#include #include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" // The handler is recorded but never invoked: host crashes are not turned into guest core dumps. static std::atomic g_coredumpHandler{0}; static std::atomic g_coredumpContext{0}; +static std::atomic coredumpSdkVersion{0}; static constexpr int SCE_COREDUMP_ERROR_INVALID_ARGUMENT = static_cast(0x81180000u); static constexpr int SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER = static_cast(0x81180003u); -static void requireRegionOutsideHandler(const char* function, const void* mem, size_t size) { - if (mem == nullptr || size == 0 || size > UINTPTR_MAX - reinterpret_cast(mem)) - throw std::runtime_error(std::string(function) + ": null, empty or wrapping region outside the core dump handler"); -} - -static void requireTextOutsideHandler(const char* function, const char* argument, const char* text) { - if (text == nullptr) throw std::runtime_error(std::string(function) + ": null " + argument + " outside the core dump handler"); +static bool validUserFileFlags(std::uint32_t userValue) { + return coredumpSdkVersion.load(std::memory_order_relaxed) < 0x06500000 || (userValue & 0xc0000000u) != 0x40000000u; } extern "C" { int APS5_VABI sceCoredumpRegisterCoredumpHandler(uint64_t handler, size_t stack_size, uint64_t context) { - (void)stack_size; + if (g_coredumpHandler.load(std::memory_order_relaxed)) return static_cast(0x81180002u); + if (!handler || stack_size < 0x4000 || stack_size >= 0x20000000) return SCE_COREDUMP_ERROR_INVALID_ARGUMENT; + const auto* parameters = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t sdkVersion; + std::memcpy(&sdkVersion, parameters + 16, sizeof(sdkVersion)); + coredumpSdkVersion.store(sdkVersion, std::memory_order_relaxed); g_coredumpHandler.store(handler, std::memory_order_relaxed); g_coredumpContext.store(context, std::memory_order_relaxed); return 0; } int APS5_VABI sceCoredumpUnregisterCoredumpHandler(void) { + if (!g_coredumpHandler.load(std::memory_order_relaxed)) return static_cast(0x81180001u); g_coredumpHandler.store(0, std::memory_order_relaxed); g_coredumpContext.store(0, std::memory_order_relaxed); return 0; @@ -45,9 +49,9 @@ int APS5_VABI sceKernelDebugWriteCppExceptionInfo(const void* exception, uint64_ } -int APS5_VABI sceCoredumpAttachUserFile(uint32_t userValue, const char* path) { - (void)userValue; - requireTextOutsideHandler(__func__, "path", path); +int APS5_VABI sceCoredumpAttachUserFile(std::uint32_t userValue, const char* path) { + if (!path || !validUserFileFlags(userValue)) return SCE_COREDUMP_ERROR_INVALID_ARGUMENT; + if (static_cast(std::strlen(path)) < 0) return 0; return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } @@ -57,11 +61,10 @@ int APS5_VABI sceCoredumpGetStopInfoGpu_Agc(void* info, size_t size) { return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } -int APS5_VABI sceCoredumpAttachMemoryRegionAsUserFile(uint32_t userValue, const void* mem, size_t size, const char* name) { - (void)userValue; - requireRegionOutsideHandler(__func__, mem, size); - requireTextOutsideHandler(__func__, "name", name); - return SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; +int APS5_VABI sceCoredumpAttachMemoryRegionAsUserFile(std::uint32_t userValue, const void* mem, std::int32_t size, const char* name) { + (void)mem; + if (!name || !validUserFileFlags(userValue)) return SCE_COREDUMP_ERROR_INVALID_ARGUMENT; + return size < 0 ? 0 : SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER; } int APS5_VABI sceCoredumpSetUserDataType(std::int32_t type) { diff --git a/core/libs/tests/GuestCoredumpAttach.cpp b/core/libs/tests/GuestCoredumpAttach.cpp index d3d5466b57..5d3b7ebb5e 100644 --- a/core/libs/tests/GuestCoredumpAttach.cpp +++ b/core/libs/tests/GuestCoredumpAttach.cpp @@ -1,3 +1,4 @@ +#include "ProcessParametersFixture.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -20,6 +21,7 @@ int APS5_VABI sceCoredumpConfigDumpMode(int mode); } namespace { +alignas(16) std::array process{}; constexpr int InvalidArgument = static_cast(0x81180000u); constexpr int NotInCoredumpHandler = static_cast(0x81180003u); constexpr uint32_t UserFileTruncateIfExceedLimit = 0x80000000u; @@ -42,6 +44,13 @@ bool Throws(TAttach attach, const char* function) { } int main() { + const std::uint64_t processSize = process.size(); + const std::uint32_t processMagic = 0x4942524f; + const std::uint32_t sdkVersion = 0x06500000; + std::memcpy(process.data(), &processSize, sizeof(processSize)); + std::memcpy(process.data() + 8, &processMagic, sizeof(processMagic)); + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(ProcessParametersFixture::Install(process.data(), process.size())); alignas(uint64_t) std::array region{}; for (size_t index = 0; index < region.size(); ++index) region[index] = static_cast(index * 7 + 3); const auto original = region; diff --git a/core/libs/tests/GuestCoredumpUserData.cpp b/core/libs/tests/GuestCoredumpUserData.cpp index 3578510707..3b7c6c3817 100644 --- a/core/libs/tests/GuestCoredumpUserData.cpp +++ b/core/libs/tests/GuestCoredumpUserData.cpp @@ -1,3 +1,4 @@ +#include "ProcessParametersFixture.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include @@ -13,10 +14,11 @@ int APS5_VABI sceCoredumpUnregisterCoredumpHandler(void); int64_t APS5_VABI sceCoredumpWriteUserData(const void* data, size_t size); void APS5_VABI sceCoredumpDebugTextOut(const char* str, int len); int APS5_VABI sceCoredumpAttachUserFile(uint32_t userValue, const char* path); -int APS5_VABI sceCoredumpAttachMemoryRegionAsUserFile(uint32_t userValue, const void* mem, size_t size, const char* name); +int APS5_VABI sceCoredumpAttachMemoryRegionAsUserFile(uint32_t userValue, const void* mem, std::int32_t size, const char* name); } namespace { +alignas(16) std::array process{}; constexpr int64_t InvalidArgument = static_cast(0x81180000u); constexpr int64_t NotInCoredumpHandler = static_cast(0x81180003u); bool handlerRan = false; @@ -56,10 +58,23 @@ void RequireAttachesRefused(const std::array& region) { } int main() { + const std::uint64_t processSize = process.size(); + const std::uint32_t processMagic = 0x4942524f; + const std::uint32_t sdkVersion = 0x06500000; + std::memcpy(process.data(), &processSize, sizeof(processSize)); + std::memcpy(process.data() + 8, &processMagic, sizeof(processMagic)); + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(ProcessParametersFixture::Install(process.data(), process.size())); std::array region{}; for (size_t index = 0; index < region.size(); ++index) region[index] = static_cast(index * 5 + 1); const auto original = region; const char text[] = "coredump text\n"; + Require(sceCoredumpUnregisterCoredumpHandler() == static_cast(0x81180001u)); + Require(sceCoredumpRegisterCoredumpHandler(0, 0x4000, 0) == InvalidArgument); + for (const std::size_t stackSize : {std::size_t{0}, std::size_t{0x3fff}, std::size_t{0x20000000}, SIZE_MAX}) { + Require(sceCoredumpRegisterCoredumpHandler(reinterpret_cast(&Handler), stackSize, 0) == InvalidArgument); + } + Require(sceCoredumpAttachUserFile(0x40000000u, "") == NotInCoredumpHandler); Require(NotInCoredumpHandler < 0); RequireWritesRefused(region); @@ -70,6 +85,9 @@ int main() { sceCoredumpDebugTextOut(nullptr, 0); Require(sceCoredumpRegisterCoredumpHandler(reinterpret_cast(&Handler), 16 * 1024, reinterpret_cast(&handlerContext)) == 0); + Require(sceCoredumpRegisterCoredumpHandler(0, 0, 0) == static_cast(0x81180002u)); + Require(sceCoredumpAttachUserFile(0x40000000u, "") == InvalidArgument); + Require(sceCoredumpAttachMemoryRegionAsUserFile(0x40000000u, nullptr, -1, "") == InvalidArgument); RequireWritesRefused(region); RequireAttachesRefused(region); sceCoredumpDebugTextOut(text, static_cast(sizeof(text) - 1)); @@ -83,16 +101,30 @@ int main() { Require(sceCoredumpWriteUserData(region.data(), SIZE_MAX) == InvalidArgument); Require(Throws([&] { sceCoredumpDebugTextOut(nullptr, 4); }, "sceCoredumpDebugTextOut")); Require(Throws([&] { sceCoredumpDebugTextOut(text, -1); }, "sceCoredumpDebugTextOut")); - Require(Throws([&] { sceCoredumpAttachUserFile(1, nullptr); }, "sceCoredumpAttachUserFile")); - Require(Throws([&] { sceCoredumpAttachMemoryRegionAsUserFile(1, nullptr, region.size(), "a.txt"); }, "sceCoredumpAttachMemoryRegionAsUserFile")); - Require(Throws([&] { sceCoredumpAttachMemoryRegionAsUserFile(1, region.data(), 0, "a.txt"); }, "sceCoredumpAttachMemoryRegionAsUserFile")); - Require(Throws([&] { sceCoredumpAttachMemoryRegionAsUserFile(1, region.data(), SIZE_MAX, "a.txt"); }, "sceCoredumpAttachMemoryRegionAsUserFile")); - Require(Throws([&] { sceCoredumpAttachMemoryRegionAsUserFile(1, region.data(), region.size(), nullptr); }, "sceCoredumpAttachMemoryRegionAsUserFile")); + Require(sceCoredumpAttachUserFile(1, nullptr) == InvalidArgument); + Require(sceCoredumpAttachMemoryRegionAsUserFile(1, nullptr, region.size(), "a.txt") == NotInCoredumpHandler); + Require(sceCoredumpAttachMemoryRegionAsUserFile(1, region.data(), 0, "a.txt") == NotInCoredumpHandler); + Require(sceCoredumpAttachMemoryRegionAsUserFile(1, region.data(), -1, "a.txt") == 0); + Require(sceCoredumpAttachMemoryRegionAsUserFile(1, region.data(), region.size(), nullptr) == InvalidArgument); Require(sceCoredumpUnregisterCoredumpHandler() == 0); RequireWritesRefused(region); RequireAttachesRefused(region); Require(region == original); + Require(sceCoredumpUnregisterCoredumpHandler() == static_cast(0x81180001u)); + Require(sceCoredumpAttachUserFile(0x40000000u, "") == InvalidArgument); + for (const std::uint32_t version : {0x064fffffu, 0x06500000u, 0x12000000u}) { + std::memcpy(process.data() + 16, &version, sizeof(version)); + Require(sceCoredumpRegisterCoredumpHandler(reinterpret_cast(&Handler), 0x4000, 0) == 0); + for (const std::uint32_t flags : {0u, 0xffffu, 0x10000u, 0x40000000u, 0x40000001u, 0x80000000u, 0xc0000000u, UINT32_MAX}) { + const bool invalid = version >= 0x06500000 && (flags & 0xc0000000u) == 0x40000000u; + Require(sceCoredumpAttachUserFile(flags, "") == (invalid ? InvalidArgument : NotInCoredumpHandler)); + Require(sceCoredumpAttachMemoryRegionAsUserFile(flags, nullptr, 0, "") == (invalid ? InvalidArgument : NotInCoredumpHandler)); + Require(sceCoredumpAttachMemoryRegionAsUserFile(flags, nullptr, -1, "") == (invalid ? InvalidArgument : 0)); + Require(sceCoredumpAttachMemoryRegionAsUserFile(flags, nullptr, -1, nullptr) == InvalidArgument); + } + Require(sceCoredumpUnregisterCoredumpHandler() == 0); + } Require(!handlerRan); return 0; } diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index d8c3a355a9..52eea8e8bb 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -12,6 +12,8 @@ ### Unknown function info +- [sceCoredumpRegisterCoredumpHandler, sceCoredumpUnregisterCoredumpHandler](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) retain registration metadata but do not create the native report thread or deliver callbacks. Native registration-state precedence, stack bounds and SDK capture are recovered; thread-creation failures and active-report synchronization remain unimplemented. + - [sceKernelError](../../../core/libs/prx/libkernel/System/src/Error.cpp) follows [shadPS4's kernel implementation](https://github.com/shadps4-emu/shadPS4/blob/23c41333822fe8eca4c609b43119180af63ae4ac/src/core/libraries/kernel/kernel.cpp): zero returns zero; other guest POSIX error values receive the `0x80020000` bias without modifying errno. Arithmetic is defined modulo 32 bits to avoid signed overflow for inputs outside the errno domain. Console behavior for those inputs and PS5 equivalence have not been measured. - [physhm_open, physhm_unlink](../../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel, NIDs `n371J5cP+uo` and `AUqJNkobQ1c`) - declared as throwing stubs: the PS5 physical shared memory API is not documented, and [shadPS4's module generator](https://github.com/shadps4-emu/moduleGenerator/blob/main/libraries/kernel.cpp) also declares both with no arguments and returns `ORBIS_OK`, so the signature, the handle a success would return and the console's behaviour are unmeasured. The no-argument `int` signature follows shadPS4. Forever Launcher (PPSA99030) imports both and stops at load on `physhm_open`, and the relinker resolves no call site for either in that title, so no handle is fabricated and both throw - [getlogin_r](../../../core/libs/prx/libkernel/System/src/Login.cpp) (libkernel) returns the host effective account name: `GetUserNameW` as UTF-8 on Windows and `getpwuid_r(geteuid())` on POSIX. A valid account returns a name even without a login session; host `getlogin_r`, `USER` and `LOGNAME` are not consulted. The console's login name and behaviour without a session are unknown. Account lookup or encoding failures, a missing or empty account name, and POSIX account records exceeding the 16 KiB lookup buffer throw; there is no replacement name or retry. A short guest buffer returns `ERANGE` without modifying it, and a null output returns `EFAULT`. @@ -87,7 +89,7 @@ - [sceCoredumpGetThreadContextInfo](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) validates a nonnull 144-byte output using the native checks. Active-handler register capture remains unimplemented; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output. - [sceCoredumpAttachMemoryRegion, sceCoredumpAttachUserMemoryFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) cannot attach memory to an active coredump. The native signed 32-bit length is recovered: negative lengths are ignored successfully; other calls return the outside-handler error without accessing memory. Capture and kernel-side record limits remain unimplemented. - [sceCoredumpWriteUserData, sceCoredumpDebugTextOut](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) have no active coredump report. WriteUserData validates a nonnull buffer and size at most `INT32_MAX`, including zero; accepted requests return the sign-extended outside-handler error. DebugTextOut still prints to stderr instead of a report; its validation and report integration remain unverified. -- [sceCoredumpAttachUserFile, sceCoredumpAttachMemoryRegionAsUserFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel) - the signatures `int (uint32_t userValue, const char* path)` and `int (uint32_t userValue, const void* mem, size_t size, const char* name)` follow the [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json) IDA type database and the [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelSystem.cs) bindings. The crash handler calls both from inside its core dump handler, `sceCoredumpAttachUserFile(priority, absolutePath)` and `sceCoredumpAttachMemoryRegionAsUserFile(999, buffer, bytes, "LoggerQueueCrashDump.txt")`. The handler is never invoked here, so both return `SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER` (0x81180003, PS4 numbering from shadPS4) and attach nothing; whether the console checks the arguments before the calling context is unknown, so a null path or name and a null, empty or wrapping region throw. An empty path or name is passed through to that error, and `userValue` is not interpreted. No title is seen calling either +- [sceCoredumpAttachUserFile, sceCoredumpAttachMemoryRegionAsUserFile](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) cannot write files to an active coredump. Native null-name checks, the signed 32-bit memory length and SDK 6.50 flag validation are recovered. Nonnegative requests return the outside-handler error; negative memory lengths are ignored after name/flag validation. Kernel-side path handling and capture limits remain unimplemented. - [sceKernelAddTimerEvent](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - `usec` is taken as the period in microseconds; re-adding an existing timer keeps its pending expiration count and restarts the period from the time of the call, where FreeBSD 12+ `filt_timertouch` clears pending expirations on `EV_ADD`; `usec == 0` fires on every wait. None of this is confirmed on a PS5 title - [sceKernelWaitEqueue](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) - a level-triggered user event (`sceKernelAddUserEvent`) is reported at most once per call, as in FreeBSD `kqueue_scan` (`sys/kern/kern_event.c`), which re-queues a reported knote without `EV_CLEAR` or `EV_DISPATCH` behind the scan marker, and kevent(2), where several triggers do not produce several kevents. Not measured on a console. Events are scanned in list order and the list is not rotated, so when `num` is smaller than the number of ready level events the later ones can starve, where FreeBSD moves a reported level knote to the tail. Several completions of one AMPR or VideoOut event within one call are returned as separate entries, where FreeBSD would aggregate them; the Orbis behaviour is unconfirmed - [AC1FtjqMCL0](../../../core/libs/prx/libkernel/Unimplemented.cpp) (libkernel) - unknown name, signature From 0f668a2ea86c7c7c92c4c53ae8c7f267153cf6e5 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:27:45 +0300 Subject: [PATCH 506/523] fix(voice): restore start memory checks and lifecycle errors --- core/libs/prx/libSceVoice/Export.cpp | 12 ++++++---- core/libs/tests/GuestVoice.cpp | 33 ++++++++++++++++++++++++++++ docs/dev/debt/libSceVoice.md | 2 ++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libSceVoice/Export.cpp b/core/libs/prx/libSceVoice/Export.cpp index c070175dc4..6573ceaffd 100644 --- a/core/libs/prx/libSceVoice/Export.cpp +++ b/core/libs/prx/libSceVoice/Export.cpp @@ -75,6 +75,7 @@ int APS5_VABI sceVoiceInit(const VoiceInitParam* param, std::int32_t version) { if (!param || version != 100) return InvalidArgument; if (param->app_type != 4 && param->app_type != 0x08000000 && param->app_type != 0x10000000 && param->app_type != 0x20000000) return InvalidArgument; voice.appType = param->app_type; + voice.nextId = 0; voice.initialized = true; return 0; } @@ -93,8 +94,11 @@ int APS5_VABI sceVoiceSetThreadsParams(const VoiceThreadsParams* params) { int APS5_VABI sceVoiceStart(const VoiceStartParam* param) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) return VoiceErrorNotInitialized; - if (param == nullptr) APS5_INVALID_ARG_EX; + if (!voice.initialized) return NotInitialized; + if (!param || !param->container) return InvalidArgument; + if (voice.started) return 0; + const std::uint32_t memorySize = voice.appType == 0x10000000 || voice.appType == 0x20000000 ? 0x40000 : 0xc0000; + if (param->mem_size != memorySize) return static_cast(0x804e0806u); voice.started = true; return 0; } @@ -102,7 +106,7 @@ int APS5_VABI sceVoiceStart(const VoiceStartParam* param) { int APS5_VABI sceVoiceStop(void) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) Fail(__func__, "library not initialized"); + if (!voice.initialized) return NotInitialized; voice.started = false; return 0; } @@ -110,7 +114,7 @@ int APS5_VABI sceVoiceStop(void) { int APS5_VABI sceVoiceEnd_nid_postfix(void) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) Fail(__func__, "library not initialized"); + if (!voice.initialized) return NotInitialized; voice.ports.clear(); voice.connections.clear(); voice.started = false; diff --git a/core/libs/tests/GuestVoice.cpp b/core/libs/tests/GuestVoice.cpp index 4f18783667..7bf4ab9a1c 100644 --- a/core/libs/tests/GuestVoice.cpp +++ b/core/libs/tests/GuestVoice.cpp @@ -5,11 +5,13 @@ #include #include #include +#include extern "C" { int APS5_VABI sceVoiceInit(const VoiceInitParam*, std::int32_t); int APS5_VABI sceVoiceSetThreadsParams(const VoiceThreadsParams*); int APS5_VABI sceVoiceStart(const VoiceStartParam*); +int APS5_VABI sceVoiceStop(); int APS5_VABI sceVoiceEnd_nid_postfix(); int APS5_VABI sceVoiceCreatePort(std::uint32_t*, const VoicePortParam*); int APS5_VABI sceVoiceDeletePort(std::uint32_t); @@ -106,6 +108,11 @@ int main() { Require(sceVoiceGetResourceInfo(nullptr) == notInitialized); } VoiceStartParam start{}; + Require(sceVoiceStart(nullptr) == notInitialized); + Require(sceVoiceStart(&start) == notInitialized); + Require(sceVoiceStop() == notInitialized); + Require(sceVoiceEnd_nid_postfix() == notInitialized); + std::vector voiceMemory(0xc0000); Require(sceVoiceInit(&init, 100) == 0); Require(sceVoiceInit(nullptr, 0) == static_cast(0x804e0802u)); Require(sceVoiceInit(&init, 100) == static_cast(0x804e0802u)); @@ -118,7 +125,19 @@ int main() { threads.mode = 1; RequireThrows([&] { sceVoiceSetThreadsParams(&threads); }); threads.mode = 0; + Require(sceVoiceStart(nullptr) == invalidArgument); + Require(sceVoiceStart(&start) == invalidArgument); + start.container = voiceMemory.data(); + Require(sceVoiceStart(&start) == static_cast(0x804e0806u)); + start.mem_size = 0x40000; Require(sceVoiceStart(&start) == 0); + start.mem_size = 0; + Require(sceVoiceStart(&start) == 0); + Require(sceVoiceStart(nullptr) == invalidArgument); + start.container = nullptr; + Require(sceVoiceStart(&start) == invalidArgument); + start.container = voiceMemory.data(); + start.mem_size = 0x40000; Require(sceVoiceSetThreadsParams(nullptr) == invalidArgument); Require(sceVoiceSetThreadsParams(&threads) == static_cast(0x804e080bu)); Require(sceVoiceGetResourceInfo(nullptr) == invalidArgument); @@ -192,6 +211,20 @@ int main() { for (const auto mode : {4, 0x08000000, 0x10000000, 0x20000000}) { init.app_type = mode; Require(sceVoiceInit(&init, 100) == 0); + const std::uint32_t memorySize = mode == 4 || mode == 0x08000000 ? 0xc0000 : 0x40000; + start.mem_size = memorySize - 1; + Require(sceVoiceStart(&start) == static_cast(0x804e0806u)); + start.mem_size = memorySize + 1; + Require(sceVoiceStart(&start) == static_cast(0x804e0806u)); + start.mem_size = memorySize; + Require(sceVoiceStart(&start) == 0); + Require(sceVoiceStop() == 0); + Require(sceVoiceStop() == 0); + Require(sceVoiceSetThreadsParams(&threads) == 0); + VoicePortParam portParam{}; + portParam.port_type = 0; + std::uint32_t port = UINT32_MAX; + Require(sceVoiceCreatePort(&port, &portParam) == 0 && port == 0); Require(sceVoiceGetResourceInfo(&resourceOutput.info) == 0); Require(resourceOutput.info.maxVoiceInputPorts == (mode == 4 ? 0 : mode == 0x20000000 ? 7 : 15)); Require(resourceOutput.info.maxVoiceOutputPorts == (mode == 4 ? 0 : 4)); diff --git a/docs/dev/debt/libSceVoice.md b/docs/dev/debt/libSceVoice.md index db5a284c09..9cea739a9c 100644 --- a/docs/dev/debt/libSceVoice.md +++ b/docs/dev/debt/libSceVoice.md @@ -22,3 +22,5 @@ - [sceVoiceGetPortAttr](../../../core/libs/prx/libSceVoice/Export.cpp): native initialization, port-range, pointer, attribute-size and port-type validation is implemented. Supported device attributes throw because the host has no device state or voice-activity processing. The former constant-zero voice-activity fallback is removed; failed calls leave output untouched. - [sceVoiceSetThreadsParams](../../../core/libs/prx/libSceVoice/Export.cpp): the 48-byte mode/priority/affinity layout and initialization, null, started-state and mode validation are recovered. Mode 1 throws because worker scheduling is not implemented; mode 0 is the native no-op. The 32-byte event callback union is recovered, but callback delivery and device-state byte semantics remain unimplemented. + +- [sceVoiceStart, sceVoiceStop and sceVoiceEnd](../../../core/libs/prx/libSceVoice/Export.cpp) validate initialization, container presence and native mode-dependent memory sizes, but do not partition guest working memory or run the audio workers. Start/stop currently change software state only; backend failures and active-stream shutdown are not reproduced. From 09fb692fa1e184f7435151e2b3f2aab6e0d9b84d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:33:18 +0300 Subject: [PATCH 507/523] fix(voice): restore port format validation and resource limits --- core/libs/prx/libSceVoice/Export.cpp | 76 +++++++++++++--- core/libs/tests/GuestVoice.cpp | 120 +++++++++++++++++++++++++ core/libs/tests/GuestVoicePortAttr.cpp | 7 ++ docs/dev/debt/libSceVoice.md | 2 +- 4 files changed, 194 insertions(+), 11 deletions(-) diff --git a/core/libs/prx/libSceVoice/Export.cpp b/core/libs/prx/libSceVoice/Export.cpp index 6573ceaffd..fc13a6b73a 100644 --- a/core/libs/prx/libSceVoice/Export.cpp +++ b/core/libs/prx/libSceVoice/Export.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ struct Port { std::int32_t type; float volume; std::uint32_t bitrate; + std::uint16_t bufferSize; + std::uint16_t frameSize; bool muted = false; bool paused = false; }; @@ -41,7 +44,7 @@ struct Voice { bool initialized = false; bool started = false; std::int32_t appType = 0; - std::uint32_t nextId = 0; + std::deque freePorts; std::map ports; std::set> connections; }; @@ -75,7 +78,8 @@ int APS5_VABI sceVoiceInit(const VoiceInitParam* param, std::int32_t version) { if (!param || version != 100) return InvalidArgument; if (param->app_type != 4 && param->app_type != 0x08000000 && param->app_type != 0x10000000 && param->app_type != 0x20000000) return InvalidArgument; voice.appType = param->app_type; - voice.nextId = 0; + voice.freePorts.clear(); + for (std::uint32_t id = 0; id < 128; ++id) voice.freePorts.push_back(id); voice.initialized = true; return 0; } @@ -123,16 +127,67 @@ int APS5_VABI sceVoiceEnd_nid_postfix(void) { return 0; } -int APS5_VABI sceVoiceCreatePort(uint32_t* port_id, const VoicePortParam* param) { +int APS5_VABI sceVoiceCreatePort(std::uint32_t* portId, const VoicePortParam* param) { auto& voice = State(); std::lock_guard lock(voice.mutex); - if (!voice.initialized) return VoiceErrorNotInitialized; - if (port_id == nullptr || param == nullptr) APS5_INVALID_ARG_EX; - if (param->port_type < PortInDevice || param->port_type > PortOutDevice) Fail(__func__, "unknown port type " + std::to_string(param->port_type)); - const bool voicePort = param->port_type == PortInVoice || param->port_type == PortOutVoice; - const auto id = voice.nextId++; - voice.ports.emplace(id, Port{param->port_type, param->volume, voicePort ? static_cast(param->voice.bitrate) : 0u}); - *port_id = id; + if (!voice.initialized) return NotInitialized; + if (!portId || !param) return InvalidArgument; + *portId = 255; + std::uint16_t frameSize = 512; + std::uint16_t frameDuration = 16; + const auto type = param->port_type; + if (type < PortInDevice || type > PortOutDevice) return InvalidArgument; + if (type == PortInPcm || type == PortOutPcm) { + if (param->pcmaudio.sample_rate != 16000 || param->pcmaudio.data_type != 0) return InvalidArgument; + } else if (type == PortInVoice || type == PortOutVoice) { + switch (param->voice.bitrate) { + case 3850: frameSize = 20; frameDuration = 40; break; + case 4650: frameSize = 24; frameDuration = 40; break; + case 5700: frameSize = 15; frameDuration = 20; break; + case 7300: frameSize = 19; frameDuration = 20; break; + case 11800: frameSize = 15; frameDuration = 10; break; + case 12200: frameSize = 16; frameDuration = 10; break; + case 14400: frameSize = 27; frameDuration = 15; break; + case 16000: frameSize = 20; frameDuration = 10; break; + case 22533: frameSize = 43; frameDuration = 15; break; + default: return InvalidArgument; + } + } + constexpr auto resourceFull = static_cast(0x804e0808u); + if (voice.freePorts.empty()) return resourceFull; + std::uint32_t count = 0; + std::uint32_t pcmCount = 0; + std::uint32_t pcmBytes = 0; + for (const auto& [id, port] : voice.ports) { + if (port.type == type) ++count; + if (port.type == PortInPcm || port.type == PortOutPcm) { + ++pcmCount; + pcmBytes += 2u * port.bufferSize; + } + } + std::uint16_t bufferSize = 2048; + if (type == PortInDevice || type == PortOutDevice) { + const std::uint32_t limit = type == PortOutDevice && voice.appType == 4 ? 5 : 4; + if (count == limit) return resourceFull; + if (type == PortInDevice ? param->device.type != 0 : param->device.type != 2 && param->device.type != 14) return InvalidArgument; + bufferSize = 4096; + } else if (type == PortInPcm || type == PortOutPcm) { + bufferSize = static_cast(param->pcmaudio.buffer_size - param->pcmaudio.buffer_size % frameSize); + if (bufferSize < 1024 || bufferSize > 64512) return InvalidArgument; + if (type == PortInPcm && bufferSize / frameSize * frameDuration < param->threshold) return resourceFull; + const std::uint32_t limit = voice.appType == 4 ? 128 : voice.appType == 0x08000000 ? 30 : 14; + const std::uint32_t byteLimit = voice.appType == 4 || voice.appType == 0x08000000 ? 0x80000 : voice.appType == 0x10000000 ? 0x5000 : 0x10000; + if (pcmCount == limit || pcmBytes + 2u * bufferSize > byteLimit) return resourceFull; + } else { + if (type == PortInVoice && bufferSize / frameSize * frameDuration < param->threshold) return resourceFull; + const std::uint32_t limit = voice.appType == 4 ? 0 : type == PortOutVoice ? 4 : voice.appType == 0x20000000 ? 7 : 15; + if (count == limit) return resourceFull; + } + const auto id = voice.freePorts.front(); + const bool voicePort = type == PortInVoice || type == PortOutVoice; + voice.ports.emplace(id, Port{type, param->volume, voicePort ? static_cast(param->voice.bitrate) : 0u, bufferSize, frameSize, param->mute != 0}); + voice.freePorts.pop_front(); + *portId = id; return 0; } @@ -142,6 +197,7 @@ int APS5_VABI sceVoiceDeletePort(uint32_t port_id) { RequirePort(voice, port_id, __func__); std::erase_if(voice.connections, [port_id](const auto& link) { return link.first == port_id || link.second == port_id; }); voice.ports.erase(port_id); + voice.freePorts.push_back(port_id); return 0; } diff --git a/core/libs/tests/GuestVoice.cpp b/core/libs/tests/GuestVoice.cpp index 7bf4ab9a1c..3cd5219949 100644 --- a/core/libs/tests/GuestVoice.cpp +++ b/core/libs/tests/GuestVoice.cpp @@ -73,6 +73,11 @@ std::uint32_t CreatePort(std::int32_t type, float volume) { param.port_type = type; param.volume = volume; if (type == 2 || type == 4) param.voice.bitrate = 16000; + if (type == 1 || type == 3) { + param.pcmaudio.buffer_size = 1024; + param.pcmaudio.sample_rate = 16000; + } + if (type == 5) param.device.type = 2; std::uint32_t id = 0; Require(sceVoiceCreatePort(&id, ¶m) == 0); return id; @@ -234,4 +239,119 @@ int main() { Require(sceVoiceEnd_nid_postfix() == 0); } + constexpr auto resourceFull = static_cast(0x804e0808u); + std::uint32_t portId = 123; + VoicePortParam param{}; + Require(sceVoiceCreatePort(nullptr, nullptr) == notInitialized); + Require(sceVoiceCreatePort(&portId, ¶m) == notInitialized && portId == 123); + for (const auto mode : {4, 0x08000000, 0x10000000, 0x20000000}) { + init.app_type = mode; + Require(sceVoiceInit(&init, 100) == 0); + Require(sceVoiceCreatePort(&portId, nullptr) == invalidArgument && portId == 123); + Require(sceVoiceCreatePort(nullptr, ¶m) == invalidArgument); + for (const auto type : {-1, 6, INT32_MAX}) { + param = {}; + param.port_type = type; + Require(sceVoiceCreatePort(&portId, ¶m) == invalidArgument && portId == 255); + } + for (const auto type : {0, 2, 4, 5}) { + param = {}; + param.port_type = type; + if (type == 2 || type == 4) param.voice.bitrate = 16000; + if (type == 5) param.device.type = 14; + const auto limit = type == 0 ? 4 : type == 5 ? (mode == 4 ? 5 : 4) : mode == 4 ? 0 : type == 4 ? 4 : mode == 0x20000000 ? 7 : 15; + std::vector ports; + for (int i = 0; i < limit; ++i) { + Require(sceVoiceCreatePort(&portId, ¶m) == 0 && portId < 128); + ports.push_back(portId); + } + Require(sceVoiceCreatePort(&portId, ¶m) == resourceFull && portId == 255); + if (type == 0 || type == 5) { + param.device.type = 1; + Require(sceVoiceCreatePort(&portId, ¶m) == resourceFull); + param.device.type = type == 0 ? 0 : 2; + } + for (auto id : ports) Require(sceVoiceDeletePort(id) == 0); + if (limit != 0) { + Require(sceVoiceCreatePort(&portId, ¶m) == 0); + Require(sceVoiceDeletePort(portId) == 0); + } + } + param = {}; + param.port_type = 3; + param.pcmaudio.sample_rate = 16000; + param.pcmaudio.buffer_size = 1024; + const auto limit = mode == 4 ? 128 : mode == 0x08000000 ? 30 : mode == 0x10000000 ? 10 : 14; + std::vector ports; + for (int i = 0; i < limit; ++i) { + Require(sceVoiceCreatePort(&portId, ¶m) == 0 && portId < 128); + for (auto id : ports) Require(id != portId); + ports.push_back(portId); + } + Require(sceVoiceCreatePort(&portId, ¶m) == resourceFull && portId == 255); + const auto released = ports.front(); + Require(sceVoiceDeletePort(released) == 0); + Require(sceVoiceCreatePort(&portId, ¶m) == 0); + if (mode == 4) Require(portId == released); + Require(sceVoiceEnd_nid_postfix() == 0); + portId = 123; + } + init.app_type = 0x08000000; + Require(sceVoiceInit(&init, 100) == 0); + for (const auto type : {0, 5}) { + param = {}; + param.port_type = type; + for (const auto deviceType : {-1, 0, 1, 2, 3, 13, 14, 15, INT32_MAX}) { + param.device.type = deviceType; + const bool valid = type == 0 ? deviceType == 0 : deviceType == 2 || deviceType == 14; + Require(sceVoiceCreatePort(&portId, ¶m) == (valid ? 0 : invalidArgument)); + if (valid) Require(sceVoiceDeletePort(portId) == 0); + else Require(portId == 255); + } + } + for (const auto type : {1, 3}) { + param = {}; + param.port_type = type; + param.pcmaudio.buffer_size = 1024; + Require(sceVoiceCreatePort(&portId, ¶m) == invalidArgument); + param.pcmaudio.sample_rate = 16000; + param.pcmaudio.data_type = 1; + Require(sceVoiceCreatePort(&portId, ¶m) == invalidArgument); + param.pcmaudio.data_type = 0; + for (const std::uint32_t bytes : {0u, 511u, 512u, 1023u, 1024u, 1535u, 64512u, 65023u, 65024u, 65535u, 65536u, 66560u, UINT32_MAX}) { + param.pcmaudio.buffer_size = bytes; + const auto rounded = static_cast(bytes - bytes % 512); + const bool valid = rounded >= 1024 && rounded <= 64512; + Require(sceVoiceCreatePort(&portId, ¶m) == (valid ? 0 : invalidArgument)); + if (valid) Require(sceVoiceDeletePort(portId) == 0); + } + param.pcmaudio.buffer_size = 1024; + param.threshold = 33; + Require(sceVoiceCreatePort(&portId, ¶m) == (type == 1 ? resourceFull : 0)); + if (type == 3) Require(sceVoiceDeletePort(portId) == 0); + param.threshold = 32; + Require(sceVoiceCreatePort(&portId, ¶m) == 0); + Require(sceVoiceDeletePort(portId) == 0); + } + for (const auto type : {2, 4}) { + param = {}; + param.port_type = type; + for (const auto bitrate : {-1, 0, 3849, 3850, 4650, 5700, 7300, 11800, 12200, 14400, 16000, 16001, 22533, 22534}) { + param.voice.bitrate = bitrate; + const bool valid = bitrate == 3850 || bitrate == 4650 || bitrate == 5700 || bitrate == 7300 || bitrate == 11800 || bitrate == 12200 || bitrate == 14400 || bitrate == 16000 || bitrate == 22533; + Require(sceVoiceCreatePort(&portId, ¶m) == (valid ? 0 : invalidArgument)); + if (valid) Require(sceVoiceDeletePort(portId) == 0); + } + param.voice.bitrate = 16000; + param.threshold = 1021; + Require(sceVoiceCreatePort(&portId, ¶m) == (type == 2 ? resourceFull : 0)); + if (type == 4) Require(sceVoiceDeletePort(portId) == 0); + param.threshold = 1020; + param.mute = 1; + Require(sceVoiceCreatePort(&portId, ¶m) == 0); + Require(sceVoiceGetMuteFlag(portId, &muted) == 0 && muted); + Require(sceVoiceDeletePort(portId) == 0); + } + Require(sceVoiceEnd_nid_postfix() == 0); + } diff --git a/core/libs/tests/GuestVoicePortAttr.cpp b/core/libs/tests/GuestVoicePortAttr.cpp index e7d05b55d9..ceaa036b65 100644 --- a/core/libs/tests/GuestVoicePortAttr.cpp +++ b/core/libs/tests/GuestVoicePortAttr.cpp @@ -43,7 +43,14 @@ int main() { Require(sceVoiceGetPortAttr(128, 1001, nullptr, 1) == invalidPort); Require(sceVoiceGetPortAttr(0, 1001, nullptr, 1) == invalidArgument); for (std::int32_t type = 0; type <= 5; ++type) { + param = {}; param.port_type = type; + if (type == 1 || type == 3) { + param.pcmaudio.buffer_size = 1024; + param.pcmaudio.sample_rate = 16000; + } + if (type == 2 || type == 4) param.voice.bitrate = 16000; + if (type == 5) param.device.type = 2; std::uint32_t port = 0; Require(sceVoiceCreatePort(&port, ¶m) == 0); for (const auto attr : {1000, 1001, 1002, 1003, 2000, 2001, 2002}) { diff --git a/docs/dev/debt/libSceVoice.md b/docs/dev/debt/libSceVoice.md index 9cea739a9c..32ccc598d9 100644 --- a/docs/dev/debt/libSceVoice.md +++ b/docs/dev/debt/libSceVoice.md @@ -2,7 +2,7 @@ ### Functional -- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization validates native modes and version 100, but still lacks event callback delivery and backend creation; port creation does not enforce native resource limits. +- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization validates native modes and version 100, but still lacks event callback delivery and backend creation. Port creation enforces recovered format and resource limits, but does not allocate device or codec backends. - [sceVoiceEnableChat and sceVoiceDisableChat](../../../core/libs/prx/libSceVoice/Export.cpp): zero-argument signatures and the not-initialized error are confirmed. Initialized calls throw because the required audio chat backend is not implemented; they no longer report success for a local flag change. ### Unknown function info From bfbaf72142a68fb2037d8aebff05c97721f44b08 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:35:15 +0300 Subject: [PATCH 508/523] fix(voice): restore port parameter errors and volume normalization --- core/libs/prx/libSceVoice/Export.cpp | 53 ++++++++++++++++++++-------- core/libs/tests/GuestVoice.cpp | 46 +++++++++++++++++++++++- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/core/libs/prx/libSceVoice/Export.cpp b/core/libs/prx/libSceVoice/Export.cpp index fc13a6b73a..d4f1d3f8a5 100644 --- a/core/libs/prx/libSceVoice/Export.cpp +++ b/core/libs/prx/libSceVoice/Export.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -194,7 +195,9 @@ int APS5_VABI sceVoiceCreatePort(std::uint32_t* portId, const VoicePortParam* pa int APS5_VABI sceVoiceDeletePort(uint32_t port_id) { auto& voice = State(); std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__); + if (!voice.initialized) return NotInitialized; + if (port_id > 127) return InvalidPort; + if (!voice.ports.contains(port_id)) return PortNotFound; std::erase_if(voice.connections, [port_id](const auto& link) { return link.first == port_id || link.second == port_id; }); voice.ports.erase(port_id); voice.freePorts.push_back(port_id); @@ -219,28 +222,40 @@ int APS5_VABI sceVoiceDisconnectIPortFromOPort(uint32_t input_port_id, uint32_t return 0; } -int APS5_VABI sceVoiceGetBitRate(uint32_t port_id, uint32_t* bitrate) { - if (bitrate == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceVoiceGetBitRate(std::uint32_t portId, std::uint32_t* bitrate) { auto& voice = State(); std::lock_guard lock(voice.mutex); - const auto& port = RequirePort(voice, port_id, __func__); - if (port.type != PortInVoice && port.type != PortOutVoice) Fail(__func__, "port does not carry encoded voice"); + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + if (!bitrate) return InvalidArgument; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + const auto& port = found->second; + if (port.type != PortInVoice && port.type != PortOutVoice) return PortNotFound; *bitrate = port.bitrate; return 0; } -int APS5_VABI sceVoiceSetVolume(uint32_t port_id, float volume) { +int APS5_VABI sceVoiceSetVolume(std::uint32_t portId, float volume) { auto& voice = State(); std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__).volume = volume; + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + found->second.volume = std::isfinite(volume) ? volume : 0.0f; return 0; } -int APS5_VABI sceVoiceGetVolume(uint32_t port_id, float* volume) { - if (volume == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceVoiceGetVolume(std::uint32_t portId, float* volume) { auto& voice = State(); std::lock_guard lock(voice.mutex); - *volume = RequirePort(voice, port_id, __func__).volume; + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + if (!volume) return InvalidArgument; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + *volume = found->second.volume; return 0; } @@ -307,18 +322,26 @@ int APS5_VABI sceVoiceWriteToIPort(uint32_t input_port_id, const void* data, uin return 0; } -int APS5_VABI sceVoiceSetMuteFlag(uint32_t port_id, bool muted) { +int APS5_VABI sceVoiceSetMuteFlag(std::uint32_t portId, std::uint8_t muted) { auto& voice = State(); std::lock_guard lock(voice.mutex); - RequirePort(voice, port_id, __func__).muted = muted; + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + found->second.muted = muted != 0; return 0; } -int APS5_VABI sceVoiceGetMuteFlag(uint32_t port_id, bool* muted) { - if (muted == nullptr) APS5_INVALID_ARG_EX; +int APS5_VABI sceVoiceGetMuteFlag(std::uint32_t portId, bool* muted) { auto& voice = State(); std::lock_guard lock(voice.mutex); - *muted = RequirePort(voice, port_id, __func__).muted; + if (!voice.initialized) return NotInitialized; + if (portId > 127) return InvalidPort; + if (!muted) return InvalidArgument; + const auto found = voice.ports.find(portId); + if (found == voice.ports.end()) return PortNotFound; + *muted = found->second.muted; return 0; } diff --git a/core/libs/tests/GuestVoice.cpp b/core/libs/tests/GuestVoice.cpp index 3cd5219949..27e9cb31e2 100644 --- a/core/libs/tests/GuestVoice.cpp +++ b/core/libs/tests/GuestVoice.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -18,7 +20,9 @@ int APS5_VABI sceVoiceDeletePort(std::uint32_t); int APS5_VABI sceVoiceConnectIPortToOPort(std::uint32_t, std::uint32_t); int APS5_VABI sceVoiceDisconnectIPortFromOPort(std::uint32_t, std::uint32_t); int APS5_VABI sceVoiceGetVolume(std::uint32_t, float*); -int APS5_VABI sceVoiceSetMuteFlag(std::uint32_t, bool); +int APS5_VABI sceVoiceSetMuteFlag(std::uint32_t, std::uint8_t); +int APS5_VABI sceVoiceSetVolume(std::uint32_t, float); +int APS5_VABI sceVoiceGetBitRate(std::uint32_t, std::uint32_t*); int APS5_VABI sceVoiceGetMuteFlag(std::uint32_t, bool*); int APS5_VABI sceVoiceGetPortInfo(std::uint32_t, VoicePortInfo*); int APS5_VABI sceVoiceReadFromOPort(std::uint32_t, void*, std::uint32_t*); @@ -91,6 +95,12 @@ int main() { constexpr auto invalidPort = static_cast(0x804e0804u); constexpr auto invalidArgument = static_cast(0x804e0805u); constexpr auto portNotFound = static_cast(0x804e0807u); + Require(sceVoiceGetVolume(128, nullptr) == notInitialized); + Require(sceVoiceGetBitRate(128, nullptr) == notInitialized); + Require(sceVoiceGetMuteFlag(128, nullptr) == notInitialized); + Require(sceVoiceSetVolume(128, 1.0f) == notInitialized); + Require(sceVoiceSetMuteFlag(128, 1) == notInitialized); + Require(sceVoiceDeletePort(128) == notInitialized); Require(sceVoiceResetPort(0) == notInitialized); Require(sceVoicePausePort(0) == notInitialized); Require(sceVoiceResumePort(0) == notInitialized); @@ -352,6 +362,40 @@ int main() { Require(sceVoiceGetMuteFlag(portId, &muted) == 0 && muted); Require(sceVoiceDeletePort(portId) == 0); } + Require(sceVoiceGetVolume(128, nullptr) == invalidPort); + Require(sceVoiceGetBitRate(128, nullptr) == invalidPort); + Require(sceVoiceGetMuteFlag(128, nullptr) == invalidPort); + Require(sceVoiceSetVolume(128, 1.0f) == invalidPort); + Require(sceVoiceSetMuteFlag(128, 1) == invalidPort); + Require(sceVoiceDeletePort(128) == invalidPort); + Require(sceVoiceGetVolume(127, nullptr) == invalidArgument); + Require(sceVoiceGetBitRate(127, nullptr) == invalidArgument); + Require(sceVoiceGetMuteFlag(127, nullptr) == invalidArgument); + volume = 123.0f; + std::uint32_t bitrate = 123; + muted = true; + Require(sceVoiceGetVolume(127, &volume) == portNotFound && volume == 123.0f); + Require(sceVoiceGetBitRate(127, &bitrate) == portNotFound && bitrate == 123); + Require(sceVoiceGetMuteFlag(127, &muted) == portNotFound && muted); + Require(sceVoiceSetVolume(127, 1.0f) == portNotFound); + Require(sceVoiceSetMuteFlag(127, 1) == portNotFound); + Require(sceVoiceDeletePort(127) == portNotFound); + for (const auto type : {0, 1, 2, 3, 4, 5}) { + const auto id = CreatePort(type, -2.0f); + Require(sceVoiceGetBitRate(id, &bitrate) == (type == 2 || type == 4 ? 0 : portNotFound)); + if (type == 2 || type == 4) Require(bitrate == 16000); + for (const float value : {-2.0f, -0.0f, 0.25f, 2.0f, std::numeric_limits::max(), std::numeric_limits::infinity(), -std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}) { + Require(sceVoiceSetVolume(id, value) == 0); + Require(sceVoiceGetVolume(id, &volume) == 0); + Require(volume == (std::isfinite(value) ? value : 0.0f)); + if (value == 0.0f) Require(std::signbit(volume) == std::signbit(value)); + } + for (const std::uint8_t value : {0, 1, 2, 128, 255}) { + Require(sceVoiceSetMuteFlag(id, value) == 0); + Require(sceVoiceGetMuteFlag(id, &muted) == 0 && muted == (value != 0)); + } + Require(sceVoiceDeletePort(id) == 0); + } Require(sceVoiceEnd_nid_postfix() == 0); } From 89567a597f750913ac006fe921003627cb57bf40 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:36:58 +0300 Subject: [PATCH 509/523] fix(hmd2): define gaze object selection abi layouts --- core/libs/prx/libSceHmd2/Export.cpp | 58 ++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index bcc53524e0..d2458b815c 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -89,8 +89,62 @@ struct GazeUserCalibrationStatus { float point[3]; }; struct GazeRawData; -struct GazeObjectSelectionParameter; -struct GazeObjectSelectionResult; +struct GazeObjectSelectionTransform { + float values[16]; +}; + +struct GazeObjectSelectionObject { + std::uint64_t id; + std::uint32_t enabled; + std::uint32_t reserved12; + GazeObjectSelectionTransform transform; + const GazeObjectSelectionTransform* boundingBoxes; + std::uint32_t boundingBoxCount; + std::uint32_t reserved92; +}; + +struct GazeObjectSelectionParameter { + std::uint32_t size; + std::uint32_t reserved4; + std::uint64_t reserved8; + std::uint64_t gazeTimestamp; + GazeObjectSelectionTransform transform; + std::uint32_t selectionMode; + std::uint32_t filterMode; + std::uint64_t timestamp; + const GazeObjectSelectionObject* objects; + std::uint32_t objectCount; + std::uint32_t reserved116; +}; + +struct GazeObjectSelectionResult { + std::uint32_t size; + std::uint32_t selected; + std::uint64_t objectId; + std::uint64_t gazeTimestamp; + std::uint32_t gazeValid; + float origin[3]; + float direction[3]; + float distance; +}; + +static_assert(sizeof(GazeObjectSelectionTransform) == 64); +static_assert(sizeof(GazeObjectSelectionObject) == 96); +static_assert(offsetof(GazeObjectSelectionObject, transform) == 16); +static_assert(offsetof(GazeObjectSelectionObject, boundingBoxes) == 80); +static_assert(offsetof(GazeObjectSelectionObject, boundingBoxCount) == 88); +static_assert(sizeof(GazeObjectSelectionParameter) == 120); +static_assert(offsetof(GazeObjectSelectionParameter, transform) == 24); +static_assert(offsetof(GazeObjectSelectionParameter, selectionMode) == 88); +static_assert(offsetof(GazeObjectSelectionParameter, timestamp) == 96); +static_assert(offsetof(GazeObjectSelectionParameter, objects) == 104); +static_assert(offsetof(GazeObjectSelectionParameter, objectCount) == 112); +static_assert(sizeof(GazeObjectSelectionResult) == 56); +static_assert(offsetof(GazeObjectSelectionResult, objectId) == 8); +static_assert(offsetof(GazeObjectSelectionResult, gazeTimestamp) == 16); +static_assert(offsetof(GazeObjectSelectionResult, origin) == 28); +static_assert(offsetof(GazeObjectSelectionResult, direction) == 40); +static_assert(offsetof(GazeObjectSelectionResult, distance) == 52); struct GazeObjectSelectionInitializeParameter { std::uint32_t size; std::uint32_t maxObjects; From d47b4d729d031f29708c8feec9850ca7e20a32e5 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:39:20 +0300 Subject: [PATCH 510/523] fix(hmd2): define reprojection initialization and output layouts --- core/libs/prx/libSceHmd2/Export.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceHmd2/Export.cpp b/core/libs/prx/libSceHmd2/Export.cpp index d2458b815c..a0dd351bdf 100644 --- a/core/libs/prx/libSceHmd2/Export.cpp +++ b/core/libs/prx/libSceHmd2/Export.cpp @@ -14,8 +14,31 @@ namespace Hmd2 { struct ReprojectionStatus; -struct ReprojectionOutput; -struct ReprojectionInitializeParameter; +struct ReprojectionOutput { + std::uint32_t textureDescriptor[8]; +}; + +struct ReprojectionInitializeParameter { + void* workMemory; + void* displayMemory; + std::int32_t threadPriority; + std::uint32_t threadAffinity; + std::uint32_t queueParameter24; + std::uint32_t queueParameter28; + std::uint32_t renderingMode; + std::uint32_t reserved36; + void* auxiliaryMemory; + std::uint32_t timingInterval; + std::uint32_t reserved52[5]; +}; + +static_assert(sizeof(ReprojectionOutput) == 32); +static_assert(sizeof(ReprojectionInitializeParameter) == 72); +static_assert(offsetof(ReprojectionInitializeParameter, threadPriority) == 16); +static_assert(offsetof(ReprojectionInitializeParameter, threadAffinity) == 20); +static_assert(offsetof(ReprojectionInitializeParameter, renderingMode) == 32); +static_assert(offsetof(ReprojectionInitializeParameter, auxiliaryMemory) == 40); +static_assert(offsetof(ReprojectionInitializeParameter, timingInterval) == 48); struct ReprojectionLayer; struct ReprojectionBufferedLayer; From 870376c65faef22c37a3eadd8702ce21d5681f93 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:48:29 +0300 Subject: [PATCH 511/523] fix(abi): apply guest calling convention to system callbacks --- core/libs/SceTypes.hpp | 14 +++++++------- core/libs/prx/libSceRudp/Export.cpp | 2 +- core/libs/tests/GuestHttp.cpp | 2 +- core/libs/tests/GuestImeOpen.cpp | 4 ++++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 8b3eaa3f89..4a61c38ed7 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1260,8 +1260,8 @@ struct ImeKeycode { std::uint64_t timestamp; }; -using ImeTextFilter = std::int32_t (APS5_VABI *)(char16_t* out_text, std::uint32_t* out_text_length, const char16_t* source_text, std::uint32_t source_text_length); -using ImeExtKeyboardFilter = int (APS5_VABI *)(const ImeKeycode* source_keycode, std::uint16_t* out_keycode, std::uint32_t* out_status, void* reserved); +using ImeTextFilter = std::int32_t (APS5_VABI*)(char16_t* outText, std::uint32_t* outTextLength, const char16_t* sourceText, std::uint32_t sourceTextLength); +using ImeExtKeyboardFilter = int (APS5_VABI*)(const ImeKeycode* sourceKeycode, std::uint16_t* outKeycode, std::uint32_t* outStatus, void* reserved); struct ImeExtendedParam { std::uint32_t option; @@ -1443,10 +1443,10 @@ using NetCtlCallback = void (APS5_VABI *)(int, void*); struct HttpEpoll; using HttpEpollHandle = HttpEpoll*; -using HttpsCallback = int (*)(int, unsigned int, void* const*, int, void*); -using HttpRedirectCallback = int (*)(int, std::int32_t, std::int32_t*, const char*, void*); -using HttpCookieRecvCallback = int (*)(int, const char*, const char*, std::uint64_t, void*); -using HttpAuthInfoCallback = int (*)(int, int, const char*, char*, char*, int, std::uint8_t**, std::uint64_t*, int*, void*); +using HttpsCallback = int (APS5_VABI*)(int, unsigned int, void* const*, int, void*); +using HttpRedirectCallback = int (APS5_VABI*)(int, std::int32_t, std::int32_t*, const char*, void*); +using HttpCookieRecvCallback = int (APS5_VABI*)(int, const char*, const char*, std::uint64_t, void*); +using HttpAuthInfoCallback = int (APS5_VABI*)(int, int, const char*, char*, char*, int, std::uint8_t**, std::uint64_t*, int*, void*); struct HttpNBEvent { std::uint8_t opaque[64]; }; @@ -2068,7 +2068,7 @@ static_assert(sizeof(RudpPollEvent) == 8); static_assert(offsetof(RudpPollEvent, events) == 4); static_assert(offsetof(RudpPollEvent, returnedEvents) == 6); -using RudpEventHandler = void (*)(int ctx_id, int event_id, int error_code, void* arg); +using RudpEventHandler = void (APS5_VABI*)(int ctxId, int eventId, int errorCode, void* arg); struct SystemServiceStatus { std::int32_t event_num = 0; diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 68afe5a022..dcfb2c25be 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -58,7 +58,7 @@ enum RudpOption { RUDP_OPTION_KEEP_ALIVE_TIMEOUT = 19, }; -using GuestRudpEventHandler = void(APS5_VABI*)(int, int, int, void*); +using GuestRudpEventHandler = RudpEventHandler; std::mutex g_mutex; bool g_inited = false; diff --git a/core/libs/tests/GuestHttp.cpp b/core/libs/tests/GuestHttp.cpp index 2d0060ef46..b70b78bba2 100644 --- a/core/libs/tests/GuestHttp.cpp +++ b/core/libs/tests/GuestHttp.cpp @@ -96,7 +96,7 @@ static void CheckUriEscapeBytes() { } } -static int AuthInfo(int, int, const char*, char*, char*, int, std::uint8_t**, std::uint64_t*, int*, void*) { std::abort(); } +static int APS5_VABI AuthInfo(int, int, const char*, char*, char*, int, std::uint8_t**, std::uint64_t*, int*, void*) { std::abort(); } static void APS5_VABI RequestStatusCallback(int, int, void* argument) { ++*static_cast(argument); } diff --git a/core/libs/tests/GuestImeOpen.cpp b/core/libs/tests/GuestImeOpen.cpp index ce516d5530..39c7278c1e 100644 --- a/core/libs/tests/GuestImeOpen.cpp +++ b/core/libs/tests/GuestImeOpen.cpp @@ -69,6 +69,10 @@ void APS5_VABI Record(void* arg, const ImeEvent* event) { if (g_closeOnPressClose && event->id == EventPressClose) g_reentrantClose = sceImeClose_nid_postfix(); } +std::int32_t APS5_VABI TextFilter(char16_t*, std::uint32_t*, const char16_t*, std::uint32_t) { + std::abort(); +} + void APS5_VABI Other(void*, const ImeEvent*) { std::abort(); } From 1e6077d1faf1dd294f0991d69096e41230752edf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:48:31 +0300 Subject: [PATCH 512/523] fix(voice): restore port info frame sizes and output bounds --- core/libs/prx/libSceVoice/Export.cpp | 7 ++----- core/libs/tests/GuestVoice.cpp | 8 ++++++++ docs/dev/debt/libSceVoice.md | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceVoice/Export.cpp b/core/libs/prx/libSceVoice/Export.cpp index d4f1d3f8a5..707d112ea4 100644 --- a/core/libs/prx/libSceVoice/Export.cpp +++ b/core/libs/prx/libSceVoice/Export.cpp @@ -21,10 +21,7 @@ constexpr std::int32_t PortOutPcm = 3; constexpr std::int32_t PortOutVoice = 4; constexpr std::int32_t PortOutDevice = 5; constexpr std::int32_t PortStateReady = 1; -constexpr std::uint32_t VoiceFrameMs = 20; -constexpr std::int32_t PortAttrVoiceActivity = 1001; constexpr int VoiceErrorNotInitialized = static_cast(0x804E0801u); -constexpr int VoiceErrorAlreadyInitialized = static_cast(0x804E0802u); constexpr auto NotInitialized = static_cast(0x804e0801u); constexpr auto InvalidPort = static_cast(0x804e0804u); constexpr auto InvalidArgument = static_cast(0x804e0805u); @@ -265,10 +262,10 @@ int APS5_VABI sceVoiceGetPortInfo(uint32_t port_id, VoicePortInfo* info) { if (!voice.initialized) return VoiceErrorNotInitialized; if (info == nullptr) APS5_INVALID_ARG_EX; const auto& port = RequirePort(voice, port_id, __func__); - *info = VoicePortInfo{}; info->port_type = port.type; info->state = PortStateReady; - if (port.bitrate != 0) info->frame_size = (port.bitrate * VoiceFrameMs + 7999u) / 8000u; + info->byte_count = 0; + info->frame_size = port.bitrate != 0 ? port.frameSize : 2; return 0; } diff --git a/core/libs/tests/GuestVoice.cpp b/core/libs/tests/GuestVoice.cpp index 27e9cb31e2..cb12940acb 100644 --- a/core/libs/tests/GuestVoice.cpp +++ b/core/libs/tests/GuestVoice.cpp @@ -172,8 +172,16 @@ int main() { const auto output = CreatePort(3, 0.25f); Require(sceVoiceConnectIPortToOPort(input, output) == 0); Require(sceVoiceSetMuteFlag(output, true) == 0); + std::uint32_t portIdForInfo = 0; VoicePortInfo before{}; + before.edge = &portIdForInfo; + before.edge_count = 0x1234; + before.reserved = 0x5678; Require(sceVoiceGetPortInfo(input, &before) == 0); + Require(before.frame_size == 20 && before.byte_count == 0); + Require(before.edge == &portIdForInfo && before.edge_count == 0x1234 && before.reserved == 0x5678); + VoicePortInfo pcmInfo{}; + Require(sceVoiceGetPortInfo(output, &pcmInfo) == 0 && pcmInfo.frame_size == 2); Require(sceVoiceResetPort(input) == 0); Require(sceVoiceResetPort(output) == 0); diff --git a/docs/dev/debt/libSceVoice.md b/docs/dev/debt/libSceVoice.md index 32ccc598d9..9e8d8fc46e 100644 --- a/docs/dev/debt/libSceVoice.md +++ b/docs/dev/debt/libSceVoice.md @@ -2,7 +2,7 @@ ### Functional -- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization validates native modes and version 100, but still lacks event callback delivery and backend creation. Port creation enforces recovered format and resource limits, but does not allocate device or codec backends. +- [Voice resource and port state](../../../core/libs/prx/libSceVoice/Export.cpp): resource information is eight bytes containing four 16-bit limits selected by initialization mode. Reset, pause and resume use confirmed initialization, range and missing-port errors; repeated pause/resume succeed. GetPortInfo preserves unused output fields and reports native frame sizes, but still reports a constant ready state and does not reproduce stopped-state or snapshot-slot errors. The host has no buffered voice processing or device reset backend, so reset only covers empty software ports and pause flags currently affect no audio. Initialization validates native modes and version 100, but still lacks event callback delivery and backend creation. Port creation enforces recovered format and resource limits, but does not allocate device or codec backends. - [sceVoiceEnableChat and sceVoiceDisableChat](../../../core/libs/prx/libSceVoice/Export.cpp): zero-argument signatures and the not-initialized error are confirmed. Initialized calls throw because the required audio chat backend is not implemented; they no longer report success for a local flag change. ### Unknown function info From e7a1e5594a319318da016e2bc150796da314117d Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:48:31 +0300 Subject: [PATCH 513/523] fix(ssl): restore ca query errors and sdk unload boundary --- core/libs/prx/libSceSsl/Export.cpp | 23 +++++++++++++++-------- core/libs/tests/GuestSsl.cpp | 30 ++++++++++++++++++++++++------ docs/dev/debt/libSceSsl.md | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index a1bab7e2af..923f584821 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -6,6 +6,7 @@ #endif #include "SslTypes.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libc/include/General.hpp" #include "prx/libkernel/Socket/include/SocketPoll.hpp" @@ -701,17 +702,26 @@ int APS5_VABI sceSslLoadCert(int sslCtxId, int caCertNum, SceSsl::Data** caList, int APS5_VABI sceSslUnloadCert(int sslCtxId) { auto& state = State(); std::lock_guard lock(state.mutex); - auto& context = GetContext(sslCtxId); + const auto found = state.contexts.find(sslCtxId); + if (found == state.contexts.end()) { + const auto* parameters = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t sdkVersion; + std::memcpy(&sdkVersion, parameters + 16, sizeof(sdkVersion)); + return sdkVersion < 0x07500000 ? 0 : ErrorInvalidId; + } + auto& context = *found->second; context.authorities.clear(); context.identity.reset(); return 0; } int APS5_VABI sceSslGetCaList(int sslCtxId, SceSsl::CaList* output) { - if (!output) return ErrorInvalidArg; + if (!output) return ErrorInvalidValue; auto& state = State(); std::lock_guard lock(state.mutex); - auto& context = GetContext(sslCtxId); + const auto found = state.contexts.find(sslCtxId); + if (found == state.contexts.end()) return ErrorInvalidId; + auto& context = *found->second; PoolScope scope(context.pool); const auto count = CertificateCount(context); if (!count) { @@ -743,14 +753,11 @@ int APS5_VABI sceSslFreeCaList(int sslCtxId, SceSsl::CaList* list) { } int APS5_VABI sceSslGetCaCerts(int sslCtxId, SceSsl::CaCerts* output) { - if (!output) return ErrorInvalidArg; auto& state = State(); std::lock_guard lock(state.mutex); const auto found = state.contexts.find(sslCtxId); - if (found == state.contexts.end()) { - *output = {}; - return ErrorNotFound; - } + if (found == state.contexts.end()) return ErrorInvalidId; + if (!output) return ErrorInvalidValue; auto& context = *found->second; PoolScope scope(context.pool); const auto count = CertificateCount(context); diff --git a/core/libs/tests/GuestSsl.cpp b/core/libs/tests/GuestSsl.cpp index 12c079d7eb..4aa00482b6 100644 --- a/core/libs/tests/GuestSsl.cpp +++ b/core/libs/tests/GuestSsl.cpp @@ -1,4 +1,7 @@ #include "prx/libc/include/general/VabiMacros.hpp" +#include "ProcessParametersFixture.hpp" +#include +#include #include #include #include @@ -62,6 +65,13 @@ struct SslCaList { }; int main() { + static std::array parameters{}; + const std::uint64_t processSize = parameters.size(); + const std::uint32_t processMagic = 0x4942524f; + std::memcpy(parameters.data(), &processSize, sizeof(processSize)); + std::memcpy(parameters.data() + 8, &processMagic, sizeof(processMagic)); + Require(ProcessParametersFixture::Install(parameters.data(), parameters.size())); + constexpr int invalidValue = static_cast(0x8095F007); constexpr int notFound = static_cast(0x8095F004); constexpr int invalidArg = static_cast(0x8095177A); int marker = 0; @@ -70,7 +80,7 @@ int main() { SslMemoryPoolStats stats{1, 1, 1, 1}; Require(sceSslGetMemoryPoolStats(context, &stats) == 0); Require(stats.pool_size == 0x10000 && stats.max_inuse_size == 0 && stats.current_inuse_size == 0 && stats.reserved == 0); - Require(sceSslGetCaCerts(context, nullptr) == invalidArg); + Require(sceSslGetCaCerts(context, nullptr) == invalidValue); Require(sceSslFreeCaCerts(context, nullptr) == invalidArg); SslCaCerts certs{&marker, 3, &marker}; @@ -81,7 +91,7 @@ int main() { Require(sceSslFreeCaCerts(context, &certs) == 0); Require(certs.certs == nullptr && certs.num == 0 && certs.pool == nullptr); - Require(sceSslGetCaList(context, nullptr) == invalidArg); + Require(sceSslGetCaList(context, nullptr) == invalidValue); Require(sceSslFreeCaList(context, nullptr) == invalidArg); void* handle = ▮ @@ -103,7 +113,6 @@ int main() { Require(list.certs == nullptr && list.num == 0); constexpr int invalidId = static_cast(0x8095F006); - constexpr int invalidValue = static_cast(0x8095F007); constexpr int badLength = static_cast(0x80951773); constexpr int badDescriptor = static_cast(0x80410109); Require(sceSslInit_nid_postfix(0) == static_cast(0x80020016)); @@ -172,10 +181,19 @@ int main() { Require(sceSslCreateConnection(context, 0, "example.com") == invalidId); Require(sceSslDeleteConnection(connection) == invalidId); - Require(sceSslGetCaList(context, nullptr) == invalidArg); + Require(sceSslGetCaList(context, nullptr) == invalidValue); Require(sceSslFreeCaList(context, nullptr) == invalidArg); list = {&handle, 3}; - Require(Throws([&] { sceSslGetCaList(context, &list); })); + Require(sceSslGetCaList(context, &list) == invalidId); + Require(list.certs == &handle && list.num == 3); + Require(sceSslGetCaCerts(context, nullptr) == invalidId); + certs = {&marker, 3, &marker}; + Require(sceSslGetCaCerts(context, &certs) == invalidId); + Require(certs.certs == &marker && certs.num == 3 && certs.pool == &marker); Require(Throws([&] { sceSslFreeCaList(context, &list); })); - Require(Throws([&] { sceSslUnloadCert(context); })); + for (const std::uint32_t sdkVersion : {0u, 0x074fffffu, 0x07500000u, 0x09000000u}) { + std::memcpy(parameters.data() + 16, &sdkVersion, sizeof(sdkVersion)); + Require(sceSslUnloadCert(context) == (sdkVersion < 0x07500000 ? 0 : invalidId)); + Require(sceSslUnloadCert(0) == (sdkVersion < 0x07500000 ? 0 : invalidId)); + } } diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 877af46e33..736ec71de5 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -9,6 +9,6 @@ - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with flags 1 or 2 returns what the first record delivers. Native flags 3 peek without consuming data; values outside 0 through 3 return `SSL_ERROR_INVALID_VALUE` before connection lookup. A non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown - [Certificate readers](../../../core/libs/prx/libSceSsl/Export.cpp): foreign and expired certificate/name handles throw instead of following native unchecked pointers. Name views track certificate lifetime and preserve identity; the native no-op name release is implemented. View storage remains allocated until context destruction. Numeric names for attributes Mbed TLS cannot identify may differ from native names. Fingerprint stack-space validation and backend digest failures are not reproduced. Signatures, valid-context/null-argument precedence, serial truncation, name copy bounds, validity timestamps and supported hash selectors are confirmed by the supplied module. -- [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. No root bundle is shipped; an empty context still returns `NOT_FOUND`. The existing null-list error, unknown-context behavior and legacy clearing of foreign raw CA lists remain unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate +- [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. Native CA queries include built-in roots; the host still queries loaded authorities and returns `NOT_FOUND` for an empty context. No native root bundle is copied. Null-output and invalid-context precedence, output preservation on invalid contexts and the SDK 7.50 unload error boundary are recovered. Legacy clearing of foreign raw CA lists remains unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate - [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1-255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp): native argument validation, CRLF line endings, 64-character Base64 lines, NUL-inclusive length and buffer clearing on insufficient capacity are recovered. Foreign and expired certificate handles throw instead of performing native unchecked pointer access. DER reserialization differences for noncanonical certificates remain unverified. From 682165a9290b2d344fcaf5ab09a703ea69036633 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:55:54 +0300 Subject: [PATCH 514/523] fix(agc): restore occlusion query size argument --- core/libs/prx/libSceAgc/DcbDraw/src/Stats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgc/DcbDraw/src/Stats.cpp b/core/libs/prx/libSceAgc/DcbDraw/src/Stats.cpp index b3ae56e199..2681834e0b 100644 --- a/core/libs/prx/libSceAgc/DcbDraw/src/Stats.cpp +++ b/core/libs/prx/libSceAgc/DcbDraw/src/Stats.cpp @@ -21,7 +21,7 @@ std::uint32_t* APS5_VABI sceAgcDcbGetLodStats(CommandBuffer* buf, std::uint8_t c return Agc::Command::Emit(buf, 0x10u, {bufferSizeInBytes, static_cast(address), static_cast(address >> 32u), control}, __func__); } -std::uint32_t APS5_VABI sceAgcDcbBeginOcclusionQueryGetSize() { +std::uint32_t APS5_VABI sceAgcDcbBeginOcclusionQueryGetSize(std::int32_t queryType) { NotImplemented_nid_no_patch(__func__); return 0; } From 350c6eb32396e786bddb1e55e4b2a199fd689c12 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:57:02 +0300 Subject: [PATCH 515/523] fix(font): restore typography validation and reject unsupported shaping --- .../prx/libSceFont/include/FontInternal.hpp | 1 - core/libs/prx/libSceFont/src/Style.cpp | 56 ++++++++++++++---- core/libs/tests/GuestFont.cpp | 59 +++++++++++++++++-- docs/dev/debt/libSceFont.md | 2 +- 4 files changed, 99 insertions(+), 19 deletions(-) diff --git a/core/libs/prx/libSceFont/include/FontInternal.hpp b/core/libs/prx/libSceFont/include/FontInternal.hpp index 8c68010f10..4e057a93e1 100644 --- a/core/libs/prx/libSceFont/include/FontInternal.hpp +++ b/core/libs/prx/libSceFont/include/FontInternal.hpp @@ -37,7 +37,6 @@ struct FontState { float scaleW = 16.0f; float scaleH = 16.0f; std::map scriptLanguages; - std::map typographicFeatures; int attribute = 0; FontState() = default; diff --git a/core/libs/prx/libSceFont/src/Style.cpp b/core/libs/prx/libSceFont/src/Style.cpp index 22291654ee..01ae245867 100644 --- a/core/libs/prx/libSceFont/src/Style.cpp +++ b/core/libs/prx/libSceFont/src/Style.cpp @@ -60,20 +60,38 @@ int AccessFontSettings(FontHandle fontHandle, Access&& access) { }); } -int SetFontSetting(FontHandle fontHandle, std::map FontState::*table, int key, int value) { - return AccessFontSettings(fontHandle, [&](FontState& state) { - (state.*table)[key] = value; - return SCE_FONT_OK; - }); +bool HasTypographyRecordFeature(const void* record, int typographic) { + const auto* bytes = static_cast(record); + std::uint32_t count; + std::memcpy(&count, bytes + 32, sizeof(count)); + for (std::uint32_t index = 0; index < count; ++index) { + std::int32_t fontId; + std::memcpy(&fontId, bytes + 36 + sizeof(fontId) * index, sizeof(fontId)); + if (count == 1) { + if (typographic == 65538) return fontId == 43 || fontId == 12; + return static_cast(fontId) <= 29 && (0x30000c00u & (1u << fontId)) != 0; + } + if ((std::to_integer(bytes[100 + index]) & 0x80u) != 0 && fontId >= 0) return index == (typographic == 65538 ? 2u : 1u); + } + return false; } -int GetFontSetting(FontHandle fontHandle, std::map FontState::*table, int key, int* value) { - if (!value) return SCE_FONT_ERROR_INVALID_PARAMETER; - return AccessFontSettings(fontHandle, [&](FontState& state) { - const auto found = (state.*table).find(key); - *value = found == (state.*table).end() ? 0 : found->second; +int CheckTypography(FontHandleNative* font, int typographic, bool setting) { + const auto* record = font->open_info.fontset_record; + switch (typographic) { + case 1: + case 17: return SCE_FONT_OK; - }); + case 65537: + if (setting) return !record || HasTypographyRecordFeature(record, typographic) ? SCE_FONT_ERROR_INVALID_PARAMETER : SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY; + return !record || HasTypographyRecordFeature(record, typographic) ? SCE_FONT_OK : SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY; + case 65538: + return !record || HasTypographyRecordFeature(record, typographic) ? SCE_FONT_OK : SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY; + case 65539: + return record ? SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY : SCE_FONT_OK; + default: + return SCE_FONT_ERROR_INVALID_PARAMETER; + } } bool IsScriptLanguageValid(int script, int language) { @@ -161,11 +179,23 @@ int APS5_VABI sceFontGetScriptLanguage(FontHandle fontHandle, int fontScript, in } int APS5_VABI sceFontSetTypographicDesign(FontHandle fontHandle, int typographic, int feature) { - return SetFontSetting(fontHandle, &FontState::typographicFeatures, typographic, feature); + const int result = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { + const int validation = CheckTypography(font, typographic, true); + if (validation != SCE_FONT_OK) return validation; + return feature == 0 || feature == (typographic == 1 ? 1 : 32) ? SCE_FONT_OK : SCE_FONT_ERROR_INVALID_PARAMETER; + }); + if (result != SCE_FONT_OK) return result; + throw std::logic_error("sceFontSetTypographicDesign: driver typography selection is not implemented"); } int APS5_VABI sceFontGetTypographicDesign(FontHandle fontHandle, int typographic, int* feature) { - return GetFontSetting(fontHandle, &FontState::typographicFeatures, typographic, feature); + const int result = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { + const int validation = CheckTypography(font, typographic, false); + return feature ? validation : SCE_FONT_ERROR_INVALID_PARAMETER; + }); + if (feature) *feature = 0; + if (result != SCE_FONT_OK) return result; + throw std::logic_error("sceFontGetTypographicDesign: driver typography query is not implemented"); } int APS5_VABI sceFontSetScalePixel(FontHandle fontHandle, float w, float h) { diff --git a/core/libs/tests/GuestFont.cpp b/core/libs/tests/GuestFont.cpp index cd0085e158..1ca28f3228 100644 --- a/core/libs/tests/GuestFont.cpp +++ b/core/libs/tests/GuestFont.cpp @@ -1,5 +1,6 @@ #include "prx/libSceFont/include/FontDriver.hpp" #include "tests/FontFixture.hpp" +#include #include #include #include @@ -335,10 +336,60 @@ int main() { } Require(setThrew && getThrew && setting == 0); } - Require(sceFontSetTypographicDesign(font, 1, 2) == SCE_FONT_OK); - Require(sceFontGetTypographicDesign(font, 1, &setting) == SCE_FONT_OK && setting == 2); - Require(sceFontGetTypographicDesign(font, 5, &setting) == SCE_FONT_OK && setting == 0); - Require(sceFontGetTypographicDesign(nullptr, 1, &setting) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + Require(sceFontSetTypographicDesign(font, 1, 2) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontSetTypographicDesign(font, 17, 1) == SCE_FONT_ERROR_INVALID_PARAMETER); + for (const auto selector : {-1, 0, 2, 5, 16, 18, 65536, 65540}) { + Require(sceFontSetTypographicDesign(font, selector, 0) == SCE_FONT_ERROR_INVALID_PARAMETER); + setting = 123; + Require(sceFontGetTypographicDesign(font, selector, &setting) == SCE_FONT_ERROR_INVALID_PARAMETER && setting == 0); + } + Require(sceFontGetTypographicDesign(nullptr, 1, &setting) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && setting == 0); + Require(sceFontGetTypographicDesign(nullptr, 1, nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + Require(sceFontSetTypographicDesign(nullptr, 1, 2) == SCE_FONT_ERROR_INVALID_FONT_HANDLE); + Require(sceFontSetTypographicDesign(font, 65537, 0) == SCE_FONT_ERROR_INVALID_PARAMETER); + for (const auto selector : {1, 17, 65537, 65538, 65539}) { + Require(sceFontGetTypographicDesign(font, selector, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + bool getThrew = false; + try { + sceFontGetTypographicDesign(font, selector, &setting); + } catch (const std::logic_error&) { + getThrew = true; + } + Require(getThrew && setting == 0); + if (selector == 65537) continue; + for (const auto feature : {0, selector == 1 ? 1 : 32}) { + bool setThrew = false; + try { + sceFontSetTypographicDesign(font, selector, feature); + } catch (const std::logic_error&) { + setThrew = true; + } + Require(setThrew); + } + } + std::array typographyRecord{}; + auto* nativeFont = reinterpret_cast(font); + nativeFont->open_info.fontset_record = typographyRecord.data(); + for (const auto selector : {65537, 65538, 65539}) { + Require(sceFontSetTypographicDesign(font, selector, 0) == SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY); + setting = 123; + Require(sceFontGetTypographicDesign(font, selector, &setting) == SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY && setting == 0); + Require(sceFontGetTypographicDesign(font, selector, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER); + } + const std::uint32_t typographyCount = 1; + const std::int32_t typographyFontId = 10; + std::memcpy(typographyRecord.data() + 32, &typographyCount, sizeof(typographyCount)); + std::memcpy(typographyRecord.data() + 36, &typographyFontId, sizeof(typographyFontId)); + Require(sceFontSetTypographicDesign(font, 65537, 0) == SCE_FONT_ERROR_INVALID_PARAMETER); + Require(sceFontGetTypographicDesign(font, 65538, &setting) == SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY); + bool typographyQueryThrew = false; + try { + sceFontGetTypographicDesign(font, 65537, &setting); + } catch (const std::logic_error&) { + typographyQueryThrew = true; + } + Require(typographyQueryThrew && setting == 0); + nativeFont->open_info.fontset_record = nullptr; int attribute = -1; Require(sceFontGetAttribute(font, 0x11, &attribute) == SCE_FONT_OK && attribute == 0); Require(sceFontDefineAttribute(font, 0x11, &attribute) == SCE_FONT_OK && attribute == 0); diff --git a/docs/dev/debt/libSceFont.md b/docs/dev/debt/libSceFont.md index bd2a0098c2..b1b5e56398 100644 --- a/docs/dev/debt/libSceFont.md +++ b/docs/dev/debt/libSceFont.md @@ -2,7 +2,7 @@ ### Unknown function info -- [sceFontSetTypographicDesign and sceFontGetTypographicDesign](../../../core/libs/prx/libSceFont/src/Style.cpp): signatures are confirmed by the supplied PS5 library. Native feature ranges, defaults and driver-dependent availability are not implemented; the host still accepts arbitrary values and stores them without affecting shaping or rendering. +- [Typographic design](../../../core/libs/prx/libSceFont/src/Style.cpp): native selector, feature, font-set eligibility and output-clearing rules are recovered. Driver typography selection and queries, feature-cache updates and glyph substitution remain unimplemented and throw instead of storing arbitrary selector/value pairs. - [sceFontSetScriptLanguage and sceFontGetScriptLanguage](../../../core/libs/prx/libSceFont/src/Style.cpp): native script/language ranges, handle-error precedence and clearing of failed getter outputs are implemented. Valid driver queries and updates throw; the previous arbitrary-value map and fabricated zero default are no longer used. Driver selection and actual language-dependent shaping remain unimplemented. - [sceFontGetRenderScaledKerning](../../../core/libs/prx/libSceFont/src/Render.cpp) (libSceFont) - arguments, the cleared output and the bound renderer check taken from `cellFontGetRenderScaledKerning` in [RPCS3](https://github.com/RPCS3/rpcs3/blob/master/rpcs3/Emu/Cell/Modules/cellFont.cpp) (PS3 predecessor of the library), not confirmed on a title. The kerning is the `sceFontGetKerning` one at the render scale - [sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode](../../../core/libs/prx/libSceFont/src/Open.cpp): signatures, error precedence, failed-output clearing and external-font glyph-code encoding are confirmed by the PS5 library. Multi-face system-font glyph counts, face-selection bits and typographic glyph substitutions are not modeled. From 77835951cd01aa053ed679c5e559d5d9e024eb24 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 13:58:31 +0300 Subject: [PATCH 516/523] fix(ssl): defer private key parsing and allow separate identity loads --- core/libs/prx/libSceSsl/Export.cpp | 58 +++++++++++++++++++----------- core/libs/tests/GuestSslLoad.cpp | 24 +++++++++---- docs/dev/debt/libSceSsl.md | 2 +- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceSsl/Export.cpp b/core/libs/prx/libSceSsl/Export.cpp index 923f584821..bfc982dcdc 100644 --- a/core/libs/prx/libSceSsl/Export.cpp +++ b/core/libs/prx/libSceSsl/Export.cpp @@ -206,9 +206,14 @@ struct Random { } }; +struct EncodedPrivateKey { + Block bytes; + std::size_t size; +}; + struct Identity { std::shared_ptr chain; - PrivateKey key; + std::shared_ptr key; }; struct Snapshot { @@ -309,6 +314,7 @@ struct Session { int verifyResult = 0; unsigned int failedOptions = 0; Random random; + PrivateKey privateKey; mbedtls_x509_crt authorities; mbedtls_ssl_config config; mbedtls_ssl_context ssl; @@ -339,8 +345,18 @@ struct Session { mbedtls_ssl_conf_min_tls_version(&config, MBEDTLS_SSL_VERSION_TLS1_2); mbedtls_ssl_conf_ca_chain(&config, &authorities, nullptr); mbedtls_ssl_conf_rng(&config, mbedtls_ctr_drbg_random, &random.drbg); - if (identity) - CheckBackend(mbedtls_ssl_conf_own_cert(&config, &identity->chain->root, &identity->key.key), "SSL client certificate setup failed"); + if (identity) { + if (!identity->chain || !identity->key) throw std::logic_error("Incomplete SSL client identity is not supported"); + const auto& encoded = *identity->key; + auto bytes = Allocate(Add(encoded.size, 1)); + std::memcpy(bytes.get(), encoded.bytes.get(), encoded.size); + std::size_t first = 0; + while (first < encoded.size && (bytes.get()[first] == ' ' || bytes.get()[first] == '\r' || bytes.get()[first] == '\n' || bytes.get()[first] == '\t')) ++first; + const auto size = first < encoded.size && bytes.get()[first] == '-' ? Add(encoded.size, 1) : encoded.size; + CheckBackend(mbedtls_pk_parse_key(&privateKey.key, bytes.get(), size, nullptr, 0, mbedtls_ctr_drbg_random, &random.drbg), "Invalid SSL private key"); + CheckBackend(mbedtls_pk_check_pair(&identity->chain->root.pk, &privateKey.key, mbedtls_ctr_drbg_random, &random.drbg), "SSL client certificate and private key do not match"); + CheckBackend(mbedtls_ssl_conf_own_cert(&config, &identity->chain->root, &privateKey.key), "SSL client certificate setup failed"); + } for (std::size_t start = 0; start <= alpn.size();) { const auto end = alpn.find(',', start); const auto protocol = alpn.substr(start, end == std::string::npos ? std::string::npos : end - start); @@ -661,15 +677,15 @@ int APS5_VABI sceSslGetMemoryPoolStats(int sslCtxId, SceSsl::MemoryPoolStats* st } int APS5_VABI sceSslLoadCert(int sslCtxId, int caCertNum, SceSsl::Data** caList, SceSsl::Data* cert, SceSsl::Data* privateKey) { - if (caCertNum < 0 || (caCertNum && !caList) || (!!cert != !!privateKey)) - throw std::invalid_argument("Invalid SSL certificate load arguments"); auto& state = State(); std::lock_guard lock(state.mutex); - auto& context = GetContext(sslCtxId); + const auto found = state.contexts.find(sslCtxId); + if (found == state.contexts.end()) return ErrorInvalidId; + auto& context = *found->second; PoolScope scope(context.pool); std::vector> authorities; std::vector> handles; - for (int index = 0; index < caCertNum; ++index) { + for (int index = 0; caList && index < caCertNum; ++index) { if (!caList[index]) throw std::invalid_argument("Missing SSL certificate descriptor"); auto chain = ParseCertificate(*caList[index]); for (auto* entry = &chain->root; entry; entry = entry->next) { @@ -679,23 +695,25 @@ int APS5_VABI sceSslLoadCert(int sslCtxId, int caCertNum, SceSsl::Data** caList, } authorities.push_back(std::move(chain)); } - std::shared_ptr identity; - if (cert) { - identity = std::make_shared(); - identity->chain = ParseCertificate(*cert); - const auto key = CopyInput(*privateKey); - Random random; - random.Seed(); - CheckBackend(mbedtls_pk_parse_key(&identity->key.key, key.bytes.get(), key.size, nullptr, 0, - mbedtls_ctr_drbg_random, &random.drbg), "Invalid SSL private key"); - CheckBackend(mbedtls_pk_check_pair(&identity->chain->root.pk, &identity->key.key, mbedtls_ctr_drbg_random, &random.drbg), - "SSL client certificate and private key do not match"); - } context.authorities.reserve(Add(context.authorities.size(), authorities.size())); context.certificateHandles.reserve(Add(context.certificateHandles.size(), handles.size())); for (auto& chain : authorities) context.authorities.push_back(std::move(chain)); for (auto& handle : handles) context.certificateHandles.push_back(std::move(handle)); - if (identity) context.identity = std::move(identity); + if (cert) { + if (!cert->ptr) return static_cast(0x80951771); + context.identity = std::make_shared(); + context.identity->chain = ParseCertificate(*cert); + } + if (privateKey) { + if (!privateKey->ptr) return static_cast(0x80951771); + if (!context.identity) context.identity = std::make_shared(); + auto identity = std::make_shared(*context.identity); + context.identity = identity; + identity->key.reset(); + auto bytes = Allocate(privateKey->size); + std::memcpy(bytes.get(), privateKey->ptr, privateKey->size); + identity->key = std::make_shared(EncodedPrivateKey{std::move(bytes), privateKey->size}); + } return 0; } diff --git a/core/libs/tests/GuestSslLoad.cpp b/core/libs/tests/GuestSslLoad.cpp index 904b4764a7..b59ad7c6c5 100644 --- a/core/libs/tests/GuestSslLoad.cpp +++ b/core/libs/tests/GuestSslLoad.cpp @@ -141,14 +141,14 @@ void AtomicFailures() { Check(sceSslGetCaCerts(id, &bytes) == 0, "CA vanished after failure"); CheckRoot(bytes); Check(sceSslFreeCaCerts(id, &bytes) == 0, "snapshot free failed"); - Throws([&] { sceSslLoadCert(id, -1, nullptr, nullptr, nullptr); }); - Throws([&] { sceSslLoadCert(id, 1, nullptr, nullptr, nullptr); }); + Check(sceSslLoadCert(id, -1, nullptr, nullptr, nullptr) == 0, "negative CA count was not ignored"); + Check(sceSslLoadCert(id, 1, nullptr, nullptr, nullptr) == 0, "null CA list was not ignored"); SceSsl::Data* absent = nullptr; Throws([&] { sceSslLoadCert(id, 1, &absent, nullptr, nullptr); }); - Throws([&] { sceSslLoadCert(id, 0, nullptr, &der, nullptr); }); + Check(Stats(id).currentInuseSize == before, "argument failure changed retained CA"); Check(sceSslTerm_nid_postfix(id) == 0, "term failed"); - Throws([&] { sceSslLoadCert(id, 1, &pointer, nullptr, nullptr); }); + Check(sceSslLoadCert(id, 1, &pointer, nullptr, nullptr) == InvalidId, "invalid load context error mismatch"); const int tiny = sceSslInit_nid_postfix(32); Throws([&] { sceSslLoadCert(tiny, 1, &pointer, nullptr, nullptr); }); Check(Stats(tiny).currentInuseSize == 0, "failed small-pool load leaked"); @@ -163,8 +163,20 @@ void ClientIdentity() { Check(sceSslLoadCert(id, 0, nullptr, &client, &key) == 0, "client identity load failed"); const auto before = Stats(id).currentInuseSize; Check(before > 0, "identity not retained"); - Throws([&] { sceSslLoadCert(id, 0, nullptr, &client, &wrong); }); - Check(Stats(id).currentInuseSize == before, "mismatched key changed identity"); + Check(sceSslLoadCert(id, 0, nullptr, &client, &wrong) == 0, "key pair was checked during loading"); + Check(sceSslLoadCert(id, 0, nullptr, &client, nullptr) == 0, "certificate-only load failed"); + const auto certificateBytes = Stats(id).currentInuseSize; + Check(certificateBytes > 0 && certificateBytes < before, "certificate replacement retained the old key"); + Check(sceSslLoadCert(id, 0, nullptr, nullptr, &key) == 0, "independent key load failed"); + Check(Stats(id).currentInuseSize == before, "separate identity load retained different data"); + auto arbitraryKey = Data("invalid key encoding"); + Check(sceSslLoadCert(id, 0, nullptr, nullptr, &arbitraryKey) == 0, "key was parsed during loading"); + Check(Stats(id).currentInuseSize == certificateBytes + arbitraryKey.size, "raw key bytes were not retained"); + SceSsl::Data missing{}; + Check(sceSslLoadCert(id, 0, nullptr, &missing, nullptr) == static_cast(0x80951771), "null certificate payload error mismatch"); + Check(sceSslLoadCert(id, 0, nullptr, nullptr, &missing) == static_cast(0x80951771), "null key payload error mismatch"); + Check(Stats(id).currentInuseSize == certificateBytes + arbitraryKey.size, "null payload cleared the identity"); + Check(sceSslLoadCert(-1, -1, nullptr, &missing, &missing) == InvalidId, "load context validation precedence mismatch"); Check(sceSslUnloadCert(id) == 0 && Stats(id).currentInuseSize == 0, "identity release failed"); Check(sceSslTerm_nid_postfix(id) == 0, "identity term failed"); } diff --git a/docs/dev/debt/libSceSsl.md b/docs/dev/debt/libSceSsl.md index 736ec71de5..23190fe3ef 100644 --- a/docs/dev/debt/libSceSsl.md +++ b/docs/dev/debt/libSceSsl.md @@ -9,6 +9,6 @@ - [sceSslCreateConnection, sceSslDeleteConnection, sceSslSetVerifyCallback, sceSslConnect, sceSslRead, sceSslWrite](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - imported by `ffmpeg.prx`. Signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252), which also says the socket stays open after `sceSslDeleteConnection`. The behaviour below was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title); what differs for a title process is unknown. An unknown context returns `SSL_ERROR_INVALID_ID` (0x8095F006) from `sceSslCreateConnection`, and every connection call on an unknown or deleted connection id or on a connection whose context was terminated returns `SSL_ERROR_INVALID_ID`. `sceSslCreateConnection` returns `SSL_ERROR_INVALID_VALUE` (0x8095F007) for a null host and does not check the socket: `sceSslConnect` on a descriptor that is not a socket returns `SCE_NET_ERROR_EBADF` (0x80410109). `sceSslConnect` runs a TLS 1.2 client handshake with Mbed TLS 3.6.7 over the host socket that the guest number maps to in the libkernel or libSceNet socket table, and `sceSslRead` and `sceSslWrite` carry application data over it. TLS 1.3 is compiled out: the console offers TLS 1.2 only (its ClientHello carries 12 suites and no supported_versions extension) and a TLS 1.3-only server gets `SSL_ERROR_FATAL_ALERT` (0x8095F010) from it, as from this build. Connection state is allocated from the context's pool and returned to it by `sceSslDeleteConnection`; a connection keeps the pool alive after `sceSslTerm`, whose connections return `SSL_ERROR_INVALID_ID` from then on. The console's pool use for one connection with a two-certificate chain peaked at 80,544 bytes in a 256 KiB pool (5,568 after `sceSslInit`, 12,000 after loading one CA); a 16 KiB pool fails the connect with `SSL_ERROR_OUT_OF_MEMORY` (0x809517D5) there and here. The server's chain is checked against the CAs loaded with `sceSslLoadCert`, the host name the connection was created with (case-insensitively; an IP literal sends no SNI and matches an IP subject alternative name; an empty host sends an empty SNI and fails `CN_CHECK`) and, when enabled, its validity dates, and `sceSslConnect` returns the first failure the connection's verify options select (the verify option entry below lists the bits, codes and initial options); no root bundle is shipped and the console's built-in roots are unknown, so with no CA loaded a connection with the initial options returns `SSL_ERROR_UNKNOWN_CA` (0x8095F00C), as on the console. A client certificate loaded with `sceSslLoadCert` is sent when the server asks for one. A failed verification is final: close_notify is sent, later `sceSslConnect` calls return the same code (also after the `WANT_POLLIN` rounds of a non-blocking socket), and `sceSslRead` and `sceSslWrite` throw, as the console's codes for them were not measured. With a verify callback set it is called once after the handshake, outside the connection's lock, with the context id, the bits of the enabled checks that failed (`KNOWN_CA_CHECK` 0x20, `CN_CHECK` 0x4, the date checks when enabled, 0 when the chain passed), handles to a copy of the peer chain as received (the leaf first, the root last when the server sent it) that the certificate readers of the connection's context accept until the connection is deleted (the handles stay registered in the context until it is terminated; the console frees them at the delete), the chain length and the user argument. A chain fault that no option names (`SSL_ERROR_INVALID_CERT` without a callback) throws when a callback is set, as the bits the console passes for it were not measured. A non-negative return accepts the connection whatever the bits were; a negative return fails the connect with `SSL_ERROR_APPLICATION_VERIFICATION_CALLBACK_CHECK` (0x8095F0FD) and the callback runs again, with the same chain, from every later `sceSslConnect`, `sceSslRead` and `sceSslWrite` of the connection, which return 0x8095F0FD while it keeps refusing and proceed once it accepts. A `sceSslConnect` from inside the callback, or from another thread while it runs, throws. `sceSslRead` and `sceSslWrite` before `sceSslConnect` run the handshake and verification themselves and return its code when it fails. A non-blocking socket that would block returns `SSL_ERROR_WANT_POLLIN` (0x8095F013) or `SSL_ERROR_WANT_POLLOUT` (0x8095F014) from `sceSslConnect`, `sceSslRead` and `sceSslWrite`; the next `sceSslConnect` resumes the handshake, and `sceSslWrite` returns the part it sent before blocking (the console returned 32 KiB of a 64 KiB write). A blocking libSceNet socket whose `SO_RCVTIMEO` expires returns `SSL_ERROR_WANT_POLLIN` as well, and the call can be repeated; `SSL_ERROR_READ_TIMEOUT` (0x80951710) is never returned. libkernel keeps its timeouts above the host socket, so they do not bound SSL reads and writes on a libkernel socket. `sceSslRead` with flags 0 reads until `len` bytes arrived or the stream ended and with flags 1 or 2 returns what the first record delivers. Native flags 3 peek without consuming data; values outside 0 through 3 return `SSL_ERROR_INVALID_VALUE` before connection lookup. A non-blocking read that got some bytes returns them. It returns 0 after the server's close_notify and after a transport that ends without one, every later read 0 again, and `SSL_ERROR_EOF` (0x8095177C) and `SSL_ERROR_SOCKET_CLOSED` (0x8095170D) never from a read. A reset (`ECONNRESET`, `EPIPE`, `WSAECONNRESET`, `WSAECONNABORTED`, `WSAENETRESET`) returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) from `sceSslRead` and `SCE_NET_ERROR_EPIPE` (0x80410120) from `sceSslWrite`, on every later call too; a record that fails its MAC returns 0x80951D55 from `sceSslRead` after the fatal alert goes out. `sceSslWrite` accepts flags 0 only, `SSL_ERROR_INVALID_VALUE` otherwise, sends all of `len`, capped at `INT_MAX`, and returns the count; it keeps writing after the peer's close_notify or a stream that ended without one, as the console does. A fatal alert during the handshake returns `SSL_ERROR_FATAL_ALERT` and ends the transport: later `sceSslConnect` calls return 0, `sceSslRead` 0 and `sceSslWrite` `SSL_ERROR_SOCKET_CLOSED`, as measured. A transport that ends without close_notify or a reset during the handshake returns `SSL_ERROR_SOCKET_CLOSED`, the session is dropped and the next `sceSslConnect` starts over; those two handshake cases and `SSL_ERROR_INVALID_CERT` (0x8095F00A) for a verification failure no option names were not measured. Other handshake and socket failures, a read or write after a failed verification, a write after `sceSslClose`, a null buffer and a read or write from inside the callback throw, as their console codes are unknown. A connection serializes its reads and writes, so a read blocked in one thread holds back a write on the same connection from another. `sceSslDeleteConnection` sends no close_notify and returns `SSL_ERROR_INVALID_ID` when repeated. `sceSslTerm` leaves the context's connections to `sceSslDeleteConnection`, which then returns `SSL_ERROR_INVALID_ID`; a second `sceSslTerm` returns `SSL_ERROR_INVALID_ID` and `sceSslInit` with a pool size of 0 returns `SCE_KERNEL_ERROR_EINVAL` (0x80020016). `sceSslSetVerifyCallback` accepts any callback, null included, and returns 0. The console's first `sceSslConnect` in a process took 23.8 s before its ClientHello left, a one-time initialisation not emulated. `SSL_ERROR_BEFORE_INIT` (0x8095F001) is never returned, as the library keeps no state apart from its contexts - [sceSslClose](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - takes the connection id, as in [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs#L195-L252). Measured on 13.40 as the connection entry describes: it sends close_notify once if the handshake completed and returns 0, 0 again when repeated and 0 on a connection that never connected, already failed verification or lost its transport to a fatal alert; a close before the handshake changes nothing, so the connection can still connect. After the close `sceSslConnect` returns 0 and `sceSslRead` 0, as on the console; `sceSslWrite` after it throws, as the console's code was not measured. A close_notify that a non-blocking socket cannot take at once returns `SSL_ERROR_WANT_POLLOUT` (0x8095F014) and the close can be repeated; after a reset it returns `SCE_NET_ERROR_ECONNRESET` (0x80410136) every time. The socket is left to the title; an unknown connection returns `SSL_ERROR_INVALID_ID` (0x8095F006). Whether the console closes the socket is unknown - [Certificate readers](../../../core/libs/prx/libSceSsl/Export.cpp): foreign and expired certificate/name handles throw instead of following native unchecked pointers. Name views track certificate lifetime and preserve identity; the native no-op name release is implemented. View storage remains allocated until context destruction. Numeric names for attributes Mbed TLS cannot identify may differ from native names. Fingerprint stack-space validation and backend digest failures are not reproduced. Signatures, valid-context/null-argument precedence, serial truncation, name copy bounds, validity timestamps and supported hash selectors are confirmed by the supplied module. -- [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS 3.6.7 parses DER/PEM chains and checks an optional client certificate/key pair. Imports are retained per context and committed together; a failed import preserves existing credentials. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. Native CA queries include built-in roots; the host still queries loaded authorities and returns `NOT_FOUND` for an empty context. No native root bundle is copied. Null-output and invalid-context precedence, output preservation on invalid contexts and the SDK 7.50 unload error boundary are recovered. Legacy clearing of foreign raw CA lists remains unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate +- [sceSslLoadCert, sceSslGetCaList, sceSslFreeCaList, sceSslGetCaCerts, sceSslFreeCaCerts, sceSslUnloadCert](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and layouts follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs). PPSA15508/01.002.000 calls `sceSslLoadCert` with one 1,408-byte PEM CA and null client certificate/key arguments; this confirms that call shape, not console error behavior. Mbed TLS parses DER/PEM chains. CA imports still commit together instead of preserving successful entries before a later failure. Client certificate replacement clears the previous identity; raw private keys load independently and are parsed when configuring TLS. Partial client chains on decode failure, native key parse errors and incomplete identities during TLS setup remain unsupported. CA queries return independently owned snapshots; unload clears active credentials while outstanding snapshots remain valid. Appending repeated CA imports, snapshot lifetime across unload, memory overhead and error precedence have not been measured on a PS5. Invalid loads, malformed material, exhausted pools and unsupported keys throw rather than inventing console error codes. Native CA queries include built-in roots; the host still queries loaded authorities and returns `NOT_FOUND` for an empty context. No native root bundle is copied. Null-output and invalid-context precedence, output preservation on invalid contexts and the SDK 7.50 unload error boundary are recovered. Legacy clearing of foreign raw CA lists remains unchanged. Certificate reader queries reflect active certificates or outstanding snapshots. Loading credentials does not start a TLS connection; `sceSslConnect` verifies the server against the loaded CAs and offers the loaded client certificate - [sceSslEnableVerifyOption, sceSslDisableVerifyOption, sceSslSetMinSslVersion, sceSslSetAlpn, sceSslGetAlpnSelected](../../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/Net/Http.cs), the source of the other connection signatures; the behaviour was measured on a PS5 on system software 13.40 with a ps5-payload-dev payload (every call printed with its return, a TLS 1.2 test server on the LAN presenting leaves issued by the test fixture root loaded with `sceSslLoadCert`, from an elfldr process rather than a title). The verify options select which verification failures `sceSslConnect` reports (see the connection entry above), with the bit values SharpProspero's `SceSslVerifyOption` gives: `SERVER_VERIFY` 0x1, `CN_CHECK` 0x4, `NOT_AFTER_CHECK` 0x8, `NOT_BEFORE_CHECK` 0x10, `KNOWN_CA_CHECK` 0x20. The initial options are `SERVER_VERIFY`, `CN_CHECK` and `KNOWN_CA_CHECK` (0x25): the console accepted an expired and a not-yet-valid server certificate until 0x8 or 0x10 was enabled, which then returned `SSL_ERROR_NOT_AFTER_CHECK` (0x8095F00D) and `SSL_ERROR_NOT_BEFORE_CHECK` (0x8095F00E); whether 0x2 (`CLIENT_VERIFY`) starts enabled was not determined and it is kept off. A check that is disabled is not reported, whatever the chain. Bits outside 0x3F return `SSL_ERROR_INVALID_VALUE` (0x8095F007) from both calls. Disabling 0x1, 0x4 or 0x20 from the payload stopped its process through the kernel's debug exception path (`mDBG: Sending signal ... signo: 0xa0020101`, 6 of 6 attempts; disabling 0x2, 0x8 and 0x10 returned 0), so whether a title may clear them is unknown; this build clears them, as titles import the call. `sceSslSetMinSslVersion` accepts the `SceSslVersion` values 2 (SSL 3.0) to 5 (TLS 1.2) and returns `SSL_ERROR_INVALID_VALUE` for 0, 1 and anything above 5; the console has no TLS 1.3 value. Every accepted value gives the same handshake, as Mbed TLS 3 speaks TLS 1.2 and nothing older and TLS 1.3 is compiled out, which matches the console offering TLS 1.2 only. `sceSslSetAlpn` stores the string up to its terminator, split on commas into the protocols offered in the handshake (the console sent `h2,http/1.1` as the two names and a wire-format string as one name), a second call replaces the list, an empty string returns `SSL_ERROR_BAD_LENGTH` (0x80951773), and a null list clears the configured protocols. Native inspection confirms that replacement clears the old list even on failure, that each comma-separated name must contain 1-255 bytes and that the whole string must be shorter than 65535 bytes. `sceSslGetAlpnSelected` returns the protocol the server selected, still after `sceSslClose`, or null before the handshake and when none was selected; the pointer is owned by the connection. An unknown connection and a connection whose context was terminated return `SSL_ERROR_INVALID_ID` (0x8095F006) from the four setters and null from `sceSslGetAlpnSelected`, as confirmed by the supplied native module - [sceSslGetPem](../../../core/libs/prx/libSceSsl/Export.cpp): native argument validation, CRLF line endings, 64-character Base64 lines, NUL-inclusive length and buffer clearing on insufficient capacity are recovered. Foreign and expired certificate handles throw instead of performing native unchecked pointer access. DER reserialization differences for noncanonical certificates remain unverified. From 8b75d497c0f5048fc5d786738cf013a5db81bb38 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 14:02:24 +0300 Subject: [PATCH 517/523] fix(kernel): reject unimplemented internal pool statistics --- core/libs/prx/libkernel/DirectMemory/Export.cpp | 7 +++---- core/libs/tests/GuestMemory.cpp | 10 ++++++++-- docs/dev/debt/libkernel.md | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libkernel/DirectMemory/Export.cpp b/core/libs/prx/libkernel/DirectMemory/Export.cpp index 79d07266cb..bd93f135d1 100644 --- a/core/libs/prx/libkernel/DirectMemory/Export.cpp +++ b/core/libs/prx/libkernel/DirectMemory/Export.cpp @@ -465,10 +465,9 @@ int APS5_VABI sceKernelConfiguredFlexibleMemorySize(size_t* size) { return 0; } -int APS5_VABI sceKernelInternalMemoryGetAvailableSize(size_t* size) { - if (!size) return SCE_KERNEL_ERROR_EINVAL; - *size = 0; - return 0; +int APS5_VABI sceKernelInternalMemoryGetAvailableSize(std::size_t* size) { + if (!size) return -1; + throw std::logic_error("sceKernelInternalMemoryGetAvailableSize: internal pool statistics are not implemented"); } int APS5_VABI sceKernelSetVirtualRangeName(const void* addr, uint64_t len, const char* name) { diff --git a/core/libs/tests/GuestMemory.cpp b/core/libs/tests/GuestMemory.cpp index 7bd76d6dad..d03a35de70 100644 --- a/core/libs/tests/GuestMemory.cpp +++ b/core/libs/tests/GuestMemory.cpp @@ -157,8 +157,14 @@ static void CheckPageTableStats() { static void CheckInternalMemoryAvailableSize() { std::size_t available = 1; - Require(sceKernelInternalMemoryGetAvailableSize(&available) == 0 && available == 0); - Require(sceKernelInternalMemoryGetAvailableSize(nullptr) == static_cast(0x80020016u)); + bool threw = false; + try { + sceKernelInternalMemoryGetAvailableSize(&available); + } catch (const std::logic_error&) { + threw = true; + } + Require(threw && available == 1); + Require(sceKernelInternalMemoryGetAvailableSize(nullptr) == -1); } static void CheckNamedAndHintedMappings() { diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 52eea8e8bb..7779dc8dc6 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -7,7 +7,7 @@ - [pthread_mutexattr_setprotocol, pthread_mutexattr_getprotocol, pthread_mutexattr_setprioceiling, pthread_mutexattr_getprioceiling](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) - the protocol and the priority ceiling are stored and reported as in [FreeBSD 11 libthr](https://github.com/freebsd/freebsd-src/blob/releng/11.0/lib/libthr/thread/thr_mutexattr.c#L192-L245) (the ceiling is set or read only under `PTHREAD_PRIO_PROTECT`, and setting a protocol resets it to the highest `SCHED_RR` priority). A null, uninitialized or destroyed attribute returns `EINVAL` before the other arguments are checked; this error behavior is unverified on the console. Mutexes do not apply the protocol or ceiling: a priority-inherit or priority-protect mutex locks like a normal one and does not change its owner's priority - [sceKernelGetMainSocId and sceKernelGetCpumode](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) both return 0, and [sceKernelGetCpuFrequency](../../../core/libs/prx/libkernel/Time/Time.cpp) returns the host TSC frequency measured for `sceKernelGetTscFrequency`, not the console CPU clock. The PS5's SoC id and CPU mode are unknown; shadPS4's PS4 values are [0x710f10/0x740f30 and 0, 2 or 5 from the param.sfo CPU-mode flags](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1a/src/core/libraries/kernel/process.cpp). PPSA99203 only logs them (`[console] main SoC id: result=%#x out=%#x`, `CPU: %llu Hz, cpumode %d`), passing a pointer that `sceKernelGetMainSocId` leaves untouched - [sceKernelEnableDmemAliasing](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) returns 0 and changes nothing: direct memory can always be mapped more than once here, as `DoMapDirect` in [DirectMemory.cpp](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) only checks that the physical pages are allocated, so there is no aliasing to turn on. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/memory.cpp#L39-L43) (PS4) sets a flag there that lifts the `EBUSY` its [`sceKernelMapDirectMemory2`](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/memory.cpp#L299-L301), and [`sceKernelMapDirectMemory` for SDK 2.50 and later](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/libraries/kernel/memory.cpp#L237-L242), return for direct memory that is [already mapped](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1aacbc8db8253be685bf7ab4d9daae/src/core/memory.cpp#L548-L553); that check is not applied here. The no-argument `int` signature follows shadPS4 and ProsperoEden (PPSA99008), which calls it before mapping the same direct memory again. Whether the PS5 refuses such a mapping without the call is unverified -- [sceKernelInternalMemoryGetAvailableSize](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) reports 0 bytes and returns 0: what the console's internal memory pool holds and how large it is are unknown. The `int (size_t*)` signature is from [xUtilty](https://github.com/Backporter/xUtilty/blob/328a856af1ef/source/SystemWrapper.cpp#L85) (PS4). PPSA99203 calls it once beside `sceKernelAvailableFlexibleMemorySize` while starting its GS renderer and never reads the result ([GS.cpp](https://github.com/Swordpdf/PS5SX2/blob/b4cf55e87c34/pcsx2/GS/GS.cpp#L233-L247)) +- [sceKernelInternalMemoryGetAvailableSize](../../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - internal pool allocation and statistics are not implemented, so a non-null query throws without changing its output. Native null output returns -1; an absent pool returns -2, a failed statistics query returns -3, and success writes total pool bytes minus used bytes. - [sceKernelIsThreadSanitizerEnabled](../../../core/libs/prx/libkernel/System/src/Sanitizer.cpp) - the native zero-argument query returns bit 3 of the process sanitizer flags loaded from sysctl `{1, 14, 41}`. The host always reports disabled because guest sanitizer activation is not modeled. ### Unknown function info From 6036bc651219e0a46ac27b5e10596635822b13e6 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 14:04:29 +0300 Subject: [PATCH 518/523] fix(zlib): restore validation errors and finalization behavior --- core/libs/prx/libSceZlib/Export.cpp | 25 ++++++++-------- core/libs/tests/GuestZlib.cpp | 46 ++++++++++++++++++++--------- docs/dev/debt/libSceZlib.md | 2 +- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/core/libs/prx/libSceZlib/Export.cpp b/core/libs/prx/libSceZlib/Export.cpp index 22135e465c..093a5401d2 100644 --- a/core/libs/prx/libSceZlib/Export.cpp +++ b/core/libs/prx/libSceZlib/Export.cpp @@ -13,6 +13,8 @@ static_assert(sizeof(std::uint32_t) == 4 && sizeof(std::uint64_t) == 8 && sizeof constexpr std::int32_t NOT_INITIALIZED = static_cast(0x81120032); constexpr std::int32_t ALREADY_INITIALIZED = static_cast(0x81120033); constexpr std::int32_t NO_SPACE = static_cast(0x8112001C); +constexpr std::int32_t INVALID_ARGUMENT = static_cast(0x81120016); +constexpr std::int32_t NOT_FOUND = static_cast(0x81120002); constexpr std::int32_t FATAL = static_cast(0x811200FF); struct Result { @@ -34,7 +36,8 @@ extern "C" { std::int32_t APS5_VABI sceZlibInitialize(const void* buffer, std::uint64_t length) { std::lock_guard lock(serviceMutex); if (initialized) return ALREADY_INITIALIZED; - if ((buffer == nullptr) != (length == 0)) throw std::runtime_error("sceZlibInitialize: invalid work buffer"); + if ((buffer == nullptr) != (length == 0)) return INVALID_ARGUMENT; + if (buffer) throw std::runtime_error("sceZlibInitialize: work memory registration is not implemented"); initialized = true; return 0; } @@ -42,20 +45,18 @@ std::int32_t APS5_VABI sceZlibInitialize(const void* buffer, std::uint64_t lengt std::int32_t APS5_VABI sceZlibFinalize() { std::lock_guard lock(serviceMutex); if (!initialized) return NOT_INITIALIZED; - if (result && !result->retrieved) throw std::runtime_error("sceZlibFinalize: outstanding result semantics unknown"); result.reset(); initialized = false; return 0; } -std::int32_t APS5_VABI sceZlibInflate(const void* source, std::uint32_t sourceLength, - void* destination, std::uint32_t destinationLength, - std::uint64_t* requestId) { +std::int32_t APS5_VABI sceZlibInflate(const void* source, std::uint32_t sourceLength, void* destination, std::uint32_t destinationLength, std::uint64_t* requestId) { std::lock_guard lock(serviceMutex); if (!initialized) return NOT_INITIALIZED; if (!source || !sourceLength || !destination || !requestId || !destinationLength || - destinationLength > 65536 || reinterpret_cast(destination) % 2048 != 0) - throw std::runtime_error("sceZlibInflate: invalid buffer or size"); + destinationLength > 65536 || destinationLength % 2048 != 0) return INVALID_ARGUMENT; + if (reinterpret_cast(destination) % 2048 != 0) + throw std::runtime_error("sceZlibInflate: unaligned destination driver behavior is not implemented"); if (result && !result->retrieved) throw std::runtime_error("sceZlibInflate: multiple outstanding requests unsupported"); if (nextId == std::numeric_limits::max()) throw std::runtime_error("sceZlibInflate: request IDs exhausted"); z_stream stream{}; @@ -78,18 +79,18 @@ std::int32_t APS5_VABI sceZlibInflate(const void* source, std::uint32_t sourceLe std::int32_t APS5_VABI sceZlibWaitForDone(std::uint64_t* requestId, std::uint32_t* timeout) { std::lock_guard lock(serviceMutex); if (!initialized) return NOT_INITIALIZED; + if (!requestId) return INVALID_ARGUMENT; if (timeout) throw std::runtime_error("sceZlibWaitForDone: timeout semantics unknown"); - if (!requestId || !result || result->retrieved) throw std::runtime_error("sceZlibWaitForDone: no outstanding request"); + if (!result || result->retrieved) throw std::runtime_error("sceZlibWaitForDone: no outstanding request"); *requestId = result->id; return 0; } -std::int32_t APS5_VABI sceZlibGetResult(std::uint64_t requestId, std::uint32_t* destinationLength, - std::int32_t* status) { +std::int32_t APS5_VABI sceZlibGetResult(std::uint64_t requestId, std::uint32_t* destinationLength, std::int32_t* status) { std::lock_guard lock(serviceMutex); if (!initialized) return NOT_INITIALIZED; - if (!destinationLength || !status || !result || result->id != requestId) - throw std::runtime_error("sceZlibGetResult: invalid request or output"); + if (!destinationLength || !status) return INVALID_ARGUMENT; + if (!result || result->id != requestId) return NOT_FOUND; if (result->retrieved) throw std::runtime_error("sceZlibGetResult: repeated retrieval semantics unknown"); *destinationLength = result->produced; *status = result->status; diff --git a/core/libs/tests/GuestZlib.cpp b/core/libs/tests/GuestZlib.cpp index 50a9688420..e893ab95e5 100644 --- a/core/libs/tests/GuestZlib.cpp +++ b/core/libs/tests/GuestZlib.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -14,7 +16,11 @@ std::int32_t APS5_VABI sceZlibWaitForDone(std::uint64_t*, std::uint32_t*); std::int32_t APS5_VABI sceZlibGetResult(std::uint64_t, std::uint32_t*, std::int32_t*); } -static void Require(bool value) { if (!value) std::abort(); } +static void Require(bool value, const std::source_location& location = std::source_location::current()) { + if (value) return; + std::fprintf(stderr, "Zlib assertion failed at line %u\n", location.line()); + std::abort(); +} template static void RequireThrows(TAction action) { @@ -33,12 +39,20 @@ int main() { std::int32_t status = 0; Require(sceZlibFinalize() == static_cast(0x81120032)); Require(sceZlibInflate(nullptr, 0, nullptr, 0, nullptr) == static_cast(0x81120032)); - RequireThrows([&] { sceZlibInitialize(nullptr, 1); }); + Require(sceZlibInitialize(nullptr, 1) == static_cast(0x81120016)); + Require(sceZlibInitialize(output, 0) == static_cast(0x81120016)); Require(sceZlibInitialize(nullptr, 0) == 0); Require(sceZlibInitialize(nullptr, 0) == static_cast(0x81120033)); - RequireThrows([&] { sceZlibInflate(compressed.data(), compressed.size(), output + 1, expectedLength, &id); }); - RequireThrows([&] { sceZlibInflate(compressed.data(), compressed.size(), output, 65537, &id); }); - RequireThrows([&] { sceZlibInflate(nullptr, compressed.size(), output, expectedLength, &id); }); + RequireThrows([&] { sceZlibInflate(compressed.data(), compressed.size(), output + 1, 8192, &id); }); + for (const auto capacity : {0u, 1u, 2047u, 2049u, 65535u, 65537u}) { + Require(sceZlibInflate(compressed.data(), compressed.size(), output, capacity, &id) == static_cast(0x81120016)); + Require(id == 0); + } + Require(sceZlibInflate(nullptr, compressed.size(), output, 8192, &id) == static_cast(0x81120016)); + Require(sceZlibWaitForDone(nullptr, nullptr) == static_cast(0x81120016)); + Require(sceZlibGetResult(0, nullptr, &status) == static_cast(0x81120016)); + Require(sceZlibGetResult(0, &produced, nullptr) == static_cast(0x81120016)); + Require(sceZlibGetResult(0, &produced, &status) == static_cast(0x81120002)); std::uint64_t previous = 0; auto decode = [&](const void* input, std::uint32_t inputLength, std::uint32_t capacity, std::int32_t expectedStatus) { storage.fill(0xA5); @@ -46,34 +60,38 @@ int main() { Require(id > previous); previous = id; RequireThrows([&] { sceZlibInflate(input, inputLength, output, capacity, &id); }); - RequireThrows([&] { sceZlibFinalize(); }); std::uint32_t timeout = 0; RequireThrows([&] { sceZlibWaitForDone(&id, &timeout); }); std::uint64_t completed; Require(sceZlibWaitForDone(&completed, nullptr) == 0); Require(completed == id); - RequireThrows([&] { sceZlibGetResult(id + 1, &produced, &status); }); + Require(sceZlibGetResult(id + 1, &produced, &status) == static_cast(0x81120002)); Require(sceZlibGetResult(id, &produced, &status) == 0); + if (status != expectedStatus) std::fprintf(stderr, "input=%u capacity=%u status=%x expected=%x produced=%u\n", inputLength, capacity, static_cast(status), static_cast(expectedStatus), produced); Require(status == expectedStatus && produced <= capacity); RequireThrows([&] { sceZlibGetResult(id, &produced, &status); }); Require(std::all_of(storage.begin(), storage.begin() + 2048, [](auto byte) { return byte == 0xA5; })); Require(std::all_of(storage.begin() + 2048 + capacity, storage.end(), [](auto byte) { return byte == 0xA5; })); }; - decode(compressed.data(), compressed.size(), expectedLength, 0); + decode(compressed.data(), compressed.size(), 8192, 0); Require(produced == expectedLength); for (std::uint32_t offset = 0; offset < produced; offset += sizeof(text) - 1) Require(std::memcmp(output + offset, text, sizeof(text) - 1) == 0); - decode(compressed.data(), compressed.size(), expectedLength - 1, static_cast(0x8112001C)); + decode(compressed.data(), compressed.size(), 2048, static_cast(0x8112001C)); auto corrupt = compressed; corrupt.back() ^= 1; - decode(corrupt.data(), corrupt.size(), expectedLength, static_cast(0x811200FF)); + decode(corrupt.data(), corrupt.size(), 8192, static_cast(0x811200FF)); decode(compressed.data(), compressed.size() - 1, 65536, static_cast(0x811200FF)); - decode(compressed.data(), compressed.size() - 4, expectedLength, static_cast(0x811200FF)); - decode(compressed.data(), compressed.size() - 1, expectedLength, static_cast(0x811200FF)); + decode(compressed.data(), compressed.size() - 4, 8192, static_cast(0x811200FF)); + decode(compressed.data(), compressed.size() - 1, 8192, static_cast(0x811200FF)); Require(sceZlibFinalize() == 0); Require(sceZlibGetResult(id, &produced, &status) == static_cast(0x81120032)); std::array scratch{}; - Require(sceZlibInitialize(scratch.data(), scratch.size()) == 0); - decode(compressed.data(), compressed.size(), 65536, 0); + RequireThrows([&] { sceZlibInitialize(scratch.data(), scratch.size()); }); + Require(sceZlibInitialize(nullptr, 0) == 0); + Require(sceZlibInflate(compressed.data(), compressed.size(), output, 65536, &id) == 0); + Require(sceZlibFinalize() == 0); + Require(sceZlibInitialize(nullptr, 0) == 0); + Require(sceZlibGetResult(id, &produced, &status) == static_cast(0x81120002)); Require(sceZlibFinalize() == 0); } diff --git a/docs/dev/debt/libSceZlib.md b/docs/dev/debt/libSceZlib.md index 5d289d575b..8cbfd2864e 100644 --- a/docs/dev/debt/libSceZlib.md +++ b/docs/dev/debt/libSceZlib.md @@ -2,4 +2,4 @@ ### Functional -- [libSceZlib](../../../core/libs/prx/libSceZlib/Export.cpp) completes RFC1950 decompression during submission and supports one outstanding request, null-timeout waits and one result retrieval. Timed waits, multiple outstanding requests, repeated retrieval and finalization with an unread result throw. The 64-bit initialization length and 64 KiB output limit with 2 KiB destination alignment follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Compression/Zlib.cs); the PS4 initialization length is 32 bits. shadPS4 also requires the output length to be a multiple of 2 KiB and returns `0x81120016` for invalid arguments; this implementation accepts any length up to 64 KiB (SharpProspero documents only the pointer alignment) and throws on invalid arguments. Scratch memory is not used, and its firmware size/alignment requirements are unknown. The initialization errors follow SharpProspero; malformed/checksum/truncated streams report `0x811200FF` and insufficient output `0x8112001C` as in [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/zlib/zlib.cpp). PS5 error precedence, scheduling, completion selection, result consumption and cancellation are unverified. The payload SDK's FreeBSD kernel `z_stream` and host errno are not this library's ABI. +- [libSceZlib](../../../core/libs/prx/libSceZlib/Export.cpp) decompresses synchronously and supports one outstanding request. Timed waits, multiple outstanding requests and repeated retrieval throw. Work-memory registration and driver-side destination alignment validation remain unsupported; non-null work memory and unaligned destinations throw. Native user-space validation confirms the 64-bit initialization length, 2 KiB output-length granularity up to 64 KiB, null-argument errors and initialization precedence. Finalization discards the host's completed result. Driver scheduling, completion consumption, cancellation, scratch-memory requirements and decompression error details remain unverified; host decoding maps malformed streams to 0x811200FF and exhausted output to 0x8112001C. From 2f678e904639b2d4d13ee999fe34b152962ee025 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 15:04:02 +0300 Subject: [PATCH 519/523] docs: record retained activation and pad no-op behavior --- docs/dev/debt/libScePad.md | 4 ++++ docs/dev/debt/libSceSystemService.md | 2 ++ 2 files changed, 6 insertions(+) diff --git a/docs/dev/debt/libScePad.md b/docs/dev/debt/libScePad.md index 3fdf755b89..d533ccb116 100644 --- a/docs/dev/debt/libScePad.md +++ b/docs/dev/debt/libScePad.md @@ -1,5 +1,9 @@ # libScePad technical debt +### Silent stubs + +- [scePadEnableExtensionPort](../../../core/libs/prx/libScePad/Export.cpp) returns success without changing extension-port state. The boolean argument is recovered; connected-device behavior is not modeled. + ### Unknown function info - [libScePad profile and device-setting exports](../../../core/libs/prx/libScePad/Export.cpp) - profile getters, tuning, saved settings, USB state, shared output and virtual-device setting arguments are recovered. Hunt and Leonardo reset each consume only a handle; physical VR USB status consumes only an output pointer. Remote settings output is 64 bytes and license-controller information is 1224 bytes. Kernel ioctl payload fields remain incomplete. diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index e697b107ae..a5b01ed7ab 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -2,6 +2,8 @@ ### Silent stubs +- [sceShellCoreUtilActivateAbort, sceShellCoreUtilActivateStart, sceShellCoreUtilActivateStartAsync, sceShellCoreUtilActivateTerm](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) return zero with the recovered handle argument. Activation service state is not modeled. + - [libSceSystemService](../../../core/libs/prx/libSceSystemService/Export.cpp) - `sceSystemServicePowerTick`, `sceSystemServiceReportAbnormalTermination`, `sceSystemServiceDisableMusicPlayer`, `sceSystemServiceReenableMusicPlayer`, `sceSystemServiceDisableMediaPlay` and `sceSystemServiceReenableMediaPlay` return success and do nothing: the host has no auto power-off timer to reset, no system crash reporter to notify, no system music player to pause or resume and no system media playback to disable. PPSA01521 calls `sceSystemServiceDisableMediaPlay` during start-up, and its wrappers for both pass no arguments - [sceSystemServiceLaunchWebBrowser](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 launches no web browser, so the call fails and nothing is shown. RommPS PPSA76677 then tells the player to scan its QR code instead - [sceSystemServiceShowControllerSettings](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - returns success without showing the controller settings: AnyPS5 has no system UI to open. Cat Quest III (PPSA15065) imports it From 5888cf3cb295e42d0df6d0c22ba1381bccfae26b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 15:10:04 +0300 Subject: [PATCH 520/523] fix(net): align merged ipv6 and rudp argument types --- core/libs/prx/libSceNetCtl/Export.cpp | 2 +- core/libs/tests/GuestNetCtl.cpp | 10 ++++++---- core/libs/tests/GuestRudpPoll.cpp | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/libs/prx/libSceNetCtl/Export.cpp b/core/libs/prx/libSceNetCtl/Export.cpp index aea249a719..30be5f6e85 100644 --- a/core/libs/prx/libSceNetCtl/Export.cpp +++ b/core/libs/prx/libSceNetCtl/Export.cpp @@ -527,7 +527,7 @@ int APS5_VABI sceNetCtlRegisterCallbackV6(NetCtlCallback func, void* arg, int* c return RegisterCallback(g_callbacksV6, func, arg, cid); } -int APS5_VABI sceNetCtlGetInfoV6(int code, void* info) { +int APS5_VABI sceNetCtlGetInfoV6(std::uint32_t code, NetCtlInfoV6* info) { if (!info) return SCE_NET_CTL_ERROR_INVALID_ADDR; if (code < NET_CTL_INFO_V6_IP_ADDRESS || code > NET_CTL_INFO_V6_SECONDARY_DNS) return SCE_NET_CTL_ERROR_INVALID_CODE; if (!HostHasAddress(AF_INET6)) return SCE_NET_CTL_ERROR_NOT_CONNECTED; diff --git a/core/libs/tests/GuestNetCtl.cpp b/core/libs/tests/GuestNetCtl.cpp index aa2c54300b..1b7ce582b2 100644 --- a/core/libs/tests/GuestNetCtl.cpp +++ b/core/libs/tests/GuestNetCtl.cpp @@ -31,6 +31,8 @@ struct NetCtlDefaultRoute { }; #endif +union NetCtlInfoV6; + extern "C" { int APS5_VABI sceNetCtlInit(void); int APS5_VABI sceNetCtlCheckCallback(void); @@ -38,7 +40,7 @@ int APS5_VABI sceNetCtlGetInfo(int, NetCtlInfo*); int APS5_VABI sceNetCtlGetNatInfo(NetCtlNatInfo*); int APS5_VABI sceNetCtlGetState(int*); int APS5_VABI sceNetCtlGetStateV6(int*); -int APS5_VABI sceNetCtlGetInfoV6(int, void*); +int APS5_VABI sceNetCtlGetInfoV6(std::uint32_t, NetCtlInfoV6*); int APS5_VABI sceNetCtlGetResult(int, int*); int APS5_VABI sceNetCtlGetResultV6(int, int*); #ifdef __APPLE__ @@ -316,10 +318,10 @@ int main() { Require(sceNetCtlGetInfo(14, nullptr) == kErrInvalidAddr); std::uint8_t infoV6[256] = {}; - Require(sceNetCtlGetInfoV6(0, infoV6) == kErrInvalidCode); - Require(sceNetCtlGetInfoV6(6, infoV6) == kErrInvalidCode); + Require(sceNetCtlGetInfoV6(0, reinterpret_cast(infoV6)) == kErrInvalidCode); + Require(sceNetCtlGetInfoV6(6, reinterpret_cast(infoV6)) == kErrInvalidCode); Require(sceNetCtlGetInfoV6(1, nullptr) == kErrInvalidAddr); - if (stateV6 != kStateIpObtained) Require(sceNetCtlGetInfoV6(1, infoV6) == kErrNotConnected); + if (stateV6 != kStateIpObtained) Require(sceNetCtlGetInfoV6(1, reinterpret_cast(infoV6)) == kErrNotConnected); const int result = sceNetCtlGetInfo(14, &info); if (result == kErrNotConnected) { diff --git a/core/libs/tests/GuestRudpPoll.cpp b/core/libs/tests/GuestRudpPoll.cpp index d797fad444..4ff92c482f 100644 --- a/core/libs/tests/GuestRudpPoll.cpp +++ b/core/libs/tests/GuestRudpPoll.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -18,9 +19,9 @@ int APS5_VABI sceRudpInit_nid_postfix(void*, int); int APS5_VABI sceRudpCreateContext(GuestRudpEventHandler, void*, int*); int APS5_VABI sceRudpTerminate(int); int APS5_VABI sceRudpEnd(); -int APS5_VABI sceRudpPollCreate(std::uint32_t); +int APS5_VABI sceRudpPollCreate(std::size_t); int APS5_VABI sceRudpPollControl(int, int, int, std::uint16_t); -int APS5_VABI sceRudpPollWait(int, GuestRudpPollEvent*, std::uint32_t, std::uint64_t); +int APS5_VABI sceRudpPollWait(int, GuestRudpPollEvent*, std::size_t, std::uint64_t); int APS5_VABI sceRudpPollDestroy(int); } From 9301f2ffeb03680626c4b884dd9d7046ec0de873 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 15:17:14 +0300 Subject: [PATCH 521/523] test: align merged cases with recovered library behavior --- core/libs/tests/GuestUltCondition.cpp | 23 +++++++++++++---------- core/libs/tests/GuestUltSync.cpp | 22 +++++++++++++--------- core/libs/tests/GuestUltThread.cpp | 2 +- core/libs/tests/GuestVoiceQoS.cpp | 1 - 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/core/libs/tests/GuestUltCondition.cpp b/core/libs/tests/GuestUltCondition.cpp index 51741e4aa2..5bec09da1b 100644 --- a/core/libs/tests/GuestUltCondition.cpp +++ b/core/libs/tests/GuestUltCondition.cpp @@ -23,6 +23,7 @@ int APS5_VABI sceUltConditionVariableDestroy(void*); constexpr int Null = -2139029503; constexpr int Invalid = -2139029500; +constexpr int Permission = -2139029499; constexpr int State = -2139029498; constexpr int Busy = -2139029497; @@ -41,8 +42,10 @@ struct Fixture { Object mutex; Object condition; - Fixture() { - CHECK(sceUltMutexCreate(&mutex, "mutex", nullptr, nullptr, 0x12000000) == 0); + explicit Fixture(bool recursive = false) { + UltMutexOptParam options{}; + options.attribute = recursive ? 1 : 0; + CHECK(sceUltMutexCreate(&mutex, "mutex", nullptr, &options, 0x12000000) == 0); CHECK(_sceUltConditionVariableCreate(&condition, "condition", &mutex, nullptr, 0x12000000) == 0); } @@ -58,7 +61,7 @@ struct Fixture { }; static void Validation() { - Fixture f; + Fixture f(true); Object unused; CHECK(sceUltMutexDestroy(nullptr) == Null); CHECK(sceUltMutexDestroy(&unused) == State); @@ -73,16 +76,16 @@ static void Validation() { CHECK(sceUltConditionVariableWait(&unused) == State); CHECK(_sceUltConditionVariableCreate(&unused, nullptr, nullptr, nullptr, 0) == Null); CHECK(sceUltConditionVariableWait(&f.condition) == State); - CHECK(sceUltMutexUnlock(&f.mutex) == State); + CHECK(sceUltMutexUnlock(&f.mutex) == Permission); bool unsupported = false; try { _sceUltConditionVariableCreate(&unused, nullptr, &f.mutex, &unused, 0); } catch (const std::runtime_error&) { unsupported = true; } CHECK(unsupported); f.Lock(); - CHECK(sceUltMutexDestroy(&f.mutex) == Busy); + CHECK(sceUltMutexDestroy(&f.mutex) == State); CHECK(sceUltMutexCreate(&f.mutex, nullptr, nullptr, nullptr, 0) == State); auto other = std::async(std::launch::async, [&] { - CHECK(sceUltMutexUnlock(&f.mutex) == State); + CHECK(sceUltMutexUnlock(&f.mutex) == Permission); CHECK(sceUltConditionVariableWait(&f.condition) == State); }); other.get(); @@ -91,9 +94,9 @@ static void Validation() { try { f.Wait(); } catch (const std::runtime_error&) { unsupported = true; } CHECK(unsupported); f.Unlock(); - CHECK(sceUltMutexDestroy(&f.mutex) == Busy); + CHECK(sceUltMutexDestroy(&f.mutex) == State); f.Unlock(); - CHECK(sceUltMutexDestroy(&f.mutex) == Busy); + CHECK(sceUltMutexDestroy(&f.mutex) == State); CHECK(sceUltConditionVariableDestroy(&f.condition) == 0); CHECK(sceUltMutexDestroy(&f.mutex) == 0); CHECK(sceUltMutexLock(&f.mutex) == State); @@ -121,13 +124,13 @@ static void Handoff() { entered = 1; f.Wait(); CHECK(payload == 42); - CHECK(sceUltMutexDestroy(&f.mutex) == Busy); + CHECK(sceUltMutexDestroy(&f.mutex) == State); f.Unlock(); }); Await(entered, 1); f.Lock(); CHECK(sceUltConditionVariableDestroy(&f.condition) == Busy); - CHECK(sceUltMutexDestroy(&f.mutex) == Busy); + CHECK(sceUltMutexDestroy(&f.mutex) == State); CHECK(waiter.wait_for(std::chrono::milliseconds(30)) == std::future_status::timeout); f.Unlock(); f.Lock(); diff --git a/core/libs/tests/GuestUltSync.cpp b/core/libs/tests/GuestUltSync.cpp index 9695ac0263..2a368aa57b 100644 --- a/core/libs/tests/GuestUltSync.cpp +++ b/core/libs/tests/GuestUltSync.cpp @@ -12,7 +12,7 @@ extern "C" { int APS5_VABI sceUltInitialize(); int APS5_VABI sceUltFinalize(); -int APS5_VABI _sceUltMutexCreate(void*, const char*, void*, const void*, std::uint32_t); +int APS5_VABI sceUltMutexCreate(void*, const char*, void*, const UltMutexOptParam*, std::uint32_t); int APS5_VABI sceUltMutexLock(void*); int APS5_VABI sceUltMutexTryLock(void*); int APS5_VABI sceUltMutexUnlock(void*); @@ -22,7 +22,7 @@ int APS5_VABI sceUltQueueOptParamInitialize(void*, std::uint32_t); int APS5_VABI sceUltQueueDataResourcePoolOptParamInitialize(void*, std::uint32_t); int APS5_VABI sceUltWaitingQueueResourcePoolOptParamInitialize(void*, std::uint32_t); int APS5_VABI sceUltUlthreadOptParamInitialize(void*, std::uint32_t); -int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*, std::uint32_t); +int APS5_VABI _sceUltWaitingQueueResourcePoolCreate(void*, const char*, std::uint32_t, std::uint32_t, void*, const void*); int APS5_VABI sceUltWaitingQueueResourcePoolDestroy(void*); int APS5_VABI sceUltReaderWriterLockOptParamInitialize(void*, std::uint32_t); int APS5_VABI sceUltReaderWriterLockCreate(void*, const char*, void*, const void*, std::uint32_t); @@ -49,6 +49,8 @@ namespace { constexpr int Ok = 0; constexpr int Null = -2139029503; constexpr int Invalid = -2139029500; +constexpr int Permission = -2139029499; +constexpr int Again = -2139029496; constexpr int State = -2139029498; constexpr int Busy = -2139029497; @@ -68,7 +70,9 @@ void MutexTryLock() { Object mutex{}; CHECK(sceUltMutexTryLock(nullptr) == Null); CHECK(sceUltMutexTryLock(&mutex) == State); - CHECK(_sceUltMutexCreate(&mutex, "trylock", nullptr, nullptr, 0) == Ok); + UltMutexOptParam options{}; + options.attribute = 1; + CHECK(sceUltMutexCreate(&mutex, "trylock", nullptr, &options, 0) == Ok); CHECK(sceUltMutexTryLock(&mutex) == Ok); CHECK(sceUltMutexTryLock(&mutex) == Ok); std::atomic other{0}; @@ -78,14 +82,14 @@ void MutexTryLock() { std::thread([&] { other = sceUltMutexTryLock(&mutex); }).join(); CHECK(other == Busy); CHECK(sceUltMutexUnlock(&mutex) == Ok); - CHECK(sceUltMutexUnlock(&mutex) == State); + CHECK(sceUltMutexUnlock(&mutex) == Permission); std::thread([&] { other = sceUltMutexTryLock(&mutex); if (other == Ok) other = sceUltMutexUnlock(&mutex); }).join(); CHECK(other == Ok); CHECK(sceUltMutexLock(&mutex) == Ok); - CHECK(sceUltMutexDestroy(&mutex) == Busy); + CHECK(sceUltMutexDestroy(&mutex) == State); CHECK(sceUltMutexUnlock(&mutex) == Ok); CHECK(sceUltMutexDestroy(&mutex) == Ok); CHECK(sceUltMutexTryLock(&mutex) == State); @@ -210,7 +214,7 @@ void ReaderWriterPool() { Object pool{}; Object rwlock{}; std::uint8_t workArea[2048]; - CHECK(_sceUltWaitingQueueResourcePoolCreate(&pool, "pool", 2, 2, workArea, nullptr, 0) == Ok); + CHECK(_sceUltWaitingQueueResourcePoolCreate(&pool, "pool", 2, 2, workArea, nullptr) == Ok); CHECK(sceUltReaderWriterLockCreate(&rwlock, "pooled", &pool, nullptr, 0) == Ok); CHECK(sceUltReaderWriterLockLockWrite(&rwlock) == Ok); std::atomic started{false}; @@ -289,10 +293,10 @@ void Ulthreads() { gSelf = nullptr; CHECK(sceUltUlthreadCreate(&second, "tryjoin", ExitingEntry, 7, context, sizeof(context), &runtime, nullptr, 0) == Ok); const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); - int joined = Busy; - while (joined == Busy && std::chrono::steady_clock::now() < deadline) { + int joined = Again; + while (joined == Again && std::chrono::steady_clock::now() < deadline) { joined = sceUltUlthreadTryJoin(&second, &status); - if (joined == Busy) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (joined == Again) std::this_thread::sleep_for(std::chrono::milliseconds(1)); } CHECK(joined == Ok && status == 7 && gSelf.load() == &second); CHECK(sceUltUlthreadRuntimeDestroy(&runtime) == Ok); diff --git a/core/libs/tests/GuestUltThread.cpp b/core/libs/tests/GuestUltThread.cpp index e1fb8dbbba..32b8be2972 100644 --- a/core/libs/tests/GuestUltThread.cpp +++ b/core/libs/tests/GuestUltThread.cpp @@ -119,7 +119,7 @@ static void Validation() { CHECK(sceUltFinalize() == Ok); CHECK(sceUltUlthreadRuntimeGetWorkAreaSize(1, 1) == 1184); CHECK(sceUltConditionVariableDestroy(nullptr) == -2139029503); - CHECK(sceUltReaderWriterLockDestroy(nullptr) == ULT_ERROR_NULL); + CHECK(sceUltReaderWriterLockDestroy(nullptr) == Null); bool threwInfo = false; try { sceUltGetConditionVariableInfo(nullptr, nullptr, nullptr, 0); diff --git a/core/libs/tests/GuestVoiceQoS.cpp b/core/libs/tests/GuestVoiceQoS.cpp index 8db920a44f..b4d318548e 100644 --- a/core/libs/tests/GuestVoiceQoS.cpp +++ b/core/libs/tests/GuestVoiceQoS.cpp @@ -139,7 +139,6 @@ int main(int argc, char** argv) { Require(sceVoiceQoSDeleteRemoteEndpoint(remote) == notInitialized); Require(sceVoiceQoSCreateLocalEndpoint(&local, 1, 0, 0) == notInitialized); Require(sceVoiceQoSSetRemoteEndpointAttribute(remote, 3, &level, sizeof(level)) == notInitialized); - Require(sceVoiceQoSGetLocalEndpointAttribute(local, 3, &readLevel, sizeof(readLevel)) == notInitialized); Require(sceVoiceQoSReadPacket(connection, packet.data(), &packetSize) == notInitialized); Require(sceVoiceQoSWritePacket(connection, packet.data(), &packetSize) == notInitialized); Require(sceVoiceQoSInit(memory.data(), 0x40000, appType) == 0); From dfe41f1f4596d03df0458162e919e9208adc19bf Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 15:45:18 +0300 Subject: [PATCH 522/523] fix: align merged hmd declarations and webapi tests --- core/libs/prx/libSceHmd/Hmd.hpp | 13 +++++++++---- core/libs/tests/GuestHmd.cpp | 8 ++++---- core/libs/tests/GuestNpWebApi2.cpp | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libSceHmd/Hmd.hpp b/core/libs/prx/libSceHmd/Hmd.hpp index eb2f77625f..31cf038dbb 100644 --- a/core/libs/prx/libSceHmd/Hmd.hpp +++ b/core/libs/prx/libSceHmd/Hmd.hpp @@ -51,6 +51,11 @@ static_assert(offsetof(DeviceInformation, hmuMount) == 24); } +struct HmdFieldOfView; +struct HmdEyeOffset; +struct HmdInertialSensorData; +struct HmdAssemblyError; + extern "C" { std::int32_t APS5_VABI sceHmdInitialize(const Hmd::InitializeParam* param); std::int32_t APS5_VABI sceHmdInitialize315(const Hmd::InitializeParam* param); @@ -69,10 +74,10 @@ int APS5_VABI sceHmdReprojectionQueryGarlicBuffAlign(void); int APS5_VABI sceHmdReprojectionQueryGarlicBuffSize(void); int APS5_VABI sceHmdReprojectionQueryOnionBuffAlign(void); int APS5_VABI sceHmdReprojectionQueryOnionBuffSize(void); -int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, void* fieldOfView); -int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, void* leftOffset, void* rightOffset); -int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, void* data, std::int32_t count); -int APS5_VABI sceHmdGetAssyError(void* data); +int APS5_VABI sceHmdGetFieldOfView(std::int32_t handle, HmdFieldOfView* fieldOfView); +int APS5_VABI sceHmdGet2DEyeOffset(std::int32_t handle, HmdEyeOffset* leftOffset, HmdEyeOffset* rightOffset); +int APS5_VABI sceHmdGetInertialSensorData(std::int32_t handle, HmdInertialSensorData* data, std::uint32_t count); +int APS5_VABI sceHmdGetAssyError(HmdAssemblyError* data); } #endif diff --git a/core/libs/tests/GuestHmd.cpp b/core/libs/tests/GuestHmd.cpp index 203775d1fd..230395afb9 100644 --- a/core/libs/tests/GuestHmd.cpp +++ b/core/libs/tests/GuestHmd.cpp @@ -42,10 +42,10 @@ void CheckHandleQueries(std::int32_t expected) { std::memset(sensor, 0x5a, sizeof(sensor)); for (const auto handle : {0, -1, 1, 0x0f000000}) { Require(sceHmdGetFieldOfView(handle, nullptr) == Hmd::ParameterNull, "null field of view accepted"); - Require(sceHmdGetFieldOfView(handle, fieldOfView) == expected, "field of view without a headset"); - Require(sceHmdGet2DEyeOffset(handle, left, right) == expected, "eye offset without a headset"); + Require(sceHmdGetFieldOfView(handle, reinterpret_cast(fieldOfView)) == expected, "field of view without a headset"); + Require(sceHmdGet2DEyeOffset(handle, reinterpret_cast(left), reinterpret_cast(right)) == expected, "eye offset without a headset"); Require(sceHmdGet2DEyeOffset(handle, nullptr, nullptr) == expected, "eye offset with null outputs"); - Require(sceHmdGetInertialSensorData(handle, sensor, 1) == expected, "sensor data without a headset"); + Require(sceHmdGetInertialSensorData(handle, reinterpret_cast(sensor), 1) == expected, "sensor data without a headset"); } for (const auto* bytes : {left, right, fieldOfView}) { for (std::size_t index = 0; index < 16; ++index) Require(bytes[index] == 0x5a, "a refused query wrote its output"); @@ -57,7 +57,7 @@ void CheckAssyError(std::int32_t expected) { unsigned char data[32]; std::memset(data, 0x5a, sizeof(data)); Require(sceHmdGetAssyError(nullptr) == Hmd::ParameterNull, "null assembly error output accepted"); - Require(sceHmdGetAssyError(data) == expected, "assembly error query without a headset"); + Require(sceHmdGetAssyError(reinterpret_cast(data)) == expected, "assembly error query without a headset"); for (const auto byte : data) Require(byte == 0x5a, "a refused assembly error query wrote its output"); } diff --git a/core/libs/tests/GuestNpWebApi2.cpp b/core/libs/tests/GuestNpWebApi2.cpp index 7bd8327e2b..1adccd989d 100644 --- a/core/libs/tests/GuestNpWebApi2.cpp +++ b/core/libs/tests/GuestNpWebApi2.cpp @@ -92,7 +92,7 @@ int main() { Require(presence > 0 && sceNpWebApi2GetMemoryPoolStats(presence, &stats) == 0 && stats.pool_size == 0x2000); for (const int created : {internal, internal2, presence}) { Require(sceNpWebApi2Terminate(created) == 0); - Require(Throws([&] { sceNpWebApi2GetMemoryPoolStats(created, &stats); })); + Require(sceNpWebApi2GetMemoryPoolStats(created, &stats) == invalidContext); } return 0; } From 68aeef249af0e36e8c0f749a300316f79850a6a2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 21:36:22 +0300 Subject: [PATCH 523/523] fix(tools): scan committed sources for duplicate exports --- tools/check_conventions.py | 29 ++++++++++--------- .../tests/test_check_conventions_comments.py | 1 + 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/check_conventions.py b/tools/check_conventions.py index 132f275710..872ae1dd6c 100644 --- a/tools/check_conventions.py +++ b/tools/check_conventions.py @@ -206,21 +206,22 @@ class Check: def duplicates(self, exports): if not exports: return - result = subprocess.run(["git", "-c", "core.quotePath=false", "grep", "-nF", "--full-name", "-f", "-", self.head, "--", ":(top)core/libs/prx"], input="\n".join(exports) + "\n", capture_output=True, encoding="utf-8", errors="replace") - hits = [line.split(":", 3)[1:] for line in filter(None, result.stdout.split("\n"))] - self.read(path for path, _, _ in hits) - sources = {} - for path, number, text in hits: - if path not in sources: - sources[path] = cpp_text(self.show(path)).split("\n") - source = sources[path] - match = next((match for match in EXPORT.finditer(source[int(number) - 1]) if match.group(1) in exports), None) - if match is None: + paths = sorted(path for path in self.objects if path.startswith("core/libs/prx/") and + "tests" not in PurePosixPath(path).parts) + self.read(paths) + for path in paths: + text = self.show(path) + if "APS5_VABI" not in text: continue - name = match.group(1) - own, own_number = exports[name] - if library(path) != library(own) and "tests" not in PurePosixPath(path).parts and function_body(source, int(number) - 1) is not None: - self.report("duplicate-export", own, own_number, f"{name} is also in {library(path)}") + source = cpp_text(text).split("\n") + for number, line in enumerate(source): + for match in EXPORT.finditer(line): + name = match.group(1) + if name not in exports: + continue + own, own_number = exports[name] + if library(path) != library(own) and function_body(source, number) is not None: + self.report("duplicate-export", own, own_number, f"{name} is also in {library(path)}") def unknown_nids(self, unknowns, symbols): if not unknowns and not symbols: diff --git a/tools/tests/test_check_conventions_comments.py b/tools/tests/test_check_conventions_comments.py index c86fcdd05a..95aa949321 100644 --- a/tools/tests/test_check_conventions_comments.py +++ b/tools/tests/test_check_conventions_comments.py @@ -715,6 +715,7 @@ class RepositoryChecksTests(unittest.TestCase): second.parent.mkdir(parents=True) second.write_text("".join(f"int APS5_VABI sceLargeExportSet{index}(int value) {{ return value; }}\n" for index in range(2000)), encoding="utf-8") self.commit() + first.write_text("", encoding="utf-8") check = Check("HEAD~1", "HEAD") check.code() self.assertEqual(check.findings, [("duplicate-export", "core/libs/prx/libSceB/Export.cpp", 2000, "sceLargeExportSet1999 is also in libSceA")])