From 02166268ea3623736afc99f67591316d1b23ffd2 Mon Sep 17 00:00:00 2001 From: Gjon Gojcevic Date: Wed, 7 Oct 2026 09:49:31 -0400 Subject: [PATCH] test(libSceNgs2.native): run the waveform parse tests in their own executable On Linux ngs2_atrac9 aborted without any output once TestParseFile was part of its main(). The exception that TestCorruptSuperframeAtPageEnd throws in the Ngs2 module and catches in main() has to resume through a cleanup landing pad inside main(). That resume goes through the unwinder statically linked into the test executable, which then fails with _URC_FATAL_PHASE2_ERROR and aborts silently. Whether main() gets such a pad depends on what the compiler inlines into it, so adding any function with non-trivial cleanups to that main() is enough to trigger it. Move the PCM and file parse tests into a new ngs2_parse executable and restore Ngs2Atrac9.cpp to its upstream content, so the test that catches exceptions is unchanged. The new executable has no catch and never relies on that path. --- .../libs/prx/libSceNgs2.native/CMakeLists.txt | 2 +- .../libSceNgs2.native/tests/Ngs2Atrac9.cpp | 70 -------------- .../prx/libSceNgs2.native/tests/Ngs2Parse.cpp | 94 +++++++++++++++++++ 3 files changed, 95 insertions(+), 71 deletions(-) create mode 100644 core/libs/prx/libSceNgs2.native/tests/Ngs2Parse.cpp diff --git a/core/libs/prx/libSceNgs2.native/CMakeLists.txt b/core/libs/prx/libSceNgs2.native/CMakeLists.txt index b9ed2af05..b212fc733 100644 --- a/core/libs/prx/libSceNgs2.native/CMakeLists.txt +++ b/core/libs/prx/libSceNgs2.native/CMakeLists.txt @@ -8,7 +8,7 @@ if(MINGW) endif() if(BUILD_TESTING) - foreach(ngs2Test IN ITEMS Render Atrac9 Filter Custom) + foreach(ngs2Test IN ITEMS Render Atrac9 Filter Custom Parse) 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/tests/Ngs2Atrac9.cpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp index 27c255202..739d8ae3c 100644 --- a/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp @@ -1,12 +1,9 @@ #include "Ngs2Test.hpp" #include "libatrac9.h" -#include "prx/libc/include/General.hpp" #include #include -#include -#include #include #include #include @@ -179,71 +176,6 @@ static void TestParse() { Require(sceNgs2ParseWaveformData(wrongRate.data(), wrongRate.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT); } -static std::vector PcmFile(std::uint32_t channels, std::uint32_t sampleRate, std::uint32_t bits, std::uint32_t frames) { - const std::uint32_t frameBytes = channels * bits / 8; - std::vector file; - PutTag(file, "RIFF"); - Put32(file, 36 + frames * frameBytes); - PutTag(file, "WAVE"); - PutTag(file, "fmt "); - Put32(file, 16); - Put16(file, 1); - Put16(file, channels); - Put32(file, sampleRate); - Put32(file, sampleRate * frameBytes); - Put16(file, frameBytes); - Put16(file, bits); - PutTag(file, "data"); - Put32(file, frames * frameBytes); - file.resize(file.size() + frames * frameBytes, 0x11); - return file; -} - -static void TestParsePcm() { - const auto file = PcmFile(2, 44100, 16, 100); - Ngs2WaveformInfo info{}; - Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK); - Require(info.format.waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L && info.format.num_channels == 2 && info.format.sample_rate == 44100); - Require(info.data_offset == 44 && info.data_size == 400 && info.num_samples == 100); - Require(info.num_blocks == 1 && info.block[0].data_offset == 44 && info.block[0].data_size == 400 && info.block[0].num_samples == 100); - - auto truncated = file; - truncated.resize(44 + 202); - Require(sceNgs2ParseWaveformData(truncated.data(), truncated.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); - - auto partialFrame = PcmFile(2, 44100, 16, 100); - partialFrame.resize(44 + 402, 0x11); - const std::uint32_t partialSize = 402; - std::memcpy(partialFrame.data() + 40, &partialSize, sizeof(partialSize)); - Require(sceNgs2ParseWaveformData(partialFrame.data(), partialFrame.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); - - const auto eightBit = PcmFile(1, 44100, 8, 10); - Require(sceNgs2ParseWaveformData(eightBit.data(), eightBit.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT); - const auto noChannels = PcmFile(0, 44100, 16, 10); - Require(sceNgs2ParseWaveformData(noChannels.data(), noChannels.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT); -} - -static void TestParseFile() { - const auto wave = PcmFile(1, 22050, 16, 64); - const char* const guestPath = "/aps5_ngs2_parse_file.wav"; - const auto path = ResolvePath_nid_no_patch(guestPath); - { - std::ofstream out(path, std::ios::binary); - const char pad[3]{}; - out.write(pad, sizeof(pad)); - out.write(reinterpret_cast(wave.data()), static_cast(wave.size())); - } - Ngs2WaveformInfo info{}; - Require(sceNgs2ParseWaveformFile(guestPath, 3, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS); - Require(sceNgs2ParseWaveformFile(nullptr, 0, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); - Require(sceNgs2ParseWaveformFile(guestPath, 3, &info) == SCE_NGS2_OK); - Require(info.format.num_channels == 1 && info.format.sample_rate == 22050 && info.num_samples == 64); - Require(info.data_offset == 3 + 44 && info.block[0].data_offset == 3 + 44 && info.data_size == 128); - Require(sceNgs2ParseWaveformFile(guestPath, static_cast(wave.size()) + 4, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); - Require(sceNgs2ParseWaveformFile(guestPath, 0, &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT); - std::filesystem::remove(path); -} - static void TestCalcBlock() { const Ngs2WaveformFormat format{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, Config, 0, 0}; Ngs2WaveformBlock block{}; @@ -303,8 +235,6 @@ int main() { TestSkipAndBlockEnd(reference); TestRepeatAndState(reference); TestParse(); - TestParsePcm(); - TestParseFile(); TestCalcBlock(); TestCorruptSuperframeAtPageEnd(); return 0; diff --git a/core/libs/prx/libSceNgs2.native/tests/Ngs2Parse.cpp b/core/libs/prx/libSceNgs2.native/tests/Ngs2Parse.cpp new file mode 100644 index 000000000..396881d46 --- /dev/null +++ b/core/libs/prx/libSceNgs2.native/tests/Ngs2Parse.cpp @@ -0,0 +1,94 @@ +#include "Ngs2Test.hpp" + +#include "prx/libc/include/General.hpp" + +#include +#include +#include +#include +#include + +static void Put16(std::vector& out, std::uint32_t value) { + out.push_back(static_cast(value)); + out.push_back(static_cast(value >> 8)); +} + +static void Put32(std::vector& out, std::uint32_t value) { + Put16(out, value & 0xffff); + Put16(out, value >> 16); +} + +static void PutTag(std::vector& out, const char* tag) { + out.insert(out.end(), tag, tag + 4); +} + +static std::vector PcmFile(std::uint32_t channels, std::uint32_t sampleRate, std::uint32_t bits, std::uint32_t frames) { + const std::uint32_t frameBytes = channels * bits / 8; + std::vector file; + PutTag(file, "RIFF"); + Put32(file, 36 + frames * frameBytes); + PutTag(file, "WAVE"); + PutTag(file, "fmt "); + Put32(file, 16); + Put16(file, 1); + Put16(file, channels); + Put32(file, sampleRate); + Put32(file, sampleRate * frameBytes); + Put16(file, frameBytes); + Put16(file, bits); + PutTag(file, "data"); + Put32(file, frames * frameBytes); + file.resize(file.size() + frames * frameBytes, 0x11); + return file; +} + +static void TestParsePcm() { + const auto file = PcmFile(2, 44100, 16, 100); + Ngs2WaveformInfo info{}; + Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK); + Require(info.format.waveform_type == SCE_NGS2_WAVEFORM_TYPE_PCM_I16L && info.format.num_channels == 2 && info.format.sample_rate == 44100); + Require(info.data_offset == 44 && info.data_size == 400 && info.num_samples == 100); + Require(info.num_blocks == 1 && info.block[0].data_offset == 44 && info.block[0].data_size == 400 && info.block[0].num_samples == 100); + + auto truncated = file; + truncated.resize(44 + 202); + Require(sceNgs2ParseWaveformData(truncated.data(), truncated.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); + + auto partialFrame = PcmFile(2, 44100, 16, 100); + partialFrame.resize(44 + 402, 0x11); + const std::uint32_t partialSize = 402; + std::memcpy(partialFrame.data() + 40, &partialSize, sizeof(partialSize)); + Require(sceNgs2ParseWaveformData(partialFrame.data(), partialFrame.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); + + const auto eightBit = PcmFile(1, 44100, 8, 10); + Require(sceNgs2ParseWaveformData(eightBit.data(), eightBit.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT); + const auto noChannels = PcmFile(0, 44100, 16, 10); + Require(sceNgs2ParseWaveformData(noChannels.data(), noChannels.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT); +} + +static void TestParseFile() { + const auto wave = PcmFile(1, 22050, 16, 64); + const char* const guestPath = "/aps5_ngs2_parse_file.wav"; + const auto path = ResolvePath_nid_no_patch(guestPath); + { + std::ofstream out(path, std::ios::binary); + const char pad[3]{}; + out.write(pad, sizeof(pad)); + out.write(reinterpret_cast(wave.data()), static_cast(wave.size())); + } + Ngs2WaveformInfo info{}; + Require(sceNgs2ParseWaveformFile(guestPath, 3, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS); + Require(sceNgs2ParseWaveformFile(nullptr, 0, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); + Require(sceNgs2ParseWaveformFile(guestPath, 3, &info) == SCE_NGS2_OK); + Require(info.format.num_channels == 1 && info.format.sample_rate == 22050 && info.num_samples == 64); + Require(info.data_offset == 3 + 44 && info.block[0].data_offset == 3 + 44 && info.data_size == 128); + Require(sceNgs2ParseWaveformFile(guestPath, static_cast(wave.size()) + 4, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA); + Require(sceNgs2ParseWaveformFile(guestPath, 0, &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT); + std::filesystem::remove(path); +} + +int main() { + TestParsePcm(); + TestParseFile(); + return 0; +}