From a0b99a8c21c8452bdf6e37e0fbf183fd66a4687b Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 29 Oct 2026 02:13:43 +0100 Subject: [PATCH] ngs2: accept larger custom voice state buffers Co-authored-by: Frenzied --- src/libs/ngs2.cpp | 28 +++++++++++++-------------- tests/Ngs2SamplerTests.cpp | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/libs/ngs2.cpp b/src/libs/ngs2.cpp index 4fac09def..30f2651b6 100644 --- a/src/libs/ngs2.cpp +++ b/src/libs/ngs2.cpp @@ -2701,24 +2701,22 @@ int KYTY_SYSV_ABI Ngs2VoiceGetState(uintptr_t voice_handle, Ngs2VoiceState* stat submixer->voice_state.state_flags = Ngs2GetStateFlags(voice); break; } - case Ngs2RackType::CustomMastering: { - const auto configured_size = - voice->rack->option.custom_mastering.custom_rack_option.state_size; - EXIT_NOT_IMPLEMENTED(configured_size < sizeof(Ngs2CustomMasteringVoiceState)); - EXIT_NOT_IMPLEMENTED(state_size != configured_size); - std::memset(state, 0, state_size); - auto* mastering = reinterpret_cast(state); - mastering->voice_state.state_flags = Ngs2GetStateFlags(voice); - break; - } + case Ngs2RackType::CustomMastering: case Ngs2RackType::CustomSampler: { + const bool sampler = voice->rack->type == Ngs2RackType::CustomSampler; const auto configured_size = voice->rack->option.custom_sampler.custom_rack_option.state_size; - EXIT_NOT_IMPLEMENTED(configured_size < sizeof(Ngs2CustomSamplerVoiceState)); - EXIT_NOT_IMPLEMENTED(state_size != configured_size); - std::memset(state, 0, state_size); - auto* sampler = reinterpret_cast(state); - Ngs2FillSamplerState(*voice, *sampler); + EXIT_NOT_IMPLEMENTED(configured_size < (sampler ? sizeof(Ngs2CustomSamplerVoiceState) + : sizeof(Ngs2CustomMasteringVoiceState))); + if (state_size < configured_size) { + return static_cast(0x804a8011u); + } + std::memset(state, 0, configured_size); + if (sampler) { + Ngs2FillSamplerState(*voice, *reinterpret_cast(state)); + } else { + state->state_flags = Ngs2GetStateFlags(voice); + } break; } case Ngs2RackType::Sampler: { diff --git a/tests/Ngs2SamplerTests.cpp b/tests/Ngs2SamplerTests.cpp index 1b1f1533c..85b4e22f0 100644 --- a/tests/Ngs2SamplerTests.cpp +++ b/tests/Ngs2SamplerTests.cpp @@ -867,6 +867,44 @@ void TestCustomMasteringChannelMixer() { } } +void TestCustomVoiceStateCapacity() { + for (auto type: {Ngs2RackType::CustomSampler, Ngs2RackType::CustomMastering}) { + Fixture f(48000, 1, Ngs2RackType::CustomSampler); + f.rack.type = type; + f.rack.option.custom_sampler.custom_rack_option.state_size = 212; + f.voice.state_flags = 3; + f.voice.decoded_samples = 37; + f.voice.decoded_bytes = 128; + alignas(Ngs2CustomSamplerVoiceState) std::array storage; + auto* state = reinterpret_cast(storage.data() + 8); + const auto handle = reinterpret_cast(&f.voice); + for (size_t capacity: {212u, 368u}) { + storage.fill(0xa5); + Check(Ngs2VoiceGetState(handle, state, capacity) == OK && state->state_flags == 3 && + state->error_code == 0 && + std::all_of(storage.begin(), storage.begin() + 8, + [](uint8_t v) { return v == 0xa5; }) && + std::all_of(storage.begin() + 220, storage.end(), + [](uint8_t v) { return v == 0xa5; }), + "oversized custom state buffer was rejected or overwritten past configured state"); + if (type == Ngs2RackType::CustomSampler) { + const auto* sampler = reinterpret_cast(state); + Check(sampler->num_decoded_samples == 37 && sampler->decoded_data_size == 128, + "custom sampler state omitted playback progress"); + } + const size_t base_size = type == Ngs2RackType::CustomSampler + ? sizeof(Ngs2CustomSamplerVoiceState) : sizeof(Ngs2CustomMasteringVoiceState); + Check(std::all_of(storage.begin() + 8 + base_size, storage.begin() + 220, + [](uint8_t v) { return v == 0; }), + "configured custom state extension was not initialized"); + } + storage.fill(0xa5); + Check(Ngs2VoiceGetState(handle, state, 211) == static_cast(0x804a8011u) && + std::ranges::all_of(storage, [](uint8_t v) { return v == 0xa5; }), + "undersized custom state buffer was accepted or modified"); + } +} + void TestPitchLoopAndSkip() { Fixture f(24000); const int16_t pcm[] = {-30000, 8192, 16384}; @@ -1766,6 +1804,7 @@ int main() { TestAtrac9Discrete(); TestPcm16RenderOutput(); TestCustomMasteringChannelMixer(); + TestCustomVoiceStateCapacity(); TestSubmixerSetupAndRouting(); TestCustomSideChainUserFx(); TestWaveformBlockGeometry();