Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4fb7022
fix(libSceNgs2.native): report one ATRAC9 frame as the parsed decoder…
MarrasFederico Oct 21, 2026
9cde6f6
Merge remote-tracking branch 'upstream/main' into feat/ngs2-calc-vag-…
MarrasFederico Oct 21, 2026
3c242fd
feat(libSceNgs2.native): map ATRAC9 waveform blocks as the module doe…
MarrasFederico Oct 21, 2026
6978f11
Merge branch 'fix/ngs2-atrac9-parsed-delay' into feat/ngs2-atrac9-mod…
MarrasFederico Oct 21, 2026
69b21ad
fix(libSceNgs2.native): parse ATRAC9 smpl loops as the module does
MarrasFederico Oct 21, 2026
997409e
Merge commit '3ca07d086d5f44fbe3c00370e7f03953ab46ca1b' into fix/ngs2…
MarrasFederico Oct 22, 2026
d20d4cb
Merge branch 'feat/ngs2-calc-vag-block' into feat/ngs2-calc-atrac9-block
MarrasFederico Oct 22, 2026
b655a90
Merge branch 'feat/ngs2-calc-atrac9-block' into feat/ngs2-atrac9-modu…
MarrasFederico Oct 22, 2026
be6d4f2
Merge branch 'fix/ngs2-atrac9-parsed-delay' into feat/ngs2-atrac9-mod…
MarrasFederico Oct 22, 2026
6660dd0
fix(libSceNgs2.native): throw for ATRAC9 loops inside the decoder del…
MarrasFederico Oct 23, 2026
0477c9c
Merge remote-tracking branch 'upstream/main' into feat/ngs2-atrac9-mo…
MarrasFederico Oct 23, 2026
601d6db
Merge remote-tracking branch 'upstream/main' into feat/ngs2-calc-atra…
MarrasFederico Oct 23, 2026
767e188
test(libSceNgs2.native): expect the module's ATRAC9 block in the fram…
MarrasFederico Oct 23, 2026
1a743bf
Merge remote-tracking branch 'upstream/main' into fix/ngs2-atrac9-par…
MarrasFederico Oct 23, 2026
25b6f6e
Merge branch 'feat/ngs2-calc-atrac9-block' into feat/ngs2-atrac9-modu…
MarrasFederico Oct 23, 2026
d82faf1
Merge branch 'fix/ngs2-atrac9-parsed-delay' into feat/ngs2-atrac9-mod…
MarrasFederico Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 42 additions & 47 deletions core/libs/prx/libSceNgs2.native/src/Atrac9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static constexpr std::uint32_t WAVEFORM_TYPE_PCM_FIRST = 0x10;
static constexpr std::uint32_t WAVEFORM_TYPE_PCM_LAST = 0x1b;
static constexpr std::uint32_t WAVEFORM_TYPE_VAG = 0x1c;
static constexpr std::uint32_t VAG_FRAME_SAMPLES = 28;
static constexpr std::uint32_t ATRAC9_FRAME_SAMPLES[16] = {0, 64, 0, 0, 128, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0};
static constexpr std::uint32_t PCM_BYTES_PER_SAMPLE[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 8, 8};

