From 0e10b75bebebd200d0b215d30b7bc849d76aaccd Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 13 Mar 2017 20:04:09 +0100 Subject: [PATCH 1/6] Fix interleaved FSB MPEG decoding Padding doesn't apply to Layer II FSBs, and it needed to manually get info from MPEG frames since mpg123 functions weren't always working. --- src/coding/mpeg_decoder.c | 287 +++++++++++++++++++++++++++++--------- src/vgmstream.h | 5 +- 2 files changed, 222 insertions(+), 70 deletions(-) diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index 789a15ef..fad917ea 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -7,37 +7,30 @@ #define AHX_EXPECTED_FRAME_SIZE 0x414 #define MPEG_DEFAULT_BUFFER_SIZE 0x1000 /* should be >= AHX_EXPECTED_FRAME_SIZE */ -#define MPEG_SYNC_BITS 0xFFE00000 -#define MPEG_PADDING_BIT 0x200 -static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int interleaved, int fixed_frame_size, int fsb_padding); +typedef struct { + int version; + int layer; + int bit_rate; + int sample_rate; + int frame_samples; + int frame_size; /* bytes */ + int channels; + coding_t coding_type; +} mpeg_frame_info; + static mpg123_handle * init_mpg123_handle(); - static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size); - -static void update_frame_sizes(mpeg_codec_data * data, VGMSTREAMCHANNEL *stream); -static void update_base_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamFile, off_t start_offset, int fixed_frame_size, int current_frame_size, int fsb_padding); - +static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset); +static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info); /** * Inits regular MPEG. */ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) { - return init_mpeg_codec_data_internal(streamfile, start_offset, coding_type, channels, 0, 0, 0); -} - -/** - * Init interleaved MPEG. - */ -mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int fixed_frame_size, int fsb_padding) { - return init_mpeg_codec_data_internal(streamfile, start_offset, coding_type, channels, 1, fixed_frame_size, fsb_padding); -} - -static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int interleaved, int fixed_frame_size, int fsb_padding) { mpeg_codec_data *data = NULL; - int current_frame_size = 0; /* init codec */ data = calloc(1,sizeof(mpeg_codec_data)); @@ -69,7 +62,15 @@ static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, of read_offset+=1; rc = mpg123_decode(main_m, data->buffer,data->buffer_size, NULL,0, &bytes_done); - if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) goto fail; //todo handle MPG123_DONE + if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) { + VGM_LOG("MPEG: unable to set up mpp123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset); + goto fail; //todo handle MPG123_DONE? + } + if (read_offset > 0x5000) { /* don't hang in some incorrectly detected formats */ + VGM_LOG("MPEG: unable to find mpeg data @ 0x%08lx to 0x%08lx\n", start_offset, read_offset); + goto fail; + } + } while (rc != MPG123_NEW_FORMAT); /* check first frame header and validate */ @@ -82,7 +83,7 @@ static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, of goto fail; if (sample_rate_per_frame != mi.rate) goto fail; - if ((channels != -1 && channels_per_frame != channels && !interleaved)) + if ((channels != -1 && channels_per_frame != channels)) goto fail; if (mi.version == MPG123_1_0 && mi.layer == 1) @@ -119,28 +120,69 @@ static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, of data->channels_per_frame = channels_per_frame; data->samples_per_frame = samples_per_frame; - /* unlikely (can fixed with bigger buffer or a feed loop) */ - if (mi.framesize > data->buffer_size) - goto fail; - current_frame_size = mi.framesize; - /* reinit, to ignore the reading we've done so far */ mpg123_open_feed(main_m); } - /* Init interleaved audio, which needs separate decoders per stream and frame size stuff. - * We still leave data->m as a "base" info/format to simplify some stuff (could be improved) */ - if (interleaved) { + return data; + +fail: + free_mpeg(data); + return NULL; +} + +/** + * Init interleaved MPEG (also accepts normal MPEGs, but it's less error tolerant than normal MPEG init). + */ +mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int fixed_frame_size, int fsb_padding) { + mpeg_codec_data *data = NULL; + + /* init codec */ + data = calloc(1,sizeof(mpeg_codec_data)); + if (!data) goto fail; + + data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE; + data->buffer = calloc(sizeof(uint8_t), data->buffer_size); + if (!data->buffer) goto fail; + + data->m = init_mpg123_handle(); + if (!data->m) goto fail; + + /* interleaved setup */ + { + mpeg_frame_info info; int i; - data->interleaved = interleaved; + + data->interleaved = 1; + + /* get frame info at offset */ + if ( !mpeg_get_frame_info(streamfile, start_offset, &info) ) + goto fail; + + *coding_type = info.coding_type; + data->channels_per_frame = info.channels; + data->sample_rate_per_frame = info.sample_rate; + data->samples_per_frame = info.frame_samples; + + data->fixed_frame_size = fixed_frame_size; + data->current_frame_size = fixed_frame_size; + data->fsb_padding = fsb_padding; + + /* get frame size from the first frame as it can be used externally to calc interleave */ + if ( !update_frame_sizes(data, streamfile, start_offset) ) + goto fail; + + /* unlikely, can fixed with bigger buffer or a feed loop */ + if (info.frame_size > data->buffer_size) + goto fail; if (channels < 1 || channels > 32) goto fail; /* arbitrary max */ if (channels < data->channels_per_frame) goto fail; - update_base_frame_sizes(data, streamfile, start_offset, fixed_frame_size, current_frame_size, fsb_padding); - if (!data->base_frame_size) goto fail; - + //todo improve (less buffers / simplify data->ms) + /* Init interleaved audio, which needs separate decoders per stream and frame size stuff. + * We still leave data->m as a "base" info/format to simplify some stuff */ data->ms_size = channels / data->channels_per_frame; data->ms = calloc(sizeof(mpg123_handle *), data->ms_size); for (i=0; i < data->ms_size; i++) { @@ -165,6 +207,7 @@ fail: return NULL; } + /** * Inits MPEG for AHX, which ignores frame headers. */ @@ -294,7 +337,7 @@ static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data /** * Decode interleaved (multichannel) MPEG. Works with mono/stereo too. - * Channels (1 or 2), samples and frame size per stream should be constant. //todo extra validations + * Channels (1 or 2), samples and frame size per stream should be constant. * * Reads frame 'streams' (ex. 4ch = 1+1+1+1 = 4 streams or 2+2 = 2 streams), decodes * samples per stream and muxes them into a single internal buffer before copying to outbuf @@ -332,6 +375,21 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data for (i=0; i < data->ms_size; i++) { decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size); } + + /* discard (for looping): 'remove' decoded samples from the buffer */ + if (data->samples_to_discard) { + size_t bytes_to_discard = data->samples_to_discard * sizeof(sample) * channels; + + /* 'remove' all buffer at most */ + if (bytes_to_discard > data->bytes_in_interleave_buffer) + bytes_to_discard = data->bytes_in_interleave_buffer; + + /* pretend the samples were used up */ + data->bytes_used_in_interleave_buffer = bytes_to_discard; + + /* and readjust discard */ + data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;; + } } } @@ -343,13 +401,24 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data */ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size) { size_t bytes_done; + size_t stream_size = get_streamfile_size(stream->streamfile); /* decode samples from 1 full frame */ do { int rc; /* padded frame stuff */ - update_frame_sizes(data, stream); + rc = update_frame_sizes(data, stream->streamfile, stream->offset); + /* ignore any errors and continue; mpg123 will probably sync */ + VGM_ASSERT(rc==0, "MPEG: frame error @ 0x%08lx (prev size=0x%x / padding=0x%x)\n", stream->offset, data->current_frame_size, data->current_padding); + + /* extra EOF check for edge cases when the caller tries to read more samples than possible */ + if (stream->offset > stream_size) { + memset(data->frame_buffer, 0, data->frame_buffer_size); + bytes_done = data->frame_buffer_size; + break; + } + /* read more raw data (only 1 frame, to check interleave block end) */ if (!data->buffer_full) { @@ -387,7 +456,7 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_ /* samples per frame should be constant... */ if (bytes_done > 0 && bytes_done < data->frame_buffer_size) { - VGM_LOG("borked frame: %i bytes done, expected %i, rc=%i\n", bytes_done, data->frame_buffer_size, rc); + VGM_LOG("MPEG: borked frame @ 0x%08lx (%i bytes done, expected %i, rc=%i)\n", stream->offset, bytes_done, data->frame_buffer_size, rc); memset(data->frame_buffer + bytes_done, 0, data->frame_buffer_size - bytes_done); bytes_done = data->frame_buffer_size; } @@ -424,44 +493,36 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_ } /** - * Very Clunky Stuff for FSBs of varying padding sizes per frame. + * Get frame size info for the current frame, needed by FSBs of varying padding. * Padding sometimes contains next frame header so we can't feed it to mpg123 or it gets confused. * Expected to be called at the beginning of a new frame. */ -static void update_frame_sizes(mpeg_codec_data * data, VGMSTREAMCHANNEL *stream) { +static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset) { if (!data->fixed_frame_size) { - /* Manually fix frame size. Not ideal but mpg123_info.framesize is weird. */ - uint32_t header = (uint32_t)read_32bitBE(stream->offset, stream->streamfile); - if (header & MPEG_SYNC_BITS) - data->current_frame_size = data->base_frame_size + (header & MPEG_PADDING_BIT ? 1 : 0); - else - data->current_frame_size = 0; /* todo skip invalid frame? */ + mpeg_frame_info info; - if (data->fsb_padding) //todo not always ok + /* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */ + if ( !mpeg_get_frame_info(streamfile, offset, &info) ) + goto fail; + + /* could mess some calcs */ + VGM_ASSERT(data->sample_rate_per_frame != info.sample_rate || data->samples_per_frame != info.frame_samples, + "MPEG: variable frame info found @ 0x%08lx", offset); + + /* new frame */ + data->current_frame_size = info.frame_size; + + /* get FSB padding for MPEG1/2 Layer III (MPEG1 Layer II doesn't use it, and Layer I doesn't seem to be supported) */ + if (data->fsb_padding && info.layer == 3) { data->current_padding = (data->current_frame_size % data->fsb_padding) ? data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0; + } } -} -static void update_base_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamFile, off_t start_offset, int fixed_frame_size, int current_frame_size, int fsb_padding) { - if (fixed_frame_size) { - data->fixed_frame_size = fixed_frame_size; - data->base_frame_size = data->fixed_frame_size; - data->current_frame_size = data->fixed_frame_size; - } else { - /* adjust sizes in the first frame */ - //todo: sometimes mpg123_info.framesize is not correct, manually calculate? (Xing headers?) - uint32_t header = (uint32_t)read_32bitBE(start_offset, streamFile); - if (header & MPEG_SYNC_BITS) - data->base_frame_size = current_frame_size - (header & MPEG_PADDING_BIT ? 1 : 0); - else - data->base_frame_size = 0; /* todo skip invalid frame? */ - data->current_frame_size = current_frame_size; - data->fsb_padding = fsb_padding; - if (data->fsb_padding) //todo not always ok - data->current_padding = (data->current_frame_size % data->fsb_padding) ? - data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0; - } + return 1; + +fail: + return 0; } /** @@ -599,7 +660,7 @@ void reset_mpeg(VGMSTREAM *vgmstream) { } void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { - /* won't work for fake or multistream MPEG */ + /* won't work for fake AHX MPEG */ off_t input_offset; mpeg_codec_data *data = vgmstream->codec_data; @@ -615,7 +676,7 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset; } /* manually add skip samples, since we don't really know the correct offset */ - //todo call decode with samples_to_do and fake header + data->samples_to_discard = num_sample; data->bytes_in_interleave_buffer = 0; data->bytes_used_in_interleave_buffer = 0; @@ -653,4 +714,94 @@ void mpeg_set_error_logging(mpeg_codec_data * data, int enable) { } } } + +/*****************/ +/* FRAME HELPERS */ +/*****************/ + +/** + * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow + * it's wrong at times (maybe because we use an ancient version) so here we do our thing. + */ +static int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info) { + /* index tables */ + static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; + static const int layers[4] = { -1,3,2,1 }; + static const int bit_rates[5][16] = { /* [version index ][bit rate index] (0=free, -1=bad) */ + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 Layer I */ + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 Layer II */ + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 Layer III */ + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2/2.5 Layer I */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, /* MPEG2/2.5 Layer II/III */ + }; + static const int sample_rates[4][4] = { /* [version][sample rate index] */ + { 44100, 48000, 32000, -1}, /* MPEG1 */ + { 22050, 24000, 16000, -1}, /* MPEG2 */ + { 11025, 12000, 8000, -1}, /* MPEG2.5 */ + }; + static const int channels[4] = { 2,2,2, 1 }; /* [channel] */ + static const int frame_samples[3][3] = { /* [version][layer] */ + { 384, 1152, 1152 }, /* MPEG1 */ + { 384, 1152, 576 }, /* MPEG2 */ + { 384, 1152, 576 } /* MPEG2.5 */ + }; + static const coding_t coding_types[3][3] = { /* [version][layer] */ + { coding_MPEG1_L1, coding_MPEG1_L2, coding_MPEG1_L3 }, + { coding_MPEG2_L1, coding_MPEG2_L2, coding_MPEG2_L3 }, + { coding_MPEG25_L1, coding_MPEG25_L2, coding_MPEG25_L3 }, + }; + + uint32_t header; + int idx, padding; + + + memset(info, 0, sizeof(*info)); + + header = read_32bitBE(offset, streamfile); + + if ((header >> 21) != 0x7FF) /* 31-21: sync */ + goto fail; + + info->version = versions[(header >> 19) & 0x3]; /* 20,19: version */ + if (info->version <= 0) goto fail; + + info->layer = layers[(header >> 17) & 0x3]; /* 18,17: layer */ + if (info->layer <= 0) goto fail; + + //crc = (header >> 16) & 0x1; /* 16: protected by crc? */ + + idx = (info->version==1 ? info->layer-1 : (3 + (info->layer==1 ? 0 : 1))); + info->bit_rate = bit_rates[idx][(header >> 12) & 0xf]; /* 15-12: bit rate */ + if (info->bit_rate <= 0) goto fail; + + info->sample_rate = sample_rates[info->version-1][(header >> 10) & 0x3]; /* 11-10: sampling rate */ + if (info->sample_rate <= 0) goto fail; + + padding = (header >> 9) & 0x1; /* 9: padding? */ + //private = (header >> 8) & 0x1; /* 8: private bit */ + + info->channels = channels[(header >> 6) & 0x3]; /* 7,6: channel mode */ + + //js_mode = (header >> 4) & 0x3; /* 5,4: mode extension for joint stereo */ + //copyright = (header >> 3) & 0x1; /* 3: copyrighted */ + //original = (header >> 2) & 0x1; /* 2: original */ + //emphasis = (header >> 0) & 0x3; /* 1,0: emphasis */ + + info->frame_samples = frame_samples[info->version-1][info->layer-1]; + + /* calculate frame length (from hcs's fsb_mpeg) */ + switch (info->frame_samples) { + case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; + case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; + case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; + } + + info->coding_type = coding_types[info->version-1][info->layer-1]; + + return 1; + +fail: + return 0; +} + #endif diff --git a/src/vgmstream.h b/src/vgmstream.h index a67af6d5..91696ce8 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -776,6 +776,8 @@ typedef struct { int channels_per_frame; size_t samples_per_frame; + size_t samples_to_discard; /* for interleaved looping */ + /* interleaved MPEG internals */ int interleaved; /* flag */ mpg123_handle **ms; /* array of MPEG streams */ @@ -789,8 +791,7 @@ typedef struct { /* messy stuff for padded FSB frames */ size_t fixed_frame_size; /* when given a fixed size (XVAG) */ - size_t base_frame_size; /* without header padding byte */ - size_t current_frame_size; /* with padding byte applied if needed */ + size_t current_frame_size; int fsb_padding; /* for FSBs that have extra garbage between frames */ size_t current_padding; /* padding needed for current frame size */ From 5763a1a229b741ed6059d97d30b2205f531a2608 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 13 Mar 2017 20:05:28 +0100 Subject: [PATCH 2/6] Use interleaved MPEG FSB decoder [Timeshift, DJ Hero] --- src/meta/fsb.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 710a5b77..26e0261f 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -279,34 +279,26 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { #if defined(VGM_USE_MPEG) mpeg_codec_data *mpeg_data = NULL; coding_t mpeg_coding_type; - -#if 0 int fsb_padding = 0; + + //VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */ + VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); + if (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED) fsb_padding = fsbh.numchannels > 2 ? 16 : 2; else if (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) fsb_padding = fsbh.numchannels > 2 ? 16 : 4; - else /* seems to be needed with no flag */ + else /* needed by multichannel with no flags */ fsb_padding = fsbh.numchannels > 2 ? 16 : 0; mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, 0, fsb_padding); if (!mpeg_data) goto fail; - - vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding; - if (vgmstream->channels > 2) vgmstream->loop_flag = 0;//todo not implemented yet -#endif - - VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n"); - VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); - - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; vgmstream->coding_type = mpeg_coding_type; vgmstream->layout_type = layout_mpeg; - mpeg_set_error_logging(mpeg_data, 0); + vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding; + //mpeg_set_error_logging(mpeg_data, 0); /* should not be needed anymore with the interleave decoder */ #elif defined(VGM_USE_FFMPEG) /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ From 0cf5d4fb006b1d8481c37f2710515eb0d64a977b Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 13 Mar 2017 20:08:35 +0100 Subject: [PATCH 3/6] Add FSB5 XMA; use interleaved MPEGs; fix mono MPEG total samples --- src/meta/fsb5.c | 111 +++++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index dfc6e617..1b68c105 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -2,12 +2,12 @@ #include "../coding/coding.h" #include "../util.h" -/* FSB5 header */ +/* FSB5 - FMOD Studio multiplatform format */ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t StartOffset = 0; off_t SampleHeaderStart = 0, DSPInfoStart = 0; - size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength; + size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0; uint32_t BaseSamples = 0, LoopStart = 0, LoopEnd = 0, NumSamples = 0; int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID; @@ -20,7 +20,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (read_32bitBE(0x00,streamFile) != 0x46534235) goto fail; /* "FSB5" */ - //v0 has extra flags at 0x1c and SampleHeaderStart = 0x40? + //v0 has extra flags at 0x1c and BaseHeaderLength = 0x40? if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */ TotalStreams = read_32bitLE(0x08,streamFile); @@ -35,11 +35,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if ((SampleHeaderLength + NameTableLength + SampleDataLength + 0x3C) != get_streamfile_size(streamFile)) goto fail; if (TargetStream == 0) TargetStream = 1; /* default to 1 */ - if (TargetStream > TotalStreams || TotalStreams < 0) goto fail; + if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail; - /* find target stream header and data offset and read all needed values for later use + /* find target stream header and data offset, and read all needed values for later use * (reads one by one as the size of a single stream header is variable) */ - for (i = 0; i < TotalStreams; i++) { + for (i = 1; i <= TotalStreams; i++) { off_t DataStart = 0; size_t StreamHeaderLength = 0; uint32_t SampleMode; @@ -48,8 +48,18 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { BaseSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile); StreamHeaderLength += 0x08; - /* get global offset */ - DataStart = (SampleMode >> 7) * 0x20; + /* get offset inside data section */ + DataStart = (SampleMode >> 7) * 0x20; /* bits 31..8 */ + + /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ + switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 */ + case 0: ChannelCount = 1; break; + case 1: ChannelCount = 2; break; + case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ + case 3: ChannelCount = 8; break;/* some IMA ADPCM */ + default: /* other values (ex. 10ch) are specified in the extra flags, using 0 here */ + goto fail; + } /* get sample rate */ switch ((SampleMode >> 1) & 0x0f) { /* bits 5..1 */ @@ -64,19 +74,9 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 8: SampleRate = 44100; break; case 9: SampleRate = 48000; break; case 10: SampleRate = 96000; break; //??? - default: + default: /* probably specified in the extra flags */ SampleRate = 44100; - break; /* probably specified in the extra flags */ - } - - /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ - switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 */ - case 0: ChannelCount = 1; break; - case 1: ChannelCount = 2; break; - case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ - case 3: ChannelCount = 8; break;/* some IMA ADPCM */ - default: /* other values (ex. 10ch) seem specified in the extra flags */ - goto fail; + break; } /* get extra flags */ @@ -102,14 +102,19 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (ExtraFlagSize > 0x04) /* probably no needed */ LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); - /* when start is 0 seems the song reoeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ + /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ LoopFlag = (LoopStart != 0x00); break; - case 0x07: /* DSP Info (Coeffs), only used if coding is DSP??? */ + case 0x06: /* XMA seek table */ + /* no need for it */ + break; + case 0x07: /* DSP Info (Coeffs) */ DSPInfoStart = ExtraFlagStart + 0x04; break; + case 0x0d: /* Unknown XMA value (size 4) */ + break; default: - VGM_LOG("FSB5: unknown extra flag %i at 0x%04x\n", ExtraFlagType, ExtraFlagStart); + VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x\n", ExtraFlagType, ExtraFlagStart); break; } @@ -119,17 +124,26 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } /* stream found */ - if (i == TotalStreams-1) { + if (i == TargetStream) { StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart; + + /* get stream size from next stream or datasize if there is only one */ + if (i == TotalStreams) { + StreamSize = SampleDataLength - DataStart; + } else { + uint32_t NextSampleMode = read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); + StreamSize = ((NextSampleMode >> 7) * 0x20) - DataStart; + } + break; } /* continue searching */ SampleHeaderStart += StreamHeaderLength; } - /* target stream not found*/ - if (!StartOffset) goto fail; + /* target stream not found*/ + if (!StartOffset || !StreamSize) goto fail; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(ChannelCount,LoopFlag); @@ -140,6 +154,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { vgmstream->sample_rate = SampleRate; vgmstream->num_streams = TotalStreams; vgmstream->meta_type = meta_FSB5; + NumSamples = BaseSamples / 4; /* not affected by channels */ switch (CodingID) { case 0x00: /* FMOD_SOUND_FORMAT_NONE */ @@ -149,7 +164,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { goto fail; case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */ - NumSamples = BaseSamples / 4; if (ChannelCount == 1) { vgmstream->layout_type = layout_none; } else { @@ -171,10 +185,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM */ if (ChannelCount == 1) { - NumSamples = BaseSamples / 4; vgmstream->layout_type = layout_none; } else { - NumSamples = BaseSamples / (2*ChannelCount); vgmstream->layout_type = layout_interleave_byte; vgmstream->interleave_block_size = 0x02; } @@ -184,7 +196,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ - NumSamples = BaseSamples / 4; vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_XBOX; if (vgmstream->channels > 2) /* multichannel FSB IMA (interleaved header) */ @@ -197,37 +208,41 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */ goto fail; - case 0x0A: /* FMOD_SOUND_FORMAT_XMA */ - goto fail; +#ifdef VGM_USE_FFMPEG + case 0x0A: {/* FMOD_SOUND_FORMAT_XMA */ + uint8_t buf[100]; + int bytes, block_size, block_count; + + block_size = 0x10000; /* XACT default */ + block_count = StreamSize / block_size + (StreamSize % block_size ? 1 : 0); + + bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; + + vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif #ifdef VGM_USE_MPEG case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG */ mpeg_codec_data *mpeg_data = NULL; coding_t mpeg_coding_type; + int fsb_padding = 0; - NumSamples = BaseSamples / 2 / ChannelCount; - -#if 0 - int fsb_padding = vgmstream->channels > 2 ? 16 : 0;//todo fix + fsb_padding = vgmstream->channels > 2 ? 16 : 4; /* observed default */ mpeg_data = init_mpeg_codec_data_interleaved(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels, 0, fsb_padding); if (!mpeg_data) goto fail; - - vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding; - if (vgmstream->channels > 2) vgmstream->loop_flag = 0;//todo not implemented yet -#endif - - if (vgmstream->channels > 2) - goto fail; /* no multichannel for now */ - - mpeg_data = init_mpeg_codec_data(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; vgmstream->coding_type = mpeg_coding_type; vgmstream->layout_type = layout_mpeg; - mpeg_set_error_logging(mpeg_data, 0); + vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding; + //mpeg_set_error_logging(mpeg_data, 0); /* should not be needed anymore with the interleave decoder */ break; } #endif From 0c55e49a634db1b3584f9f27a7e815af8ffc94b5 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 13 Mar 2017 20:43:47 +0100 Subject: [PATCH 4/6] More FSB5 flags comments --- src/meta/fsb5.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index 1b68c105..ab6887f1 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -105,16 +105,20 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ LoopFlag = (LoopStart != 0x00); break; + case 0x04: /* free comment, or maybe SFX info */ + break; case 0x06: /* XMA seek table */ /* no need for it */ break; case 0x07: /* DSP Info (Coeffs) */ DSPInfoStart = ExtraFlagStart + 0x04; break; + case 0x0b: /* Vorbis data */ + break; case 0x0d: /* Unknown XMA value (size 4) */ break; default: - VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x\n", ExtraFlagType, ExtraFlagStart); + VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); break; } From de523dc6a3be9fd2944454e60af3587c63af35d4 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 13 Mar 2017 21:14:14 +0100 Subject: [PATCH 5/6] Do some cleanup while we are testing BFSTMs --- src/meta/bcstm.c | 53 ++++++++++++++---------------------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/src/meta/bcstm.c b/src/meta/bcstm.c index 54307f73..66fdab2f 100644 --- a/src/meta/bcstm.c +++ b/src/meta/bcstm.c @@ -1,30 +1,27 @@ #include "meta.h" #include "../util.h" +/* BCSTM - Nintendo 3DS format */ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - coding_t coding_type; off_t info_offset = 0, seek_offset = 0, data_offset = 0; uint16_t temp_id; int codec_number; - int channel_count; - int loop_flag; + int channel_count, loop_flag; int i, ima = 0; off_t start_offset; int section_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("bcstm", filename_extension(filename))) + if ( !check_extensions(streamFile,"bcstm") ) goto fail; - /* check header */ if ((uint32_t)read_32bitBE(0, streamFile) != 0x4353544D) /* "CSTM" */ goto fail; + if ((uint16_t)read_16bitLE(4, streamFile) != 0xFEFF) goto fail; @@ -57,11 +54,12 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { } } - - + if (info_offset == 0) goto fail; + if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ + goto fail; + /* check type details */ - if (info_offset == 0) goto fail; codec_number = read_8bit(info_offset + 0x20, streamFile); loop_flag = read_8bit(info_offset + 0x21, streamFile); channel_count = read_8bit(info_offset + 0x22, streamFile); @@ -89,13 +87,12 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { if (channel_count < 1) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ vgmstream->num_samples = read_32bitLE(info_offset + 0x2c, streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitLE(info_offset + 0x24, streamFile); + vgmstream->sample_rate = read_32bitLE(info_offset + 0x24, streamFile); /* channels and loop flag are set by allocate_vgmstream */ if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting { @@ -166,34 +163,14 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { start_offset = data_offset + 0x20; } - - /* open the file for reading by each channel */ - { - int i; - for (i = 0; ilayout_type == layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - vgmstream->interleave_block_size); - else if (vgmstream->layout_type == layout_interleave) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - 0x1000); + /* open the file for reading by each channel */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; - if (!vgmstream->ch[i].streamfile) goto fail; + return vgmstream; - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + i*vgmstream->interleave_block_size; - } - } - - return vgmstream; - - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; + close_vgmstream(vgmstream); + return NULL; } From d13d03e5738a23e6871e4b8eed054ea461309189 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 13 Mar 2017 21:19:38 +0100 Subject: [PATCH 6/6] Add little endian BFSTM [Blaster Master Zero (3DS)] --- src/meta/bfstm.c | 96 ++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/src/meta/bfstm.c b/src/meta/bfstm.c index 449abea8..2b93a08f 100644 --- a/src/meta/bfstm.c +++ b/src/meta/bfstm.c @@ -2,17 +2,17 @@ #include "../util.h" #include "../stack_alloc.h" +/* BFSTM - Nintendo Wii U format */ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - coding_t coding_type; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; off_t info_offset = 0, seek_offset = 0, data_offset = 0; uint16_t temp_id; int codec_number; - int channel_count; - int loop_flag; + int channel_count, loop_flag; int i, j; int ima = 0; off_t start_offset; @@ -20,47 +20,52 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { int section_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("bfstm", filename_extension(filename))) + if ( !check_extensions(streamFile,"bfstm") ) goto fail; - /* check header */ if ((uint32_t)read_32bitBE(0, streamFile) != 0x4653544D) /* "FSTM" */ goto fail; - if ((uint16_t)read_16bitBE(4, streamFile) != 0xFEFF) - goto fail; - section_count = read_16bitBE(0x10, streamFile); + if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* Blaster Master Zero 3DS */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } else { + goto fail; + } + + section_count = read_16bit(0x10, streamFile); for (i = 0; i < section_count; i++) { - temp_id = read_16bitBE(0x14 + i * 0xc, streamFile); + temp_id = read_16bit(0x14 + i * 0xc, streamFile); switch(temp_id) { case 0x4000: - info_offset = read_32bitBE(0x18 + i * 0xc, streamFile); - /* size_t info_size = read_32bitBE(0x1c + i * 0xc, streamFile); */ + info_offset = read_32bit(0x18 + i * 0xc, streamFile); + /* size_t info_size = read_32bit(0x1c + i * 0xc, streamFile); */ break; case 0x4001: - seek_offset = read_32bitBE(0x18 + i * 0xc, streamFile); - /* size_t seek_size = read_32bitBE(0x1c + i * 0xc, streamFile); */ + seek_offset = read_32bit(0x18 + i * 0xc, streamFile); + /* size_t seek_size = read_32bit(0x1c + i * 0xc, streamFile); */ break; case 0x4002: - data_offset = read_32bitBE(0x18 + i * 0xc, streamFile); - /* size_t data_size = read_32bitBE(0x1c + i * 0xc, streamFile); */ + data_offset = read_32bit(0x18 + i * 0xc, streamFile); + /* size_t data_size = read_32bit(0x1c + i * 0xc, streamFile); */ break; case 0x4003: - /* off_t regn_offset = read_32bitBE(0x18 + i * 0xc, streamFile); */ - /* size_t regn_size = read_32bitBE(0x1c + i * 0xc, streamFile); */ + /* off_t regn_offset = read_32bit(0x18 + i * 0xc, streamFile); */ + /* size_t regn_size = read_32bit(0x1c + i * 0xc, streamFile); */ break; case 0x4004: - /* off_t pdat_offset = read_32bitBE(0x18 + i * 0xc, streamFile); */ - /* size_t pdat_size = read_32bitBE(0x1c + i * 0xc, streamFile); */ + /* off_t pdat_offset = read_32bit(0x18 + i * 0xc, streamFile); */ + /* size_t pdat_size = read_32bit(0x1c + i * 0xc, streamFile); */ break; default: break; } } - if (info_offset == 0) goto fail; if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ goto fail; @@ -88,17 +93,16 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { if (channel_count < 1) goto fail; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(info_offset + 0x2c, streamFile); - vgmstream->sample_rate = (uint16_t)read_16bitBE(info_offset + 0x26, streamFile); + vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile); + vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile); /* channels and loop flag are set by allocate_vgmstream */ if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting { - vgmstream->loop_start_sample = read_32bitBE(info_offset + 0x28, streamFile); + vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); if (vgmstream->loop_start_sample > 10000) { vgmstream->loop_start_sample -= 5000; @@ -109,7 +113,7 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { } else { - vgmstream->loop_start_sample = read_32bitBE(info_offset + 0x28, streamFile); + vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; } @@ -128,25 +132,25 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { if (ima) vgmstream->interleave_block_size = 0x200; else { - vgmstream->interleave_block_size = read_32bitBE(info_offset + 0x34, streamFile); - vgmstream->interleave_smallblock_size = read_32bitBE(info_offset + 0x44, streamFile); + vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); + vgmstream->interleave_smallblock_size = read_32bit(info_offset + 0x44, streamFile); } if (vgmstream->coding_type == coding_NGC_DSP) { off_t coeff_ptr_table; VARDECL(off_t, coef_offset); ALLOC(coef_offset, channel_count, off_t); - coeff_ptr_table = read_32bitBE(info_offset + 0x1c, streamFile) + info_offset + 8; // Getting pointer for coefficient pointer table + coeff_ptr_table = read_32bit(info_offset + 0x1c, streamFile) + info_offset + 8; // Getting pointer for coefficient pointer table for (i = 0; i < channel_count; i++) { - tempoffset1 = read_32bitBE(coeff_ptr_table + 8 + i * 8, streamFile); + tempoffset1 = read_32bit(coeff_ptr_table + 8 + i * 8, streamFile); coef_offset[i] = tempoffset1 + coeff_ptr_table; - coef_offset[i] += read_32bitBE(coef_offset[i] + 4, streamFile); + coef_offset[i] += read_32bit(coef_offset[i] + 4, streamFile); } for (j = 0; jchannels; j++) { for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_offset[j] + i * 2, streamFile); + vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset[j] + i * 2, streamFile); } } } @@ -160,33 +164,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { } - /* open the file for reading by each channel */ - { - int i; - for (i = 0; ilayout_type == layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - vgmstream->interleave_block_size); - else if (vgmstream->layout_type == layout_interleave) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - 0x1000); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + i*vgmstream->interleave_block_size; - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; }