mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-02 12:56:05 +00:00
feat(ngs2): implement the firmware-recovered utility exports
Implement sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles and sceNgs2ParseWaveformUser, and recover the sceNgs2FftQuerySize signature and work-size table from the decrypted firmware 11.00 libSceNgs2. FftInit/FftProcess still throw because their AVX kernel and packed layout are not recovered.
This commit is contained in:
1 parent
a026562629
commit
2efc86f681
7 files changed
+131
-20
No files matched your search
@@ -19,6 +19,10 @@
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "Ngs2Internal.hpp"
|
||||
|
||||
using Ngs2ParseReadHandler = std::int32_t (APS5_VABI *)(std::uintptr_t user_data, std::uint32_t offset, void* data, std::size_t size);
|
||||
|
||||
extern "C" int APS5_VABI sceNgs2ParseWaveformData(const void* data, std::size_t data_size, Ngs2WaveformInfo* info);
|
||||
|
||||
static std::vector<Ngs2System*>& Systems() {
|
||||
static auto* const systems = new std::vector<Ngs2System*>();
|
||||
return *systems;
|
||||
@@ -613,6 +617,62 @@ int APS5_VABI sceNgs2ReportUnregisterHandler(Ngs2Handle report_handle) {
|
||||
return SCE_NGS2_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemEnumHandles(uintptr_t* out_handles, uint32_t max_handles) {
|
||||
constexpr int invalid_argument = static_cast<int>(0x804A0053);
|
||||
if (max_handles != 0 && out_handles == nullptr) return invalid_argument;
|
||||
const Ngs2Lock lock;
|
||||
uint32_t count = 0;
|
||||
for (const auto* system : Systems()) {
|
||||
if (out_handles != nullptr && count < max_handles) out_handles[count] = reinterpret_cast<uintptr_t>(system);
|
||||
++count;
|
||||
}
|
||||
return static_cast<int>(count);
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2SystemEnumRackHandles(uintptr_t system_handle, uintptr_t* out_handles, uint32_t max_handles) {
|
||||
constexpr int invalid_argument = static_cast<int>(0x804A0053);
|
||||
constexpr int invalid_system_handle = static_cast<int>(0x804A0230);
|
||||
if (max_handles != 0 && out_handles == nullptr) return invalid_argument;
|
||||
const Ngs2Lock lock;
|
||||
const auto* system = Ngs2FindSystem(system_handle);
|
||||
if (system == nullptr) return invalid_system_handle;
|
||||
uint32_t count = 0;
|
||||
for (const auto* rack : system->racks) {
|
||||
if (out_handles != nullptr && count < max_handles) out_handles[count] = reinterpret_cast<uintptr_t>(rack);
|
||||
++count;
|
||||
}
|
||||
return static_cast<int>(count);
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2FftQuerySize(std::uint32_t length, std::uint32_t flags, std::uint64_t* size) {
|
||||
static constexpr std::uint32_t samples[] = {64, 128, 256, 512, 1024};
|
||||
if (size == nullptr || length < 8 || length > 12) return -1;
|
||||
std::uint32_t count = samples[length - 8];
|
||||
if ((flags & 2) != 0) count /= 2;
|
||||
*size = static_cast<std::uint64_t>(count) * 2 * 16 + 64;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceNgs2ParseWaveformUser(std::uintptr_t handler, std::uintptr_t user_data, Ngs2WaveformInfo* out_info) {
|
||||
constexpr int invalid_out_address = static_cast<int>(0x804A0053);
|
||||
constexpr int invalid_handler = static_cast<int>(0x804A0408);
|
||||
constexpr std::uint64_t max_waveform_bytes = 256ull * 1024 * 1024;
|
||||
if (out_info == nullptr) return invalid_out_address;
|
||||
if (handler == 0) return invalid_handler;
|
||||
const auto read = reinterpret_cast<Ngs2ParseReadHandler>(handler);
|
||||
std::uint8_t header[12] = {};
|
||||
if (read(user_data, 0, header, sizeof(header)) < 0) return invalid_handler;
|
||||
if (std::memcmp(header, "RIFF", 4) != 0 || std::memcmp(header + 8, "WAVE", 4) != 0)
|
||||
return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
|
||||
const std::uint32_t riff_size = static_cast<std::uint32_t>(header[4]) | static_cast<std::uint32_t>(header[5]) << 8 |
|
||||
static_cast<std::uint32_t>(header[6]) << 16 | static_cast<std::uint32_t>(header[7]) << 24;
|
||||
const std::uint64_t riff_end = 8ull + riff_size;
|
||||
if (riff_end < 12 || riff_end > max_waveform_bytes) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
|
||||
std::vector<std::uint8_t> bytes(static_cast<std::size_t>(riff_end));
|
||||
if (read(user_data, 0, bytes.data(), bytes.size()) < 0) return invalid_handler;
|
||||
return sceNgs2ParseWaveformData(bytes.data(), bytes.size(), out_info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC visibility pop
|
||||
@@ -401,6 +401,38 @@ static void CheckSetSampleRate() {
|
||||
Status(sceNgs2SystemDestroy(system, nullptr), SCE_NGS2_OK, "system destroy failed");
|
||||
}
|
||||
|
||||
static void CheckFftQuerySize() {
|
||||
std::uint64_t size = 0;
|
||||
Require(sceNgs2FftQuerySize(8, 0, &size) == 0 && size == 2112);
|
||||
Require(sceNgs2FftQuerySize(8, 2, &size) == 0 && size == 1088);
|
||||
Require(sceNgs2FftQuerySize(9, 0, &size) == 0 && size == 4160);
|
||||
Require(sceNgs2FftQuerySize(12, 2, &size) == 0 && size == 16448);
|
||||
Require(sceNgs2FftQuerySize(7, 0, &size) == -1);
|
||||
Require(sceNgs2FftQuerySize(13, 0, &size) == -1);
|
||||
Require(sceNgs2FftQuerySize(8, 0, nullptr) == -1);
|
||||
}
|
||||
|
||||
static void CheckEnumeration() {
|
||||
constexpr int invalidArgument = static_cast<int>(0x804A0053u);
|
||||
constexpr int invalidSystem = static_cast<int>(0x804A0230u);
|
||||
uintptr_t handles[8] = {};
|
||||
Require(sceNgs2SystemEnumHandles(nullptr, 0) >= 0);
|
||||
Require(sceNgs2SystemEnumHandles(nullptr, 1) == invalidArgument);
|
||||
const auto system = CreateSystem();
|
||||
const auto rack = CreateRack(system, SCE_NGS2_RACK_ID_REVERB);
|
||||
const int systems = sceNgs2SystemEnumHandles(handles, 8);
|
||||
Require(systems >= 1);
|
||||
bool found = false;
|
||||
for (int i = 0; i < systems && i < 8; ++i) found = found || handles[i] == system;
|
||||
Require(found);
|
||||
Require(sceNgs2SystemEnumHandles(nullptr, 0) == systems);
|
||||
Require(sceNgs2SystemEnumRackHandles(system, nullptr, 1) == invalidArgument);
|
||||
Require(sceNgs2SystemEnumRackHandles(0x1234, handles, 8) == invalidSystem);
|
||||
const int racks = sceNgs2SystemEnumRackHandles(system, handles, 8);
|
||||
Require(racks == 1 && handles[0] == rack);
|
||||
Require(sceNgs2SystemDestroy(system, nullptr) == SCE_NGS2_OK);
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
constexpr int systemError = static_cast<int>(0x804A8201u);
|
||||
@@ -441,6 +473,8 @@ int main() {
|
||||
CheckResetScheduler();
|
||||
CheckSetGrainSamples();
|
||||
CheckSetSampleRate();
|
||||
CheckFftQuerySize();
|
||||
CheckEnumeration();
|
||||
CheckExportLinked();
|
||||
std::cout << "NGS2 PS5 handle errors and validation precedence checked\n";
|
||||
return 0;
|
||||
|
||||
@@ -241,8 +241,36 @@ static void TestFrameInfoErrors() {
|
||||
}
|
||||
}
|
||||
|
||||
static std::int32_t APS5_VABI ReadUserWaveform(std::uintptr_t user_data, std::uint32_t offset, void* data, std::size_t size) {
|
||||
const auto& file = *reinterpret_cast<const std::vector<std::uint8_t>*>(user_data);
|
||||
if (offset > file.size() || size > file.size() - offset) return -1;
|
||||
std::memcpy(data, file.data() + offset, size);
|
||||
return static_cast<std::int32_t>(size);
|
||||
}
|
||||
|
||||
static void TestParseUser() {
|
||||
const auto file = PcmFile(2, 44100, 16, 100);
|
||||
Ngs2WaveformInfo expected{};
|
||||
Ngs2WaveformInfo actual{};
|
||||
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &expected) == SCE_NGS2_OK);
|
||||
Require(sceNgs2ParseWaveformUser(reinterpret_cast<std::uintptr_t>(&ReadUserWaveform), reinterpret_cast<std::uintptr_t>(&file), &actual) == SCE_NGS2_OK);
|
||||
Require(std::memcmp(&expected, &actual, sizeof(expected)) == 0);
|
||||
Require(sceNgs2ParseWaveformUser(0, 0, &actual) == static_cast<int>(0x804A0408u));
|
||||
Require(sceNgs2ParseWaveformUser(reinterpret_cast<std::uintptr_t>(&ReadUserWaveform), reinterpret_cast<std::uintptr_t>(&file), nullptr) ==
|
||||
static_cast<int>(0x804A0053u));
|
||||
auto bad = file;
|
||||
bad[0] = 'X';
|
||||
Require(sceNgs2ParseWaveformUser(reinterpret_cast<std::uintptr_t>(&ReadUserWaveform), reinterpret_cast<std::uintptr_t>(&bad), &actual) ==
|
||||
SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
|
||||
auto truncated = file;
|
||||
truncated.resize(16);
|
||||
Require(sceNgs2ParseWaveformUser(reinterpret_cast<std::uintptr_t>(&ReadUserWaveform), reinterpret_cast<std::uintptr_t>(&truncated), &actual) ==
|
||||
static_cast<int>(0x804A0408u));
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestParsePcm();
|
||||
TestParseUser();
|
||||
TestParseUnknownFormats();
|
||||
TestParseFile();
|
||||
TestParseFileBank();
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
extern "C" {
|
||||
int APS5_VABI sceNgs2ParseWaveformData(const void*, size_t, Ngs2WaveformInfo*);
|
||||
int APS5_VABI sceNgs2ParseWaveformFile(const char*, uint32_t, Ngs2WaveformInfo*);
|
||||
int APS5_VABI sceNgs2ParseWaveformUser(std::uintptr_t, std::uintptr_t, Ngs2WaveformInfo*);
|
||||
int APS5_VABI sceNgs2FftQuerySize(std::uint32_t, std::uint32_t, std::uint64_t*);
|
||||
int APS5_VABI sceNgs2SystemEnumHandles(std::uintptr_t*, std::uint32_t);
|
||||
int APS5_VABI sceNgs2SystemEnumRackHandles(std::uintptr_t, std::uintptr_t*, std::uint32_t);
|
||||
int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat*, uint32_t, uint32_t, Ngs2WaveformBlock*);
|
||||
int APS5_VABI sceNgs2GetWaveformFrameInfo(const Ngs2WaveformFormat*, uint32_t*, uint32_t*, uint32_t*, uint32_t*);
|
||||
int APS5_VABI sceNgs2SystemResetOption(Ngs2SystemOption*);
|
||||
|
||||
@@ -14,21 +14,5 @@ int APS5_VABI sceNgs2FftProcess() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI sceNgs2FftQuerySize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI sceNgs2ParseWaveformUser(std::uintptr_t handler, std::uintptr_t userData, void* outInfo) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI sceNgs2SystemEnumHandles(std::uintptr_t* outHandles, std::uint32_t maxHandles) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI sceNgs2SystemEnumRackHandles(std::uintptr_t systemHandle, std::uintptr_t* outHandles, std::uint32_t maxHandles) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceNgs2.native/include/Ngs2Types.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceNgs2FftInit();
|
||||
int APS5_VABI sceNgs2FftProcess();
|
||||
int APS5_VABI sceNgs2FftQuerySize();
|
||||
int APS5_VABI sceNgs2ParseWaveformUser(std::uintptr_t handler, std::uintptr_t userData, void* outInfo);
|
||||
int APS5_VABI sceNgs2FftQuerySize(std::uint32_t length, std::uint32_t flags, std::uint64_t* size);
|
||||
int APS5_VABI sceNgs2ParseWaveformUser(std::uintptr_t handler, std::uintptr_t userData, Ngs2WaveformInfo* outInfo);
|
||||
int APS5_VABI sceNgs2SystemEnumHandles(std::uintptr_t* outHandles, std::uint32_t maxHandles);
|
||||
int APS5_VABI sceNgs2SystemEnumRackHandles(std::uintptr_t systemHandle, std::uintptr_t* outHandles, std::uint32_t maxHandles);
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
### 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.
|
||||
- [sceNgs2ParseWaveformUser, sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles](../../../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.
|
||||
- [sceNgs2FftQuerySize](../../../core/libs/prx/libSceNgs2.native/src/System.cpp) (libSceNgs2) - implemented from the firmware 11.00 module: lengths 8..12 select 64, 128, 256, 512 and 1024 samples, bit 1 of the flags halves the count, and the work size is `32 * count + 64`. [sceNgs2FftInit and sceNgs2FftProcess](../../../core/libs/prx/libSceNgs2/PendingExports.cpp) still throw: the 7.3 KB AVX radix-8 kernel and its packed input/output layout are not recovered, and no dump imports these NIDs.
|
||||
- [sceNgs2ParseWaveformUser, sceNgs2SystemEnumHandles, sceNgs2SystemEnumRackHandles](../../../core/libs/prx/libSceNgs2.native/src/System.cpp) (libSceNgs2) - implemented from the firmware 11.00 module. `sceNgs2ParseWaveformUser` reads offset 0 with the caller's read handler, takes the RIFF size from the header and parses the bytes with the shared waveform parser; a null handler returns `0x804A0408` and a null output `0x804A0053`. The enumerators return the count of live systems or racks and fill up to the requested count; a null output with a nonzero count returns `0x804A0053` and an unknown system `0x804A0230`. No dump imports these NIDs.
|
||||
Reference in new issue
Block a user