Merge pull request #3995 from delarun/fix/ngs2-dead-cells

feat(libSceNgs2): implement the Geom reset functions
This commit is contained in:
Dean 2026-10-13 03:08:54 +13:00 committed by GitHub
commit 8fa5cc4866
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 170 additions and 18 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,34 @@
#include <cstdint>
#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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,57 @@
#include "Ngs2Test.hpp"
#include <cstdint>
#include <cstring>
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(&param, 0x5a, sizeof(param));
Require(sceNgs2GeomResetListenerParam(&param) == 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(&param, 0x5a, sizeof(param));
Require(sceNgs2GeomResetSourceParam(&param) == 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;
}

View File

@ -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) {

View File

@ -393,6 +393,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