Fix stack overflow when seeking with segments/layers

This commit is contained in:
bnnm 2020-08-03 23:15:59 +02:00
parent 471dadb43f
commit c6bd158700
4 changed files with 42 additions and 23 deletions

View File

@ -28,7 +28,6 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]);
//VGM_LOG("segment decode start: cur=%i, this=%i, into=%i\n", data->current_segment, samples_this_block, vgmstream->samples_into_block);
while (samples_written < sample_count) {
int samples_to_do;
@ -39,7 +38,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
}
/* detect segment change and restart (after loop, but before decode, to allow looping to kick in) */
if (vgmstream->samples_into_block == samples_this_block) {
if (vgmstream->samples_into_block >= samples_this_block) {
data->current_segment++;
/* could happen on last segment trying to decode more samples */
@ -63,6 +62,11 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */
samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER;
if (samples_to_do < 0) { /* ? */
VGM_LOG("SEGMENTED: wrong samples_to_do %i found\n", samples_to_do);
break;
}
render_vgmstream(
use_internal_buffer ?
data->buffer : &outbuf[samples_written * data->output_channels],

View File

@ -243,9 +243,8 @@ static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstre
if (vgmstream->current_sample > vgmstream->num_samples) {
int channels = vgmstream->channels;
int to_do = sample_count;
int done = 0;
memset(buf + done * channels, 0, to_do * sizeof(sample_t) * channels);
memset(buf, 0, sample_count * sizeof(sample_t) * channels);
return sample_count;
}
@ -310,27 +309,33 @@ static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstre
if (vgmstream->current_sample > vgmstream->num_samples) {
int channels = vgmstream->channels;
int to_do = (vgmstream->current_sample - vgmstream->num_samples);
int done = sample_count - to_do;
memset(buf + done * channels, 0, to_do * sizeof(sample_t) * channels);
int32_t excess, decoded;
excess = (vgmstream->current_sample - vgmstream->num_samples);
if (excess > sample_count)
excess = sample_count;
decoded = sample_count - excess;
memset(buf + decoded * channels, 0, excess * sizeof(sample_t) * channels);
return sample_count;
}
return sample_count;
}
static void render_trim(VGMSTREAM* vgmstream) {
/* big-ish buffer since the average trim would be a few seconds for >=2ch at 48000, and less calls = better */
sample_t tmpbuf[0x40000];
int max_samples = 0x40000 / vgmstream->pstate.input_channels;
sample_t* tmpbuf = vgmstream->tmpbuf;
size_t tmpbuf_size = vgmstream->tmpbuf_size;
int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */
while (vgmstream->pstate.trim_begin_left) {
int to_do = vgmstream->pstate.trim_begin_left;
if (to_do > max_samples)
to_do = max_samples;
if (to_do > buf_samples)
to_do = buf_samples;
render_layout(tmpbuf, to_do, vgmstream);
/* just consume samples so no need to apply mixing */
/* no mixing */
vgmstream->pstate.trim_begin_left -= to_do;
}
}
@ -489,18 +494,18 @@ static void seek_force_loop(VGMSTREAM* vgmstream) {
}
static void seek_force_decode(VGMSTREAM* vgmstream, int samples) {
sample_t tmpbuf[0x40000]; /* big-ish buffer as less calls = better */
int32_t buf_samples = 0x40000 / vgmstream->channels; /* base channels, no need to apply mixing */
sample_t* tmpbuf = vgmstream->tmpbuf;
size_t tmpbuf_size = vgmstream->tmpbuf_size;
int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */
int i;
while (samples) {
int to_do = samples;
if (to_do > buf_samples)
to_do = buf_samples;
for (i = 0; i < samples; i += buf_samples) {
int to_get = buf_samples;
if (i + buf_samples > samples)
to_get = samples - i;
render_layout(tmpbuf, to_get, vgmstream);
render_layout(tmpbuf, to_do, vgmstream);
/* no mixing */
samples -= to_do;
}
}

View File

@ -726,6 +726,12 @@ VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) {
if (!vgmstream->loop_ch) goto fail;
}
/* garbage buffer for decode discarding (local bufs may cause stack overflows with segments/layers)
* in theory the bigger the better but in practice there isn't much difference */
vgmstream->tmpbuf_size = 0x10000; /* for all channels */
vgmstream->tmpbuf = malloc(sizeof(sample_t) * vgmstream->tmpbuf_size);
vgmstream->channels = channel_count;
vgmstream->loop_flag = loop_flag;
@ -736,6 +742,7 @@ VGMSTREAM* allocate_vgmstream(int channel_count, int loop_flag) {
fail:
if (vgmstream) {
mixing_close(vgmstream);
free(vgmstream->tmpbuf);
free(vgmstream->ch);
free(vgmstream->start_ch);
free(vgmstream->loop_ch);
@ -776,6 +783,7 @@ void close_vgmstream(VGMSTREAM* vgmstream) {
}
mixing_close(vgmstream);
free(vgmstream->tmpbuf);
free(vgmstream->ch);
free(vgmstream->start_ch);
free(vgmstream->loop_ch);

View File

@ -975,6 +975,8 @@ typedef struct {
play_state_t pstate; /* player state (applied over decoding) */
int loop_count; /* counter of complete loops (1=looped once) */
int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */
sample_t* tmpbuf; /* garbage buffer used for seeking/trimming */
size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels) */
} VGMSTREAM;