diff --git a/core/libs/prx/libSceOpusCeltDec/CMakeLists.txt b/core/libs/prx/libSceOpusCeltDec/CMakeLists.txt new file mode 100644 index 000000000..25870e93b --- /dev/null +++ b/core/libs/prx/libSceOpusCeltDec/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(libSceOpusCeltDec SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp) +target_include_directories(libSceOpusCeltDec PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(libSceOpusCeltDec PRIVATE libc) +configure_windows_unwind(libSceOpusCeltDec) + +if(BUILD_TESTING) + add_test_executable(guest_opus_celt_dec_tests ${LIBS_INCLUDE_DIR}/tests/GuestOpusCeltDec.cpp) + target_include_directories(guest_opus_celt_dec_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_opus_celt_dec_tests PRIVATE libSceOpusCeltDec libc) + configure_windows_unwind(guest_opus_celt_dec_tests) + add_test(NAME guest_opus_celt_dec COMMAND guest_opus_celt_dec_tests) + if(WIN32) + set_tests_properties(guest_opus_celt_dec PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceOpusCeltDec/Export.cpp b/core/libs/prx/libSceOpusCeltDec/Export.cpp new file mode 100644 index 000000000..b70e7fe12 --- /dev/null +++ b/core/libs/prx/libSceOpusCeltDec/Export.cpp @@ -0,0 +1,36 @@ +#include "prx/libc/include/General.hpp" +#include + +extern "C" { + +int APS5_VABI sceOpusCeltDecInitialize(std::uint32_t*) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceOpusCeltDecTerminate(std::uint32_t*) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceOpusCeltDecGetSize(int) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceOpusCeltDecCreateEx(std::uint32_t*, void*, int, int) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceOpusCeltDecDecode(void*, const std::uint8_t*, int, std::int16_t*, int) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceOpusCeltDecDestroy(void*) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +} diff --git a/core/libs/tests/GuestOpusCeltDec.cpp b/core/libs/tests/GuestOpusCeltDec.cpp new file mode 100644 index 000000000..c95c1542c --- /dev/null +++ b/core/libs/tests/GuestOpusCeltDec.cpp @@ -0,0 +1,44 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include + +extern "C" { +int APS5_VABI sceOpusCeltDecInitialize(std::uint32_t*); +int APS5_VABI sceOpusCeltDecTerminate(std::uint32_t*); +int APS5_VABI sceOpusCeltDecGetSize(int); +int APS5_VABI sceOpusCeltDecCreateEx(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*); +} + +static int failures = 0; + +static void RequireNotImplemented(const char* name, const std::function& call) { + try { + call(); + } catch (const std::runtime_error& error) { + if (std::string(error.what()) == std::string(name) + " not implemented") return; + std::fprintf(stderr, "%s threw: %s\n", name, error.what()); + ++failures; + return; + } + std::fprintf(stderr, "%s returned instead of reporting that it is not implemented\n", name); + ++failures; +} + +int main() { + std::uint32_t context = 0; + std::uint8_t state[64] = {}; + std::int16_t pcm[16] = {}; + const std::uint8_t packet[] = {0xFC, 0x00}; + RequireNotImplemented("sceOpusCeltDecInitialize", [&] { sceOpusCeltDecInitialize(&context); }); + RequireNotImplemented("sceOpusCeltDecTerminate", [&] { sceOpusCeltDecTerminate(&context); }); + RequireNotImplemented("sceOpusCeltDecGetSize", [] { sceOpusCeltDecGetSize(2); }); + RequireNotImplemented("sceOpusCeltDecCreateEx", [&] { sceOpusCeltDecCreateEx(&context, state, 48000, 2); }); + RequireNotImplemented("sceOpusCeltDecDecode", [&] { sceOpusCeltDecDecode(state, packet, sizeof(packet), pcm, sizeof(pcm)); }); + RequireNotImplemented("sceOpusCeltDecDestroy", [&] { sceOpusCeltDecDestroy(state); }); + return failures == 0 ? 0 : 1; +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index fab6c9813..d515db127 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -107,6 +107,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceCoredumpWriteUserString](../../core/libs/prx/libkernel/System/src/Coredump.cpp) and [sceSaveDataConvert, sceSaveDataGetConvertProgress](../../core/libs/prx/libSceSaveData.native/Export.cpp) - imported by PPSA16064, not called during its start-up and added without an implementation; unknown signatures - [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 - [sceAudioOutExClose, sceAudioOutExConfigureOutput, sceAudioOutExGetMonitorInfo, sceAudioOutExOpen](../../core/libs/prx/libSceAudioOut/src/AudioOut.cpp) (libSceAudioOut) - imported by PPSA99505, which fails to load without them; added without an implementation and declared without parameters. PPSA99505 calls them only for its HDMI bitstream output, an option that is off by default, to read the formats the TV or receiver lists, switch the HDMI output mode and open a port for IEC 61937 bursts ([Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/jelly5_bitstream.cpp#L110-L179)); [EVO-PLAYER-PS5](https://github.com/sainsaji/EVO-PLAYER-PS5/blob/da0c0451333bab82e79fffd2de4718e917f9dcb0/projects/evoplayer/core/src/audio/evo_pt.cpp) also calls `sceAudioOutExOpen`, `sceAudioOutExConfigureOutput` and `sceAudioOutExClose` for HDMI bitstream output. The two declare the arguments of `sceAudioOutExConfigureOutput` differently and neither is confirmed on a console. Audio reaches the host device through SDL as PCM, so there is no encoded HDMI output to switch to +- [sceOpusCeltDecCreateEx, sceOpusCeltDecDecode, sceOpusCeltDecDestroy, sceOpusCeltDecGetSize, sceOpusCeltDecInitialize, sceOpusCeltDecTerminate](../../core/libs/prx/libSceOpusCeltDec/Export.cpp) (libSceOpusCeltDec) - imported by PPSA99001, which fails to load without the module; added without an implementation. Their signatures match libSceOpusDec's in [ProsperoRadio](https://github.com/blackbearreloaded/ProsperoRadio/blob/2c4029c3a8f43e9e1dfef98374d94d99b14d745d/src/opus_decoder.cpp). PPSA99001 opens this decoder only as a fallback when libSceOpusDec reports a retryable error for a CELT packet, which [libSceOpusDec](../../core/libs/prx/libSceOpusDec/Export.cpp) here never returns, so the fallback is not reached - [sce::Json::Parser::parse](../../core/libs/prx/libSceJson2/Export.cpp) (libSceJson2) - a number that overflows a double (e.g. `1e309`) throws: whether the console rejects the document or stores an infinite real is unknown - [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` - [sceUserServiceGetUserColor](../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - the default profile colour is assumed to be blue (0)