From a142a065760cd51659e049e68428a1c0a9a4dbe8 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 25 Mar 2017 19:02:05 -0700 Subject: [PATCH] Updated VGMStream to r1050-386-g886a25c. --- .../vgmstream/src/coding/Makefile.unix.am | 5 +- .../vgmstream/vgmstream/src/coding/coding.h | 11 +- .../vgmstream/src/coding/fsb_vorbis_decoder.c | 334 ++++++++++++++++++ Frameworks/vgmstream/vgmstream/src/formats.c | 3 +- Frameworks/vgmstream/vgmstream/src/meta/fsb.c | 15 +- .../vgmstream/vgmstream/src/meta/fsb5.c | 70 ++-- .../vgmstream/vgmstream/src/meta/genh.c | 2 +- .../vgmstream/vgmstream/src/meta/ps3_msf.c | 10 +- .../vgmstream/vgmstream/src/vgmstream.c | 20 ++ .../vgmstream/vgmstream/src/vgmstream.h | 16 + 10 files changed, 446 insertions(+), 40 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/fsb_vorbis_decoder.c diff --git a/Frameworks/vgmstream/vgmstream/src/coding/Makefile.unix.am b/Frameworks/vgmstream/vgmstream/src/coding/Makefile.unix.am index 12380feb0..ac88119b5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/Makefile.unix.am +++ b/Frameworks/vgmstream/vgmstream/src/coding/Makefile.unix.am @@ -29,5 +29,8 @@ libcoding_la_SOURCES += SASSC_decoder.c libcoding_la_SOURCES += g7221_decoder.c libcoding_la_SOURCES += lsf_decoder.c libcoding_la_SOURCES += mtaf_decoder.c +libcoding_la_SOURCES += g719_decoder.c +libcoding_la_SOURCES += hca_decoder.c +libcoding_la_SOURCES += fsb_vorbis_decoder.c -EXTRA_DIST = coding.h g72x_state.h +EXTRA_DIST = coding.h g72x_state.h clHCA.h diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 458b157ad..97529a862 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -112,9 +112,18 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, /* hca_decoder */ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); -/* ogg_vorbis_decoder */ + #ifdef VGM_USE_VORBIS +/* ogg_vorbis_decoder */ void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); + +/* fsb_vorbis_decoder */ +vorbis_codec_data * init_fsb_vorbis_codec_data(STREAMFILE *streamfile, off_t start_offset, int channels, int sample_rate, uint32_t setup_id); +void decode_fsb_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); + +void free_fsb_vorbis(vorbis_codec_data *data); +void reset_fsb_vorbis(VGMSTREAM *vgmstream); +void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); #endif /* mpeg_decoder */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/fsb_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/fsb_vorbis_decoder.c new file mode 100644 index 000000000..5add2e07c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/fsb_vorbis_decoder.c @@ -0,0 +1,334 @@ +#include "coding.h" + +#ifdef VGM_USE_VORBIS +#include + +#define FSB_VORBIS_ON 1 //todo recompile libvorbis with vorbis_* and remove + +#define FSB_VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ + +#if FSB_VORBIS_ON +static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm); +static int vorbis_make_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); +static int vorbis_make_header_comment(uint8_t * buf, size_t bufsize); +static int vorbis_make_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); +#endif + +/** + * Inits a raw FSB vorbis stream. + * + * Vorbis packets are stored in .ogg, which is divided into ogg pages/packets, and the first packets contain necessary + * vorbis setup. For raw vorbis the setup is stored it elsewhere (i.e.- in the .exe), presumably to shave off some kb + * per stream and codec startup time. We'll read the external setup and raw data and decode it with libvorbis. + * + * FSB references the external setup with the setup_id, and the raw vorbis packets have mini headers with the block size. + * + * Format info and references from python-fsb5 (https://github.com/HearthSim/python-fsb5) and + * fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor). + * Also from the official docs (https://www.xiph.org/vorbis/doc/libvorbis/overview.html). + */ +vorbis_codec_data * init_fsb_vorbis_codec_data(STREAMFILE *streamfile, off_t start_offset, int channels, int sample_rate, uint32_t setup_id) { +#if FSB_VORBIS_ON + vorbis_codec_data * data = NULL; + + /* init stuff */ + data = calloc(1,sizeof(vorbis_codec_data)); + if (!data) goto fail; + + data->buffer_size = FSB_VORBIS_DEFAULT_BUFFER_SIZE; + data->buffer = calloc(sizeof(uint8_t), data->buffer_size); + if (!data->buffer) goto fail; + + + /* init vorbis stream state, using 3 fake Ogg setup packets (info, comments, setup/codebooks) + * libvorbis expects parsed Ogg pages, but we'll fake them with our raw data instead */ + vorbis_info_init(&data->vi); + vorbis_comment_init(&data->vc); + + data->op.packet = data->buffer; + data->op.b_o_s = 1; /* fake headers start */ + + data->op.bytes = vorbis_make_header_identification(data->buffer, data->buffer_size, channels, sample_rate, 256, 2048); /* FSB default block sizes */ + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + data->op.bytes = vorbis_make_header_comment(data->buffer, data->buffer_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + + data->op.bytes = vorbis_make_header_setup(data->buffer, data->buffer_size, setup_id, streamfile); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + + data->op.b_o_s = 0; /* end of fake headers */ + + /* init vorbis global and block state */ + if (vorbis_synthesis_init(&data->vd,&data->vi) != 0) goto fail; + if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail; + + + return data; + +fail: + free_fsb_vorbis(data); +#endif + return NULL; +} + +/** + * Decodes raw FSB vorbis + */ +void decode_fsb_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { +#if FSB_VORBIS_ON + VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; + vorbis_codec_data * data = vgmstream->codec_data; + size_t stream_size = get_streamfile_size(stream->streamfile); + //data->op.packet = data->buffer;/* implicit from init */ + int samples_done = 0; + + while (samples_done < samples_to_do) { + + /* extra EOF check for edge cases */ + if (stream->offset > stream_size) { + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample)); + break; + } + + + if (data->samples_full) { /* read more samples */ + int samples_to_get; + float **pcm; + + /* get PCM samples from libvorbis buffers */ + samples_to_get = vorbis_synthesis_pcmout(&data->vd, &pcm); + if (!samples_to_get) { + data->samples_full = 0; /* request more if empty*/ + continue; + } + + if (data->samples_to_discard) { + /* discard samples for looping */ + if (samples_to_get > data->samples_to_discard) + samples_to_get = data->samples_to_discard; + data->samples_to_discard -= samples_to_get; + } + else { + /* get max samples and convert from Vorbis float pcm to 16bit pcm */ + if (samples_to_get > samples_to_do - samples_done) + samples_to_get = samples_to_do - samples_done; + samples_done += samples_to_get; + pcm_convert_float_to_16(data, outbuf + samples_done * channels, samples_to_get, pcm); + } + + /* mark consumed samples from the buffer + * (non-consumed samples are returned in next vorbis_synthesis_pcmout calls) */ + vorbis_synthesis_read(&data->vd, samples_to_get); + } + else { /* read more data */ + int rc; + + /* this is not actually needed, but feels nicer */ + data->op.granulepos += samples_to_do; + data->op.packetno++; + + /* get next packet size from the FSB 16b header (doesn't count this 16b) */ + data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile); + stream->offset += 2; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { + goto decode_fail; /* eof or FSB end padding */ + } + + /* read raw block */ + if (read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile) != data->op.bytes) { + goto decode_fail; /* wrong packet? */ + } + stream->offset += data->op.bytes; + + /* parse the fake ogg packet into a logical vorbis block */ + rc = vorbis_synthesis(&data->vb,&data->op); + if (rc == OV_ENOTAUDIO) { + VGM_LOG("vorbis_synthesis: not audio packet @ %lx\n",stream->offset); getchar(); + continue; /* not tested */ + } else if (rc != 0) { + goto decode_fail; + } + + /* finally decode the logical block into samples */ + rc = vorbis_synthesis_blockin(&data->vd,&data->vb); + if (rc != 0) { + goto decode_fail; /* ? */ + } + + data->samples_full = 1; + } + } + + return; + +decode_fail: + /* on error just put some 0 samples */ + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample)); +#endif +} + +#if FSB_VORBIS_ON + +static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm) { + /* mostly from Xiph's decoder_example.c */ + int i,j; + + /* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc) + * to 16 bit signed PCM ints (host order) and interleave + fix clipping */ + for (i = 0; i < data->vi.channels; i++) { + sample *ptr = outbuf + i; + float *mono = pcm[i]; + for (j = 0; j < samples_to_do; j++) { + int val = floor(mono[j] * 32767.f + .5f); + if (val > 32767) val = 32767; + if (val < -32768) val = -32768; + + *ptr = val; + ptr += data->vi.channels; + } + } +} + +static int vorbis_make_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) { + int bytes = 0x1e; + uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1; + + if (bytes > bufsize) return 0; + + /* guetto log2 for allowed blocksizes (2-exp), could be improved */ + switch(blocksize_long) { + case 64: exp_blocksize_0 = 6; break; + case 128: exp_blocksize_0 = 7; break; + case 256: exp_blocksize_0 = 8; break; + case 512: exp_blocksize_0 = 9; break; + case 1024: exp_blocksize_0 = 10; break; + case 2048: exp_blocksize_0 = 11; break; + case 4096: exp_blocksize_0 = 12; break; + case 8192: exp_blocksize_0 = 13; break; + default: return 0; + } + switch(blocksize_short) { + case 64: exp_blocksize_1 = 6; break; + case 128: exp_blocksize_1 = 7; break; + case 256: exp_blocksize_1 = 8; break; + case 512: exp_blocksize_1 = 9; break; + case 1024: exp_blocksize_1 = 10; break; + case 2048: exp_blocksize_1 = 11; break; + case 4096: exp_blocksize_1 = 12; break; + case 8192: exp_blocksize_1 = 13; break; + default: return 0; + } + blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1); + + put_8bit (buf+0x00, 0x01); /* packet_type (id) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */ + put_8bit (buf+0x0b, channels); /* audio_channels */ + put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */ + put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */ + put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */ + put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */ + put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */ + put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static int vorbis_make_header_comment(uint8_t * buf, size_t bufsize) { + int bytes = 0x19; + + if (bytes > bufsize) return 0; + + put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + put_32bitLE(buf+0x07, 0x09); /* vendor_length */ + memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ + put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ + put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ + + return bytes; +} + +static int vorbis_make_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + STREAMFILE * streamFileSetup = NULL; + size_t bytes = 0; + + + streamFile->get_name(streamFile,pathname,sizeof(pathname)); + + /* try to get setup packet from external file first, using "(dir/).vorbis_{setup_id}" */ + path = strrchr(pathname,DIR_SEPARATOR); + if (path) { + *(path+1) = '\0'; + } else { + pathname[0] = '\0'; + } + snprintf(setupname,PATH_LIMIT,"%s.vorbis_%08x", pathname, setup_id); + + streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamFileSetup) { + /* file found, get contents into the buffer */ + bytes = streamFileSetup->get_size(streamFileSetup); + if (bytes > bufsize) goto fail; + + if (read_streamfile(buf, 0, bufsize, streamFileSetup) != bytes) + goto fail; + + streamFileSetup->close(streamFileSetup); + } + else { + /* check the index list */ + VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); + goto fail; + } + + + return bytes; + +fail: + if (streamFileSetup) streamFileSetup->close(streamFileSetup); + return 0; +} + +#endif + + +void free_fsb_vorbis(vorbis_codec_data * data) { +#if FSB_VORBIS_ON + if (!data) + return; + + /* internal decoder cleanp */ + vorbis_info_clear(&data->vi); + vorbis_comment_clear(&data->vc); + vorbis_dsp_clear(&data->vd); + + free(data->buffer); + free(data); +#endif +} + +void reset_fsb_vorbis(VGMSTREAM *vgmstream) { + seek_fsb_vorbis(vgmstream, 0); +} + +void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { +#if FSB_VORBIS_ON + vorbis_codec_data *data = vgmstream->codec_data; + + /* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead. + * To avoid having to parse different formats we'll just discard until the expected sample */ + vorbis_synthesis_restart(&data->vd); + data->samples_to_discard = num_sample; + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; +#endif +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 4b53cc4b7..bbb1b51b8 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -434,7 +434,8 @@ static const coding_info coding_info_list[] = { {coding_LSF, "lsf 4-bit ADPCM"}, {coding_MTAF, "Konami MTAF 4-bit ADPCM"}, #ifdef VGM_USE_VORBIS - {coding_ogg_vorbis, "Vorbis"}, + {coding_ogg_vorbis, "Ogg Vorbis"}, + {coding_fsb_vorbis, "FSB Vorbis"}, #endif #ifdef VGM_USE_MPEG {coding_fake_MPEG2_L2, "MPEG-2 Layer II Audio"}, diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 8189e27ae..2396ef77f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -243,12 +243,13 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { } } - /* XOR encryption for some FSB4 */ +#if 0 + /* XOR encryption for some FSB4, though the flag is only seen after decrypting */ if (fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED) { VGM_LOG("FSB ENCRYPTED found\n"); goto fail; } -#if 0 + /* sometimes there is garbage at the end or missing bytes due to improper demuxing */ if (fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset) { VGM_LOG("FSB wrong head/datasize found\n"); @@ -360,14 +361,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { vgmstream->interleave_block_size = 0x2; dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e); } - else if (fsbh.mode & FSOUND_OGG) { - /* FSB4: ? (possibly FMOD's custom ogg) */ - - VGM_LOG("FSB4 FSOUND_OGG found\n"); - goto fail; - } - else if (fsbh.mode & FSOUND_CELT) { - /* FSB4: ? (The Witcher 2?) */ + else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag) */ + /* FSB4: War Thunder (PC), The Witcher 2 (PC) */ VGM_LOG("FSB4 FSOUND_CELT found\n"); goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index ab6887f1c..f1cf586dc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -9,9 +9,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { off_t SampleHeaderStart = 0, DSPInfoStart = 0; size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0; - uint32_t BaseSamples = 0, LoopStart = 0, LoopEnd = 0, NumSamples = 0; + uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0; int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID; int TotalStreams, TargetStream = 0; + uint32_t VorbisSetupId = 0; int i; @@ -42,17 +43,23 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { for (i = 1; i <= TotalStreams; i++) { off_t DataStart = 0; size_t StreamHeaderLength = 0; - uint32_t SampleMode; + uint32_t SampleMode, SampleMode2; - SampleMode = read_32bitLE(SampleHeaderStart+0x00,streamFile); - BaseSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile); + + /* seems ok but could use some testing against FMOD's SDK */ + SampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile); + SampleMode2 = (uint32_t)read_32bitLE(SampleHeaderStart+0x04,streamFile); StreamHeaderLength += 0x08; + /* get samples */ + NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits 31..2 (30) */ + // bits 1..0 part of DataStart? + /* get offset inside data section */ - DataStart = (SampleMode >> 7) * 0x20; /* bits 31..8 */ + DataStart = ((SampleMode >> 7) & 0x0FFFFFF) << 5; /* bits 31..8 (24) * 0x20 */ /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ - switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 */ + switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 (2) */ case 0: ChannelCount = 1; break; case 1: ChannelCount = 2; break; case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ @@ -62,7 +69,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } /* get sample rate */ - switch ((SampleMode >> 1) & 0x0f) { /* bits 5..1 */ + switch ((SampleMode >> 1) & 0x0f) { /* bits 5..1 (4) */ case 0: SampleRate = 4000; break; //??? case 1: SampleRate = 8000; break; case 2: SampleRate = 11000; break; @@ -80,15 +87,15 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { } /* get extra flags */ - if (SampleMode&0x01) { /* bit 0 */ + if (SampleMode & 0x01) { /* bit 0 (1) */ uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd; ExtraFlagStart = SampleHeaderStart+0x08; do { ExtraFlag = read_32bitLE(ExtraFlagStart,streamFile); - ExtraFlagType = (ExtraFlag>>25)&0x7F; - ExtraFlagSize = (ExtraFlag>>1)&0xFFFFFF; - ExtraFlagEnd = (ExtraFlag&0x01); + ExtraFlagType = (ExtraFlag >> 25) & 0x7F; /* bits 32..26 (7) */ + ExtraFlagSize = (ExtraFlag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/ + ExtraFlagEnd = (ExtraFlag & 0x01); /* bit 0 (1) */ switch(ExtraFlagType) { case 0x01: /* Channel Info */ @@ -113,10 +120,21 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { case 0x07: /* DSP Info (Coeffs) */ DSPInfoStart = ExtraFlagStart + 0x04; break; + case 0x09: /* ATRAC9 data */ + break; + case 0x0a: /* XWMA data */ + break; case 0x0b: /* Vorbis data */ + VorbisSetupId = (uint32_t)read_32bitLE(ExtraFlagStart+0x04,streamFile); /* crc32? */ + /* seek table format: + * 0x08: table_size (total_entries = seek_table_size / (4+4)), not counting this value; can be 0 + * 0x0C: sample number (only some samples are saved in the table) + * 0x10: offset within data, pointing to a FSB vorbis block (with the 16b block size header) + * (xN entries) + */ break; - case 0x0d: /* Unknown XMA value (size 4) */ - break; + //case 0x0d: /* Unknown value (32b), found in some XMA2 and Vorbis */ + // break; default: VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize); break; @@ -135,8 +153,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { if (i == TotalStreams) { StreamSize = SampleDataLength - DataStart; } else { - uint32_t NextSampleMode = read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); - StreamSize = ((NextSampleMode >> 7) * 0x20) - DataStart; + uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile); + StreamSize = (((NextSampleMode >> 7) & 0x00FFFFFF) << 5) - DataStart; } break; @@ -149,16 +167,15 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { /* target stream not found*/ if (!StartOffset || !StreamSize) goto fail; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(ChannelCount,LoopFlag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->channels = ChannelCount; 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 */ @@ -250,16 +267,27 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { break; } #endif - case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ + case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ goto fail; - case 0x0D: /* FMOD_SOUND_FORMAT_AT9 */ + case 0x0D: /* FMOD_SOUND_FORMAT_AT9 */ goto fail; - case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ + case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ goto fail; - case 0x0F: /* FMOD_SOUND_FORMAT_VORBIS */ +#ifdef VGM_USE_VORBIS + case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS */ + vgmstream->codec_data = init_fsb_vorbis_codec_data(streamFile, StartOffset, vgmstream->channels, vgmstream->sample_rate,VorbisSetupId); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_fsb_vorbis; + vgmstream->layout_type = layout_none; + + break; + } +#endif + + case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */ goto fail; default: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index 538491c2a..6285a64a5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -304,7 +304,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; /* force encoder delay */ - if (skip_samples_mode) { + if (skip_samples_mode && skip_samples >= 0) { ffmpeg_set_skip_samples(ffmpeg_data, skip_samples); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c index 2df651996..5a5ceb2ec 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c @@ -21,7 +21,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { start_offset = header_offset+0x40; /* MSF header is always 0x40 */ /* check header "MSF" + version-char - * usually "MSF\0\1", "MSF\0\2", "MSF5"(\3\5), "MSFC"(\4\3) (latest/common version) */ + * usually "MSF\0\1", "MSF\0\2", "MSF0"(\3\0), "MSF5"(\3\5), "MSFC"(\4\3) (last/common version) */ id = read_32bitBE(header_offset+0x00,streamFile); if ((id & 0xffffff00) != 0x4D534600) goto fail; @@ -33,15 +33,15 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { data_size = get_streamfile_size(streamFile) - start_offset; /* byte flags, not in MSFv1 or v2 - * 0x01/02/04/08: loop marker 0/1/2/3 (requires flag 0x10) + * 0x01/02/04/08: loop marker 0/1/2/3 * 0x10: "resample" loop option (may be active with no 0x01 flag set) * 0x20: VBR MP3 * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) * 0x80+: (none/reserved) */ flags = read_32bitBE(header_offset+0x14,streamFile); - /* sometimes loop_start/end is set but not flag 0x01, but from tests it only loops with 0x01 - * Malicious PS3 uses flag 0x2 instead */ - loop_flag = flags != 0xffffffff && (((flags & 0x10) && (flags & 0x01)) || (flags & 0x02)); + /* sometimes loop_start/end is set with flag 0x10, but from tests it only loops if 0x01/02 is set + * 0x10 often goes with 0x01 but not always (Castlevania HoD); Malicious PS3 uses flag 0x2 instead */ + loop_flag = flags != 0xffffffff && ((flags & 0x01) || (flags & 0x02)); /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 9d4af2dbb..aace4f61e 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -471,6 +471,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { ov_pcm_seek(ogg_vorbis_file, 0); } + + if (vgmstream->coding_type==coding_fsb_vorbis) { + reset_fsb_vorbis(vgmstream); + } #endif if (vgmstream->coding_type==coding_CRI_HCA) { hca_codec_data *data = vgmstream->codec_data; @@ -669,6 +673,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) { vgmstream->codec_data = NULL; } } + + if (vgmstream->coding_type==coding_fsb_vorbis) { + free_fsb_vorbis(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } #endif if (vgmstream->coding_type==coding_CRI_HCA) { @@ -989,6 +998,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM8_U_int: #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: + case coding_fsb_vorbis: #endif #ifdef VGM_USE_MPEG case coding_fake_MPEG2_L2: @@ -1467,6 +1477,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to buffer+samples_written*vgmstream->channels,samples_to_do, vgmstream->channels); break; + + case coding_fsb_vorbis: + decode_fsb_vorbis(vgmstream, + buffer+samples_written*vgmstream->channels,samples_to_do, + vgmstream->channels); + break; #endif case coding_CRI_HCA: decode_hca(vgmstream->codec_data, @@ -1777,6 +1793,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { ov_pcm_seek_lap(ogg_vorbis_file, vgmstream->loop_sample); } + + if (vgmstream->coding_type==coding_fsb_vorbis) { + seek_fsb_vorbis(vgmstream, vgmstream->loop_start_sample); + } #endif #ifdef VGM_USE_FFMPEG diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 519776cd4..8345cb149 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -158,6 +158,7 @@ typedef enum { #ifdef VGM_USE_VORBIS coding_ogg_vorbis, /* Xiph Vorbis (MDCT-based) */ + coding_fsb_vorbis, /* FMOD's Vorbis without Ogg layer */ #endif #ifdef VGM_USE_MPEG @@ -753,6 +754,7 @@ typedef struct { } VGMSTREAM; #ifdef VGM_USE_VORBIS +/* Ogg with Vorbis */ typedef struct { STREAMFILE *streamfile; ogg_int64_t offset; @@ -772,6 +774,20 @@ typedef struct { ogg_vorbis_streamfile ov_streamfile; } ogg_vorbis_codec_data; + +/* any raw Vorbis without Ogg layer */ +typedef struct { + vorbis_info vi; /* stream settings */ + vorbis_comment vc; /* stream comments */ + vorbis_dsp_state vd; /* decoder global state */ + vorbis_block vb; /* decoder local state */ + ogg_packet op; /* fake packet for internal use */ + + uint8_t * buffer; /* internal raw data buffer */ + size_t buffer_size; + size_t samples_to_discard; /* for looping purposes */ + int samples_full; /* flag, samples available in vorbis buffers */ +} vorbis_codec_data; #endif #ifdef VGM_USE_MPEG