From cac398cb96eac617699785fd7580eb34aadfb677 Mon Sep 17 00:00:00 2001 From: Del'aRun Date: Mon, 12 Oct 2026 15:39:27 +0400 Subject: [PATCH] feat(libSceNgs2): implement the Geom reset functions Implement sceNgs2GeomResetListenerParam and sceNgs2GeomResetSourceParam, taken from #1021 and limited to the two reset functions. The Geom structures get their real layouts. sceNgs2GeomCalcListener and sceNgs2GeomApply stay unimplemented. Co-Authored-By: Dextroid17 <217507631+Dextroid17@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- .../libs/prx/libSceNgs2.native/CMakeLists.txt | 2 +- .../prx/libSceNgs2.native/Ngs2Library.cmake | 1 + .../libSceNgs2.native/include/Ngs2Types.hpp | 69 +++++++++++++++++-- core/libs/prx/libSceNgs2.native/src/Geom.cpp | 34 +++++++++ .../libSceNgs2.native/src/Ngs2Internal.hpp | 2 + .../libSceNgs2.native/src/Unimplemented.cpp | 12 ---- core/libs/prx/libSceNgs2.native/src/Voice.cpp | 8 +++ .../prx/libSceNgs2.native/tests/Ngs2Geom.cpp | 57 +++++++++++++++ .../prx/libSceNgs2.native/tests/Ngs2Test.hpp | 2 + docs/dev/TechnicalDebt.md | 1 + 10 files changed, 170 insertions(+), 18 deletions(-) create mode 100644 core/libs/prx/libSceNgs2.native/src/Geom.cpp create mode 100644 core/libs/prx/libSceNgs2.native/tests/Ngs2Geom.cpp diff --git a/core/libs/prx/libSceNgs2.native/CMakeLists.txt b/core/libs/prx/libSceNgs2.native/CMakeLists.txt index 79d43e8ae..ca10bd524 100644 --- a/core/libs/prx/libSceNgs2.native/CMakeLists.txt +++ b/core/libs/prx/libSceNgs2.native/CMakeLists.txt @@ -2,7 +2,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/Ngs2Library.cmake) add_sce_ngs2_library(libSceNgs2.native) if(BUILD_TESTING) - foreach(ngs2Test IN ITEMS Render Atrac9 Filter Custom Parse Stream UserFx Report) + foreach(ngs2Test IN ITEMS Render Atrac9 Filter Custom Parse Stream UserFx Report Geom) string(TOLOWER ${ngs2Test} testName) add_test_executable(ngs2_${testName}_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Ngs2${ngs2Test}.cpp) target_include_directories(ngs2_${testName}_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/3rdparty/LibAtrac9/C/src) diff --git a/core/libs/prx/libSceNgs2.native/Ngs2Library.cmake b/core/libs/prx/libSceNgs2.native/Ngs2Library.cmake index 06a20f0d4..877276b0b 100644 --- a/core/libs/prx/libSceNgs2.native/Ngs2Library.cmake +++ b/core/libs/prx/libSceNgs2.native/Ngs2Library.cmake @@ -3,6 +3,7 @@ function(add_sce_ngs2_library target) add_library(${target} SHARED EXCLUDE_FROM_ALL ${ngs2Dir}/src/Atrac9.cpp ${ngs2Dir}/src/Custom.cpp + ${ngs2Dir}/src/Geom.cpp ${ngs2Dir}/src/Pan.cpp ${ngs2Dir}/src/Rack.cpp ${ngs2Dir}/src/Render.cpp diff --git a/core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp b/core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp index f7931d83f..fd9cfd40f 100644 --- a/core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp +++ b/core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp @@ -607,20 +607,79 @@ struct Ngs2PanWork { }; static_assert(sizeof(Ngs2PanWork) == 40); -struct Ngs2GeomListenerParam { - std::uint32_t reserved[32]; +struct Ngs2GeomVector { + float x; + float y; + float z; }; +static_assert(sizeof(Ngs2GeomVector) == 12); + +struct Ngs2GeomCone { + float inner_level; + float inner_angle; + float outer_level; + float outer_angle; +}; +static_assert(sizeof(Ngs2GeomCone) == 16); + +struct Ngs2GeomRolloff { + std::uint32_t model; + float max_distance; + float rolloff_factor; + float reference_distance; +}; +static_assert(sizeof(Ngs2GeomRolloff) == 16); + +struct Ngs2GeomListenerParam { + Ngs2GeomVector position; + Ngs2GeomVector orient_front; + Ngs2GeomVector orient_up; + Ngs2GeomVector velocity; + float sound_speed; + std::uint32_t reserved[2]; +}; +static_assert(sizeof(Ngs2GeomListenerParam) == 60); struct Ngs2GeomListenerWork { - std::uint32_t reserved[64]; + float matrix[4][4]; + Ngs2GeomVector velocity; + float sound_speed; + std::uint32_t coordinate; + std::uint32_t reserved[3]; }; +static_assert(sizeof(Ngs2GeomListenerWork) == 96); struct Ngs2GeomSourceParam { - std::uint32_t reserved[32]; + Ngs2GeomVector position; + Ngs2GeomVector velocity; + Ngs2GeomVector direction; + Ngs2GeomCone cone; + Ngs2GeomRolloff rolloff; + float doppler_factor; + float fbw_level; + float lfe_level; + float max_level; + float min_level; + float radius; + std::uint32_t num_speakers; + std::uint32_t matrix_format; + std::uint32_t reserved[2]; }; +static_assert(sizeof(Ngs2GeomSourceParam) == 108); + +struct Ngs2GeomA3dAttribute { + Ngs2GeomVector position; + float volume; + std::uint32_t reserved[4]; +}; +static_assert(sizeof(Ngs2GeomA3dAttribute) == 32); struct Ngs2GeomAttribute { - std::uint32_t reserved[32]; + float pitch_ratio; + float level[64]; + Ngs2GeomA3dAttribute a3d_attrib; + std::uint32_t reserved[4]; }; +static_assert(sizeof(Ngs2GeomAttribute) == 308); #endif diff --git a/core/libs/prx/libSceNgs2.native/src/Geom.cpp b/core/libs/prx/libSceNgs2.native/src/Geom.cpp new file mode 100644 index 000000000..c5e3624b3 --- /dev/null +++ b/core/libs/prx/libSceNgs2.native/src/Geom.cpp @@ -0,0 +1,34 @@ +#include + +#include "Ngs2Internal.hpp" + +static constexpr float DEFAULT_SOUND_SPEED = 343.0f; + +int Ngs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) { + if (out_listener_param == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS; + *out_listener_param = {}; + out_listener_param->orient_front.z = 1.0f; + out_listener_param->orient_up.y = 1.0f; + out_listener_param->sound_speed = DEFAULT_SOUND_SPEED; + return SCE_NGS2_OK; +} + +int Ngs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) { + if (out_source_param == nullptr) return SCE_NGS2_ERROR_INVALID_OUT_ADDRESS; + *out_source_param = {}; + out_source_param->direction.z = 1.0f; + out_source_param->cone.inner_level = 1.0f; + out_source_param->cone.inner_angle = 360.0f; + out_source_param->cone.outer_level = 1.0f; + out_source_param->cone.outer_angle = 360.0f; + out_source_param->rolloff.max_distance = 1000000.0f; + out_source_param->rolloff.rolloff_factor = 1.0f; + out_source_param->rolloff.reference_distance = 1.0f; + out_source_param->doppler_factor = 1.0f; + out_source_param->fbw_level = 1.0f; + out_source_param->lfe_level = 1.0f; + out_source_param->max_level = 1.0f; + out_source_param->num_speakers = 2; + out_source_param->matrix_format = 2; + return SCE_NGS2_OK; +} diff --git a/core/libs/prx/libSceNgs2.native/src/Ngs2Internal.hpp b/core/libs/prx/libSceNgs2.native/src/Ngs2Internal.hpp index 9bed17834..65829bf2f 100644 --- a/core/libs/prx/libSceNgs2.native/src/Ngs2Internal.hpp +++ b/core/libs/prx/libSceNgs2.native/src/Ngs2Internal.hpp @@ -203,5 +203,7 @@ void Ngs2RenderSystem(Ngs2System& system, const Ngs2RenderBufferInfo* bufferInfo float Ngs2DefaultLevel(std::uint32_t sourceChannels, std::uint32_t source, std::uint32_t destChannels, std::uint32_t dest); int Ngs2PanInit(Ngs2PanWork* work, const float* speaker_angles, float unit_angle, uint32_t num_speakers); int Ngs2PanGetVolumeMatrix(Ngs2PanWork* work, const Ngs2PanParam* params, uint32_t num_params, uint32_t matrix_format, float* out_volume_matrix); +int Ngs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param); +int Ngs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param); #endif diff --git a/core/libs/prx/libSceNgs2.native/src/Unimplemented.cpp b/core/libs/prx/libSceNgs2.native/src/Unimplemented.cpp index 3287f78d9..9cb4cd441 100644 --- a/core/libs/prx/libSceNgs2.native/src/Unimplemented.cpp +++ b/core/libs/prx/libSceNgs2.native/src/Unimplemented.cpp @@ -25,18 +25,6 @@ int APS5_VABI sceNgs2GeomCalcListener(const Ngs2GeomListenerParam* param, Ngs2Ge return 0; } -int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) { - (void)out_listener_param; - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceNgs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) { - (void)out_source_param; - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceNgs2ReportRegisterHandler(uint32_t report_type, void* handler, uintptr_t user_data, Ngs2Handle* out_handle) { (void)report_type; (void)handler; diff --git a/core/libs/prx/libSceNgs2.native/src/Voice.cpp b/core/libs/prx/libSceNgs2.native/src/Voice.cpp index 008c1cd26..bb45dc5b2 100644 --- a/core/libs/prx/libSceNgs2.native/src/Voice.cpp +++ b/core/libs/prx/libSceNgs2.native/src/Voice.cpp @@ -486,6 +486,14 @@ int APS5_VABI sceNgs2PanGetVolumeMatrix(Ngs2PanWork* work, const Ngs2PanParam* p return Ngs2PanGetVolumeMatrix(work, params, num_params, matrix_format, out_volume_matrix); } +int APS5_VABI sceNgs2GeomResetListenerParam(Ngs2GeomListenerParam* out_listener_param) { + return Ngs2GeomResetListenerParam(out_listener_param); +} + +int APS5_VABI sceNgs2GeomResetSourceParam(Ngs2GeomSourceParam* out_source_param) { + return Ngs2GeomResetSourceParam(out_source_param); +} + } #pragma GCC visibility pop diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2Geom.cpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Geom.cpp new file mode 100644 index 000000000..ca194eea7 --- /dev/null +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Geom.cpp @@ -0,0 +1,57 @@ +#include "Ngs2Test.hpp" + +#include +#include + +static constexpr float SoundSpeed = 343.0f; + +static void TestLayouts() { + Require(sizeof(Ngs2GeomVector) == 12); + Require(sizeof(Ngs2GeomCone) == 16); + Require(sizeof(Ngs2GeomRolloff) == 16); + Require(sizeof(Ngs2GeomListenerParam) == 60); + Require(sizeof(Ngs2GeomListenerWork) == 96); + Require(sizeof(Ngs2GeomSourceParam) == 108); + Require(sizeof(Ngs2GeomA3dAttribute) == 32); + Require(sizeof(Ngs2GeomAttribute) == 308); +} + +static void TestResetListenerParam() { + Ngs2GeomListenerParam param; + std::memset(¶m, 0x5a, sizeof(param)); + Require(sceNgs2GeomResetListenerParam(¶m) == SCE_NGS2_OK); + Require(param.position.x == 0.0f && param.position.y == 0.0f && param.position.z == 0.0f); + Require(param.velocity.x == 0.0f && param.velocity.y == 0.0f && param.velocity.z == 0.0f); + Require(param.orient_front.x == 0.0f && param.orient_front.y == 0.0f && param.orient_front.z == 1.0f); + Require(param.orient_up.x == 0.0f && param.orient_up.y == 1.0f && param.orient_up.z == 0.0f); + Require(param.sound_speed == SoundSpeed); + Require(param.reserved[0] == 0u && param.reserved[1] == 0u); + Require(sceNgs2GeomResetListenerParam(nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS); +} + +static void TestResetSourceParam() { + Ngs2GeomSourceParam param; + std::memset(¶m, 0x5a, sizeof(param)); + Require(sceNgs2GeomResetSourceParam(¶m) == SCE_NGS2_OK); + Require(param.position.x == 0.0f && param.position.y == 0.0f && param.position.z == 0.0f); + Require(param.velocity.x == 0.0f && param.velocity.y == 0.0f && param.velocity.z == 0.0f); + Require(param.direction.x == 0.0f && param.direction.y == 0.0f && param.direction.z == 1.0f); + Require(param.cone.inner_level == 1.0f && param.cone.inner_angle == 360.0f); + Require(param.cone.outer_level == 1.0f && param.cone.outer_angle == 360.0f); + Require(param.rolloff.model == 0u); + Require(param.rolloff.max_distance == 1000000.0f); + Require(param.rolloff.rolloff_factor == 1.0f); + Require(param.rolloff.reference_distance == 1.0f); + Require(param.doppler_factor == 1.0f && param.fbw_level == 1.0f && param.lfe_level == 1.0f); + Require(param.max_level == 1.0f && param.min_level == 0.0f && param.radius == 0.0f); + Require(param.num_speakers == 2u && param.matrix_format == 2u); + Require(param.reserved[0] == 0u && param.reserved[1] == 0u); + Require(sceNgs2GeomResetSourceParam(nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS); +} + +int main() { + TestLayouts(); + TestResetListenerParam(); + TestResetSourceParam(); + return 0; +} diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp index 51a50d512..6312953af 100644 --- a/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Test.hpp @@ -39,6 +39,8 @@ int APS5_VABI sceNgs2VoiceGetState(uintptr_t, Ngs2VoiceState*, size_t); int APS5_VABI sceNgs2VoiceGetStateFlags(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*); +int APS5_VABI sceNgs2GeomResetSourceParam(Ngs2GeomSourceParam*); } inline void Check(bool value, int line) { diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index edfc5cefd..f0fc6ecc1 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -391,6 +391,7 @@ Throughout the project, every function at every stage either **does exactly what - [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 - [sceAppContentGetAddcontDownloadProgress](../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - imported by PPSA08755, which fails to load without it; added without an implementation because its behavior is unverified. [SharpProspero](https://github.com/SvenGDK/SharpProspero) lists `(uint32_t serviceLabel, entitlementLabel, SceAppContentAddcontDownloadProgress*)`, but no PS5 reference gives the progress layout or the error codes +- [sceNgs2GeomResetListenerParam, sceNgs2GeomResetSourceParam](../../core/libs/prx/libSceNgs2.native/src/Geom.cpp) (libSceNgs2.native) - the structures (12, 16, 16, 60, 96, 108, 32 and 308 bytes) come from [KytyPS5](https://github.com/KytyPS5/KytyPS5)'s `ngs2.cpp` and match [shadPS4](https://github.com/shadps4-emu/shadPS4)'s `ngs2_geom.h` field for field, but only KytyPS5 gives the behaviour - shadPS4 logs and returns success without writing the output. The defaults (orient front +Z, orient up +Y, sound speed 343, cone 1/360/1/360, rolloff 0/1000000/1/1, doppler factor, full-band, LFE and maximum level 1, two speakers and matrix format 2) are unverified on a PS5 title; KytyPS5 and iStark/PS5PCEM agree on them. PPSA15552 (Dead Cells) calls `sceNgs2GeomResetSourceParam` after creating its racks. A null output address returns `INVALID_OUT_ADDRESS` as elsewhere in the library rather than throwing as KytyPS5 does. `sceNgs2GeomCalcListener` and `sceNgs2GeomApply` stay unimplemented - [_sceFiberGetThreadFramePointerAddress](../../core/libs/prx/libSceFiber/Export.cpp) (libSceFiber, NID `0dy4JtMUcMQ`) forwards to `sceFiberGetThreadFramePointerAddress`; that the argument list matches is inferred from the export without the underscore, as for the underscore Ult exports, not measured - [sceAgcAcbWaitUntilSafeForRendering](../../core/libs/prx/libSceAgc/Acb/src/Control.cpp) (libSceAgc) - `(buffer, videoOutHandle, displayBufferIndex)`, the declared arguments, which PPSA12544 (01.000.010) passes inside `waitUntilSafeForRendering` and `waitUntilSafeForRendering VR` markers on an asynchronous compute command buffer, ignoring the result. It writes the same rendering-wait packet as `sceAgcDcbWaitUntilSafeForRendering`, which the driver now accepts on compute queues too; the wait blocks only that queue's worker. Whether the console encodes the compute variant differently is not known