mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-16 11:33:20 +01:00
Merge pull request #77 from bnnm/fixes-fsb-vorbis-dev
Fixes, FSB vorbis (dev)
This commit is contained in:
commit
b633da468c
@ -26,7 +26,8 @@ CODING_OBJS=coding/adx_decoder.o \
|
||||
coding/g719_decoder.o \
|
||||
coding/hca_decoder.o \
|
||||
coding/ffmpeg_decoder.o \
|
||||
coding/ffmpeg_decoder_utils.o
|
||||
coding/ffmpeg_decoder_utils.o \
|
||||
coding/fsb_vorbis_decoder.o
|
||||
|
||||
LAYOUT_OBJS=layout/ast_blocked.o \
|
||||
layout/blocked.o \
|
||||
|
@ -31,5 +31,6 @@ 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 clHCA.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 */
|
||||
|
334
src/coding/fsb_vorbis_decoder.c
Normal file
334
src/coding/fsb_vorbis_decoder.c
Normal file
@ -0,0 +1,334 @@
|
||||
#include "coding.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#define FSB_VORBIS_ON 0 //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
|
@ -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"},
|
||||
|
@ -1269,6 +1269,10 @@
|
||||
<File
|
||||
RelativePath=".\coding\ffmpeg_decoder_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\fsb_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\g719_decoder.c"
|
||||
@ -1330,10 +1334,10 @@
|
||||
RelativePath=".\coding\nwa_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ogg_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\ogg_vorbis_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\pcm_decoder.c"
|
||||
>
|
||||
|
@ -398,6 +398,7 @@
|
||||
<ClCompile Include="coding\adx_decoder.c" />
|
||||
<ClCompile Include="coding\aica_decoder.c" />
|
||||
<ClCompile Include="coding\eaxa_decoder.c" />
|
||||
<ClCompile Include="coding\fsb_vorbis_decoder.c" />
|
||||
<ClCompile Include="coding\g719_decoder.c" />
|
||||
<ClCompile Include="coding\g721_decoder.c" />
|
||||
<ClCompile Include="coding\g7221_decoder.c" />
|
||||
|
@ -742,6 +742,9 @@
|
||||
<ClCompile Include="coding\eaxa_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\fsb_vorbis_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\g721_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -149,6 +149,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
|
||||
@ -744,6 +745,7 @@ typedef struct {
|
||||
} VGMSTREAM;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
/* Ogg with Vorbis */
|
||||
typedef struct {
|
||||
STREAMFILE *streamfile;
|
||||
ogg_int64_t offset;
|
||||
@ -763,6 +765,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
|
||||
|
Loading…
Reference in New Issue
Block a user