static bool IsPcmType(std::uint32_t type) {
Expand Down Expand Up @@ -56,6 +57,25 @@ static Ngs2WaveformBlock VagBlock(std::uint32_t channels, std::uint32_t pos, std
return block;
}

static Ngs2WaveformBlock Atrac9Block(std::uint32_t config, std::uint32_t pos, std::uint32_t count) {
const std::uint32_t rateIndex = (config >> 20) & 0xfu;
const std::uint32_t frameSamples = ATRAC9_FRAME_SAMPLES[rateIndex];
if (frameSamples == 0) throw std::runtime_error("NGS2: ATRAC9 waveform blocks at sampling rate index " + std::to_string(rateIndex) + " are not implemented");
const std::uint32_t superframeShift = (config >> 3) & 3u;
const std::uint32_t superframeBytes = (((config >> 5) & 0x7ffu) + 1u) << superframeShift;
const std::uint32_t superframeSamples = frameSamples << superframeShift;
const std::uint32_t first = pos / superframeSamples;
const std::uint32_t lead = frameSamples + pos;
const std::uint32_t past = (count + lead + superframeSamples - 1u) / superframeSamples;
const std::uint32_t offset = first * superframeBytes;
Ngs2WaveformBlock block{};
block.data_offset = offset;
block.data_size = past * superframeBytes - offset;
block.num_skip_samples = lead - first * superframeSamples;
block.num_samples = count;
return block;
}

void Ngs2Atrac9DecoderDeleter::operator()(void* handle) const {
Atrac9ReleaseHandle(handle);
}
Expand Down Expand Up @@ -285,44 +305,31 @@ static int ReadChunks(std::size_t size, TRead&& read, RiffChunks& chunks) {
return SCE_NGS2_OK;
}

static bool AddAtrac9Range(Ngs2WaveformInfo& info, std::uint64_t begin, std::uint32_t samples, std::uint32_t repeats) {
if (samples == 0) return true;
const auto preroll = std::min<std::uint64_t>(begin, info.num_audio_unit_samples);
const auto firstFrame = (begin - preroll) / info.num_audio_frame_samples;
const auto skip = begin - firstFrame * info.num_audio_frame_samples;
const auto frames = (skip + samples + info.num_audio_frame_samples - 1) / info.num_audio_frame_samples;
const auto offset = firstFrame * info.audio_frame_size;
const auto bytes = frames * info.audio_frame_size;
if (offset > info.data_size || bytes > info.data_size - offset) return false;
auto& block = info.block[info.num_blocks++];
block = {info.data_offset + offset, bytes, repeats, static_cast<std::uint32_t>(skip), samples, 0, 0};
return true;
static void AddAtrac9Block(Ngs2WaveformInfo& info, std::uint32_t begin, std::uint32_t samples, std::uint32_t repeats) {
auto block = Atrac9Block(info.format.config_data, begin, samples);
if (block.data_offset > info.data_size || block.data_size > info.data_size - block.data_offset)
throw std::runtime_error("NGS2: an ATRAC9 loop block reaching past the data chunk is not implemented");
block.data_offset += info.data_offset;
block.num_repeats = repeats;
info.block[info.num_blocks++] = block;
}

static int ParseAtrac9Loop(const RiffChunks& chunks, Ngs2WaveformInfo& info) {
if (chunks.sampler == nullptr) return SCE_NGS2_OK;
if (chunks.samplerSize < 36) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
const auto count = ReadLe32(chunks.sampler + 28);
if (count > (chunks.samplerSize - 36) / 24) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
if (count == 0) return SCE_NGS2_OK;
if (count != 1) throw std::runtime_error("NGS2: multiple waveform loops are not implemented");
const auto* loop = chunks.sampler + 36;
if (ReadLe32(loop + 4) != 0 || ReadLe32(loop + 16) != 0)
throw std::runtime_error("NGS2: non-forward or fractional waveform loops are not implemented");
const std::uint64_t begin = ReadLe32(loop + 8);
const std::uint64_t end = static_cast<std::uint64_t>(ReadLe32(loop + 12)) + 1;
const auto skip = info.block[0].num_skip_samples;
const std::uint64_t waveformEnd = static_cast<std::uint64_t>(skip) + info.num_samples;
if (begin < skip || begin >= end || end > waveformEnd) return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
const auto plays = ReadLe32(loop + 20);
info.loop_begin_position = static_cast<std::uint32_t>(begin - skip);
info.loop_end_position = static_cast<std::uint32_t>(end - skip);
if (chunks.samplerSize < 32) throw std::runtime_error("NGS2: a smpl chunk too short for its loop count is not implemented");
if (ReadLe32(chunks.sampler + 28) == 0) return SCE_NGS2_OK;
if (chunks.samplerSize < 52) throw std::runtime_error("NGS2: a smpl chunk too short for its first loop is not implemented");
if (ReadLe32(chunks.sampler + 44) < info.num_delay_samples) throw std::runtime_error("NGS2: an ATRAC9 loop starting inside the decoder delay is not implemented");
const std::uint32_t begin = ReadLe32(chunks.sampler + 44) - info.num_delay_samples;
const std::uint32_t end = ReadLe32(chunks.sampler + 48) - info.num_delay_samples + 1u;
info.loop_begin_position = begin;
info.loop_end_position = end;
if (end <= begin) return SCE_NGS2_OK;
if (end > info.num_samples) throw std::runtime_error("NGS2: a waveform loop ending past the last sample is not implemented");
info.num_blocks = 0;
info.block[0] = {};
if (!AddAtrac9Range(info, skip, info.loop_begin_position, 0) ||
!AddAtrac9Range(info, begin, static_cast<std::uint32_t>(end - begin), plays == 0 ? UINT32_MAX : plays - 1) ||
!AddAtrac9Range(info, end, static_cast<std::uint32_t>(waveformEnd - end), 0))
return SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA;
if (begin != 0) AddAtrac9Block(info, 0, begin, 0);
AddAtrac9Block(info, begin, end - begin, UINT32_MAX);
if (end != info.num_samples) AddAtrac9Block(info, end, info.num_samples - end, 0);
return SCE_NGS2_OK;
}

Expand All @@ -346,7 +353,7 @@ static int ParseAtrac9(const RiffChunks& chunks, Ngs2WaveformInfo& info) {
info.num_audio_unit_per_frame = static_cast<std::uint32_t>(codec.framesInSuperframe);
info.audio_frame_size = static_cast<std::uint32_t>(codec.superframeSize);
info.num_audio_frame_samples = static_cast<std::uint32_t>(codec.frameSamples * codec.framesInSuperframe);
info.num_delay_samples = ReadLe32(chunks.fact + 4);
info.num_delay_samples = static_cast<std::uint32_t>(codec.frameSamples);
info.num_blocks = 1;
info.block[0].data_offset = chunks.dataOffset;
info.block[0].data_size = chunks.dataSize;
Expand Down Expand Up @@ -450,19 +457,7 @@ int APS5_VABI sceNgs2CalcWaveformBlock(const Ngs2WaveformFormat* format, uint32_
return SCE_NGS2_OK;
}
if (type != SCE_NGS2_WAVEFORM_TYPE_ATRAC9) return SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT;
if (format->num_channels == 0 || format->num_channels > NGS2_MAX_CHANNELS || format->sample_rate == 0 || format->sample_rate > MAX_SAMPLE_RATE) {
return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
}
Atrac9CodecInfo codec{};
if (!MatchingCodecInfo(*format, codec)) return SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT;
const std::uint64_t superframeSamples = static_cast<std::uint64_t>(codec.frameSamples) * codec.framesInSuperframe;
const std::uint64_t skip = sample_pos % superframeSamples;
block->data_offset = sample_pos / superframeSamples * codec.superframeSize;
if (num_samples != 0) {
block->data_size = (skip + num_samples + superframeSamples - 1) / superframeSamples * codec.superframeSize;
block->num_skip_samples = static_cast<std::uint32_t>(skip);
}
block->num_samples = num_samples;
*block = Atrac9Block(format->config_data, sample_pos, num_samples);
return SCE_NGS2_OK;
}

Expand Down
128 changes: 96 additions & 32 deletions core/libs/prx/libSceNgs2.native/tests/Ngs2Atrac9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
#include <cstdint>
#include <cmath>
#include <cstring>
#include <iterator>
#include <random>
#include <stdexcept>
#include <tuple>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -174,25 +176,53 @@ static void TestParse() {
Require(info.num_audio_unit_per_frame == 4 && info.audio_frame_size == SuperframeBytes && info.num_audio_frame_samples == SuperframeSamples);
Require(info.num_blocks == 1 && info.block[0].data_offset == info.data_offset && info.block[0].data_size == SuperframeBytes);
Require(info.block[0].num_skip_samples == 256 && info.block[0].num_samples == 900);
for (const std::uint32_t factDelay : {0u, 1u, 900u, 0xffffffffu}) {
auto other = file;
constexpr char factTag[] = {'f', 'a', 'c', 't'};
const auto fact = static_cast<std::size_t>(std::search(other.begin(), other.end(), std::begin(factTag), std::end(factTag)) - other.begin());
std::memcpy(other.data() + fact + 12, &factDelay, sizeof(factDelay));
Ngs2WaveformInfo parsed{};
Require(sceNgs2ParseWaveformData(other.data(), other.size(), &parsed) == SCE_NGS2_OK);
Require(parsed.num_delay_samples == 256 && parsed.block[0].num_skip_samples == 256 && parsed.num_samples == 900);
}

Require(sceNgs2ParseWaveformData(file.data(), 11, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
Require(sceNgs2ParseWaveformData(Superframe, SuperframeBytes, &info) == SCE_NGS2_ERROR_UNKNOWN_WAVEFORM_FORMAT);
const auto wrongRate = At9File(44100);
Require(sceNgs2ParseWaveformData(wrongRate.data(), wrongRate.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
}

static void RequireAtrac9Block(std::uint32_t config, std::uint32_t pos, std::uint32_t count, std::uint64_t offset, std::uint64_t size, std::uint32_t skip) {
const Ngs2WaveformFormat format{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, config, 0, 0};
Ngs2WaveformBlock block;
std::memset(&block, 0xab, sizeof(block));
Require(sceNgs2CalcWaveformBlock(&format, pos, count, &block) == SCE_NGS2_OK);
Require(block.data_offset == offset && block.data_size == size && block.num_skip_samples == skip && block.num_samples == count);
Require(block.num_repeats == 0 && block.reserved == 0 && block.user_data == 0);
}

static void TestCalcBlock() {
const Ngs2WaveformFormat format{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, Config, 0, 0};
Ngs2WaveformBlock block{};
Require(sceNgs2CalcWaveformBlock(&format, 1500, 600, nullptr) == SCE_NGS2_ERROR_INVALID_OUT_ADDRESS);
Require(sceNgs2CalcWaveformBlock(&format, 1500, 600, &block) == SCE_NGS2_OK);
Require(block.data_offset == SuperframeBytes && block.data_size == 2 * SuperframeBytes && block.num_skip_samples == 476 && block.num_samples == 600);
Require(sceNgs2CalcWaveformBlock(&format, 1500, 0, &block) == SCE_NGS2_OK);
Require(block.data_offset == SuperframeBytes && block.data_size == 0 && block.num_skip_samples == 0 && block.num_samples == 0);
const Ngs2WaveformFormat stereo{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 2, 48000, Config, 0, 0};
Require(sceNgs2CalcWaveformBlock(&stereo, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
const Ngs2WaveformFormat silent{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 0, 48000, Config, 0, 0};
Require(sceNgs2CalcWaveformBlock(&silent, 0, 1, &block) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
RequireAtrac9Block(Config, 0, 768, 0, SuperframeBytes, 256);
RequireAtrac9Block(Config, 0, 769, 0, 2 * SuperframeBytes, 256);
RequireAtrac9Block(Config, 1500, 600, SuperframeBytes, 2 * SuperframeBytes, 732);
RequireAtrac9Block(Config, 1500, 0, SuperframeBytes, SuperframeBytes, 732);
RequireAtrac9Block(Config, 4294967195u, 1000, 1073741568u, 3221226240u, 1179);
RequireAtrac9Block(0xFE7007E0, 300, 10, 64, 128, 300);
RequireAtrac9Block(0xFE4007F0, 600, 100, SuperframeBytes, SuperframeBytes, 216);
RequireAtrac9Block(0xFE1007F0, 0, 1, 0, SuperframeBytes, 64);
Ngs2WaveformBlock block{};
const Ngs2WaveformFormat stereo{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 2, 44100, Config, 0, 0};
Require(sceNgs2CalcWaveformBlock(&stereo, 0, 1, &block) == SCE_NGS2_OK && block.data_size == SuperframeBytes && block.num_skip_samples == 256);
const Ngs2WaveformFormat silent{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 0, 0, Config, 0, 0};
Require(sceNgs2CalcWaveformBlock(&silent, 0, 1, &block) == SCE_NGS2_OK && block.data_size == SuperframeBytes && block.num_skip_samples == 256);
for (const std::uint32_t rateIndex : {0u, 2u, 3u, 5u, 6u, 8u, 15u}) {
const Ngs2WaveformFormat unknown{SCE_NGS2_WAVEFORM_TYPE_ATRAC9, 1, 48000, (Config & ~0x00F00000u) | rateIndex << 20, 0, 0};
bool threw = false;
try { sceNgs2CalcWaveformBlock(&unknown, 0, 1, &block); } catch (const std::runtime_error&) { threw = true; }
Require(threw);
}
}

static void TestFrameInfo() {
Expand All @@ -211,17 +241,14 @@ static void TestFrameInfo() {
};
for (const auto& format : supported) {
Ngs2WaveformBlock block{};
Require(sceNgs2CalcWaveformBlock(&format, 0, SuperframeSamples, &block) == SCE_NGS2_OK && block.data_size == SuperframeBytes);
Require(sceNgs2CalcWaveformBlock(&format, 0, SuperframeSamples, &block) == SCE_NGS2_OK && block.data_size == 2 * SuperframeBytes);
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
Require(sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]) == SCE_NGS2_OK);
Require(values[0] == SuperframeBytes && values[1] == SuperframeSamples && values[2] == 4 && values[3] == SuperframeSamples / 4);
}
for (const auto& format : rejected) {
Ngs2WaveformBlock block{};
const int calc = sceNgs2CalcWaveformBlock(&format, 0, SuperframeSamples, &block);
Require(calc == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
Require(sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]) == calc);
Require(sceNgs2GetWaveformFrameInfo(&format, &values[0], &values[1], &values[2], &values[3]) == SCE_NGS2_ERROR_INVALID_WAVEFORM_FORMAT);
Require(values[0] == 0xa1 && values[1] == 0xa2 && values[2] == 0xa3 && values[3] == 0xa4);
}
std::uint32_t values[4] = {0xa1, 0xa2, 0xa3, 0xa4};
Expand Down Expand Up @@ -324,20 +351,20 @@ static void TestParseLoops() {
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK);
Require(info.loop_begin_position == begin && info.loop_end_position == end && info.num_samples == samples);
Require(info.num_blocks == 1 + (begin != 0) + (end != samples));
Require(info.block[begin != 0].num_repeats == 1);
Require(info.block[begin != 0].num_repeats == UINT32_MAX);
const auto system = CreateSystem();
const auto voice = Voice(CreateRack(system, SCE_NGS2_RACK_ID_SAMPLER));
Control(voice, SCE_NGS2_SAMPLER_VOICE_PARAM_SETUP, Ngs2SamplerVoiceSetupParam{{}, info.format});
Control(voice, SCE_NGS2_SAMPLER_VOICE_PARAM_ADD_WAVEFORM_BLOCKS,
Ngs2SamplerVoiceWaveformBlocksParam{{}, file.data(), 0, info.num_blocks, info.block});
Patch(voice, Mastering(system, 1));
Event(voice, SCE_NGS2_VOICE_EVENT_PLAY);
const auto rendered = Render(system, samples + end - begin + Grain);
const auto loop = end - begin;
const auto rendered = Render(system, end + 2 * loop);
for (std::uint32_t i = 0; i < rendered.size(); ++i) {
const auto index = i < end ? i : i - (end - begin);
Require(rendered[i] == (i < samples + end - begin ? reference[256 + index] : 0.0f));
const auto index = i < end ? i : begin + (i - end) % loop;
Require(rendered[i] == reference[256 + index]);
}
Require(Flags(voice) == 0);
Require(sceNgs2SystemDestroy(system, nullptr) == 0);
}
}
Expand Down Expand Up @@ -365,28 +392,65 @@ static void TestParseLoops() {
Require(sceNgs2SystemDestroy(system, nullptr) == 0);
}

static bool ParseThrows(const std::vector<std::uint8_t>& file) {
Ngs2WaveformInfo info{};
try {
sceNgs2ParseWaveformData(file.data(), file.size(), &info);
} catch (const std::runtime_error&) {
return true;
}
return false;
}

static std::vector<std::uint8_t> ShortSampler(std::vector<std::uint8_t> file, std::uint32_t size) {
file.erase(file.begin() + 100 + size, file.begin() + 160);
Set32(file, 96, size);
Set32(file, 4, static_cast<std::uint32_t>(file.size() - 8));
return file;
}

static void TestMalformedLoops() {
const auto original = LoopedFile(100, 700, 0);
Ngs2WaveformInfo info{};
for (const auto [offset, value] : {std::pair<std::size_t, std::uint32_t>{128, 2}, {144, 0}, {148, 255}, {148, 5000}}) {
Ngs2WaveformInfo expected{};
Require(sceNgs2ParseWaveformData(original.data(), original.size(), &expected) == SCE_NGS2_OK && expected.num_blocks == 3);
for (const auto [offset, value] : {std::pair<std::size_t, std::uint32_t>{128, 2}, {140, 1}, {140, 2}, {152, 0x80000000u}, {156, 5}}) {
auto file = original;
Set32(file, offset, value);
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK);
Require(info.loop_begin_position == 100 && info.loop_end_position == 700 && info.num_blocks == 3);
for (std::uint32_t block = 0; block < 3; ++block) Require(std::memcmp(&info.block[block], &expected.block[block], sizeof(Ngs2WaveformBlock)) == 0);
}
for (const auto [start, last, begin, end] : {std::tuple<std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t>{256, 255, 0, 0}, {356, 255, 100, 0}, {956, 955, 700, 700}, {956, 700, 700, 445}}) {
auto file = original;
Set32(file, 144, start);
Set32(file, 148, last);
Ngs2WaveformInfo info{};
Require(sceNgs2ParseWaveformData(file.data(), file.size(), &info) == SCE_NGS2_OK);
Require(info.loop_begin_position == begin && info.loop_end_position == end && info.num_blocks == 1);
Require(info.block[0].num_repeats == 0 && info.block[0].num_samples == info.num_samples);
}
for (const auto offset : {140u, 152u}) {
for (const std::uint32_t last : {4096u, 5000u, 0xffffffffu}) {
auto file = original;
Set32(file, offset, 1);
bool rejected = false;
try { sceNgs2ParseWaveformData(file.data(), file.size(), &info); } catch (const std::runtime_error&) { rejected = true; }
Require(rejected);
Set32(file, 148, last);
Require(ParseThrows(file));
}
for (const std::uint32_t start : {0u, 255u}) {
auto file = original;
Set32(file, 144, start);
Require(ParseThrows(file));
}
Ngs2WaveformInfo info{};
auto pastData = original;
Set32(pastData, 80, 4 * SuperframeSamples - 255);
Require(ParseThrows(pastData));
Set32(pastData, 80, 4 * SuperframeSamples - 256);
Require(sceNgs2ParseWaveformData(pastData.data(), pastData.size(), &info) == SCE_NGS2_OK && info.num_blocks == 3);
for (std::size_t size = 92; size < 168; ++size)
Require(sceNgs2ParseWaveformData(original.data(), size, &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
auto shortSampler = original;
shortSampler.erase(shortSampler.begin() + 100 + 35, shortSampler.begin() + 160);
Set32(shortSampler, 96, 35);
Set32(shortSampler, 4, static_cast<std::uint32_t>(shortSampler.size() - 8));
Require(sceNgs2ParseWaveformData(shortSampler.data(), shortSampler.size(), &info) == SCE_NGS2_ERROR_INVALID_WAVEFORM_DATA);
for (const std::uint32_t size : {28u, 36u, 50u}) Require(ParseThrows(ShortSampler(original, size)));
const auto minimal = ShortSampler(original, 52);
Require(sceNgs2ParseWaveformData(minimal.data(), minimal.size(), &info) == SCE_NGS2_OK && info.num_blocks == 3);
}

static std::uint8_t* SuperframeBeforeGuardPage() {
Expand Down
Loading
Loading