diff --git a/src/coding/coding.h b/src/coding/coding.h index 1e871ecf..2c85347f 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -137,34 +137,18 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream); void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); void free_ogg_vorbis(ogg_vorbis_codec_data *data); -/* 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 reset_fsb_vorbis(VGMSTREAM *vgmstream); -void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); -void free_fsb_vorbis(vorbis_codec_data *data); - -/* wwise_vorbis_decoder */ -vorbis_codec_data * init_wwise_vorbis_codec_data(STREAMFILE *streamfile, off_t start_offset, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp, - wwise_setup_type setup_type, wwise_header_type header_type, wwise_packet_type packet_type, int big_endian); -void decode_wwise_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); -void reset_wwise_vorbis(VGMSTREAM *vgmstream); -void seek_wwise_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); -void free_wwise_vorbis(vorbis_codec_data *data); - -/* ogl_vorbis_decoder */ -vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t start_offset, off_t * data_start_offset); -void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); -void reset_ogl_vorbis(VGMSTREAM *vgmstream); -void seek_ogl_vorbis(VGMSTREAM *vgmstream, int32_t num_sample); -void free_ogl_vorbis(vorbis_codec_data *data); +/* vorbis_custom_decoder */ +vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config); +void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); +void reset_vorbis_custom(VGMSTREAM *vgmstream); +void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample); +void free_vorbis_custom(vorbis_custom_codec_data *data); #endif #ifdef VGM_USE_MPEG /* mpeg_decoder */ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels); -mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, mpeg_interleave_type interleave_type, uint32_t interleave_value); -mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count); +mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config); void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do); diff --git a/src/coding/fsb_vorbis_decoder.c b/src/coding/fsb_vorbis_decoder.c deleted file mode 100644 index 100b0459..00000000 --- a/src/coding/fsb_vorbis_decoder.c +++ /dev/null @@ -1,422 +0,0 @@ -#include "coding.h" -#include - -#ifdef VGM_USE_VORBIS -#include - -#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */ - -#if FSB_VORBIS_USE_PRECOMPILED_FVS -#include "fsb_vorbis_data.h" -#endif - -#define FSB_VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ - -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); - -static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); -static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); -static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); - -/** - * 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) { - 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); - return NULL; -} - -/** - * Decodes raw FSB vorbis - */ -void decode_fsb_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { - 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; - pcm_convert_float_to_16(data, outbuf + samples_done * channels, samples_to_get, pcm); - samples_done += samples_to_get; - } - - /* 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)); -} - - -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) { - int bytes; - - /* try to load from external files first */ - bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - /* try to locate from the precompiled list */ - bytes = load_fvs_array(buf, bufsize, setup_id, streamFile); - if (bytes) - return bytes; - - /* not found */ - VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); - return 0; -} - -static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileSetup = NULL; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).fvs_{setup_id}" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); - streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - - if (streamFileSetup) { - /* file found, get contents into the buffer */ - size_t bytes = streamFileSetup->get_size(streamFileSetup); - if (bytes > bufsize) goto fail; - - if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes) - goto fail; - - streamFileSetup->close(streamFileSetup); - return bytes; - } - -fail: - if (streamFileSetup) streamFileSetup->close(streamFileSetup); - return 0; -} - -static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { - STREAMFILE * streamFileSetup = NULL; - - { - char setupname[PATH_LIMIT]; - char pathname[PATH_LIMIT]; - char *path; - - /* read "(dir/).fvs" */ - streamFile->get_name(streamFile,pathname,sizeof(pathname)); - path = strrchr(pathname,DIR_SEPARATOR); - if (path) - *(path+1) = '\0'; - else - pathname[0] = '\0'; - - snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); - streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - - if (streamFileSetup) { - /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ - int entries, i; - uint32_t offset = 0, size = 0; - - if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */ - entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */ - if (entries <= 0) goto fail; - - for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ - if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) { - offset = read_32bitLE(0x24 + i*0x10, streamFileSetup); - size = read_32bitLE(0x28 + i*0x10, streamFileSetup); - break; - } - } - if (!size || !offset || size > bufsize) goto fail; - - /* read into buf */ - if (read_streamfile(buf, offset, size, streamFileSetup) != size) - goto fail; - - streamFileSetup->close(streamFileSetup); - return size; - } - -fail: - if (streamFileSetup) streamFileSetup->close(streamFileSetup); - return 0; -} - -static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { -#if FSB_VORBIS_USE_PRECOMPILED_FVS - int i, list_length; - - list_length = sizeof(fvs_list) / sizeof(fvs_info); - for (i=0; i < list_length; i++) { - if (fvs_list[i].id == setup_id) { - if (fvs_list[i].size > bufsize) goto fail; - /* found: copy data as-is */ - memcpy(buf,fvs_list[i].setup, fvs_list[i].size); - return fvs_list[i].size; - } - } - -fail: -#endif - return 0; -} - -/* ********************************************** */ - -void free_fsb_vorbis(vorbis_codec_data * data) { - 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); -} - -void reset_fsb_vorbis(VGMSTREAM *vgmstream) { - 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 = 0; -} - -void seek_fsb_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { - 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; - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; -} - -#endif diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c new file mode 100644 index 00000000..f02d7a21 --- /dev/null +++ b/src/coding/mpeg_custom_utils.c @@ -0,0 +1,249 @@ +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG + +/* init config and validate per type */ +int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + mpeg_frame_info info; + + + /* get frame info at offset */ + if ( !mpeg_get_frame_info(streamFile, start_offset, &info)) + goto fail; + switch(info.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } + data->channels_per_frame = info.channels; + data->samples_per_frame = info.frame_samples; + + + /* extra checks per type */ + switch(data->type) { + case MPEG_XVAG: + if (data->config.chunk_size <= 0 || data->config.interleave <= 0) + goto fail; /* needs external fixed size */ + break; + + case MPEG_FSB: + if (data->config.fsb_padding != 0 + && data->config.fsb_padding != 2 + && data->config.fsb_padding != 4 + && data->config.fsb_padding != 16) + goto fail; /* aligned to closest 0/2/4/16 bytes */ + + /* get find interleave to stream offsets are set up externally */ + { + int current_data_size = info.frame_size; + int current_padding = 0; + if (info.layer == 3 && data->config.fsb_padding) { /* FSB padding for Layer III */ + current_padding = (current_data_size % data->config.fsb_padding) + ? data->config.fsb_padding - (current_data_size % data->config.fsb_padding) + : 0; + } + + data->config.interleave = current_data_size + current_padding; /* should be constant for all stream */ + } + break; + + case MPEG_P3D: + if (data->config.interleave <= 0) + goto fail; /* needs external fixed size */ + break; + + case MPEG_LYN: + case MPEG_AWC: + goto fail; /* not fully implemented */ + + case MPEG_STANDARD: + case MPEG_AHX: + case MPEG_EA: + if (data->channels_per_frame != data->config.channels) + goto fail; /* no multichannel expected */ + break; + + default: + break; /* nothing special needed */ + } + + + /* unknown encryption */ + if (data->type == MPEG_AHX && data->config.encryption) { + goto fail; + } + + + /* set encoder delay (samples to skip at the beginning of a stream) if needed, which varies with encoder used */ + switch(data->type) { + case MPEG_AHX: data->skip_samples = 480; break; /* observed default */ + case MPEG_P3D: data->skip_samples = info.frame_samples; break; /* matches Radical ADPCM (PC) output */ + default: break; + } + data->samples_to_discard = data->skip_samples; + + + return 1; +fail: + return 0; +} + + +/* writes data to the buffer and moves offsets */ +int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) { + mpeg_frame_info info; + size_t current_data_size = 0; + size_t current_padding = 0; + size_t current_interleave = 0; + + + /* Get data size to give to the decoder, per stream. Usually 1 frame at a time, + * but doesn't really need to be a full frame (decoder would request more data). */ + switch(data->type) { + + case MPEG_XVAG: /* frames of fixed size (though we could read frame info too) */ + current_data_size = data->config.chunk_size; + current_interleave = data->config.interleave; /* big interleave */ + break; + + case MPEG_FSB: /* frames with padding + interleave */ + if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) + goto fail; + current_data_size = info.frame_size; + + /* get FSB padding for Layer III (Layer II doesn't use it, and Layer I doesn't seem to be supported) */ + /* Padding sometimes contains garbage like the next frame header so we can't feed it to mpg123 or it gets confused. */ + if (info.layer == 3 && data->config.fsb_padding) { + current_padding = (current_data_size % data->config.fsb_padding) + ? data->config.fsb_padding - (current_data_size % data->config.fsb_padding) + : 0; + } + + /* frame interleave (ie. read 1 data-frame, skip 1 data-frame per stream) */ + current_interleave = data->config.interleave; /* constant, current_size+current_padding */ + + VGM_ASSERT(current_interleave != current_data_size+current_padding, + "MPEG FSB: non-constant interleave found @ 0x%08lx\n", stream->offset); + break; + + case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */ + current_data_size = data->config.interleave / 4; /* to ensure we don't feed mpg123 too much */ + current_interleave = data->config.interleave; /* fixed interleave (0x400) */ + //todo: last block is smaller that interleave, not sure how it's divided + break; + + default: /* standard frames (CBR or VBR) */ + if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) + goto fail; + current_data_size = info.frame_size; + break; + } + if (!current_data_size || current_data_size > data->buffer_size) { + VGM_LOG("MPEG: incorrect data_size 0x%x\n", current_data_size); + goto fail; + } + + + /* read single frame */ + data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile); + + + /* update offsets */ + stream->offset += current_data_size + current_padding; + + /* skip interleave once block is done, if defined */ + if (current_interleave && ((stream->offset - stream->channel_start_offset) % current_interleave == 0)) { + stream->offset += current_interleave * (data->ms_size-1); /* skip a block each stream */ + } + + return 1; +fail: + return 0; +} + + +/*****************/ +/* 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. + */ +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 */ + }; + + 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 || info->layer > 3) 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; + } + + return 1; + +fail: + return 0; +} + + +#endif diff --git a/src/coding/mpeg_custom_utils_ahx.c b/src/coding/mpeg_custom_utils_ahx.c new file mode 100644 index 00000000..01a5eb5c --- /dev/null +++ b/src/coding/mpeg_custom_utils_ahx.c @@ -0,0 +1,66 @@ +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG +#define MPEG_AHX_EXPECTED_FRAME_SIZE 0x414 + + +/* writes data to the buffer and moves offsets, transforming AHX frames as needed */ +int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) { + /* 0xFFF5E0C0 header: frame size 0x414 (160kbps, 22050Hz) but they actually are much shorter */ + size_t current_data_size = 0; + size_t file_size = get_streamfile_size(stream->streamfile); + + /* find actual frame size by looking for the next frame header */ + { + uint32_t current_header = (uint32_t)read_32bitBE(stream->offset, stream->streamfile); + off_t next_offset = 0x04; + + while (next_offset <= MPEG_AHX_EXPECTED_FRAME_SIZE) { + uint32_t next_header = (uint32_t)read_32bitBE(stream->offset + next_offset, stream->streamfile); + + if (current_header == next_header) { + current_data_size = next_offset; + break; + } + + /* AHXs end in a 0x0c footer (0x41485845 0x28632943 0x52490000 / "AHXE" "(c)C" "RI\0\0") */ + if (stream->offset + next_offset + 0x0c >= file_size) { + current_data_size = next_offset; + break; + } + + next_offset++; + } + } + if (!current_data_size || current_data_size > data->buffer_size || current_data_size > MPEG_AHX_EXPECTED_FRAME_SIZE) { + VGM_LOG("MPEG AHX: incorrect data_size 0x%x\n", current_data_size); + goto fail; + } + + + /* read VBR frames with CBR header, 0-fill up to expected size to keep mpg123 happy */ + data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,current_data_size,stream->streamfile); + memset(data->buffer + data->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - data->bytes_in_buffer); + data->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE; + + + /* encryption 0x08 modifies a few bits in the side_data every frame, here we decrypt the buffer */ + if (data->config.encryption) { + VGM_LOG("MPEG AHX: unknown encryption\n"); + goto fail; + } + + + /* update offsets */ + stream->offset += current_data_size; + if (stream->offset + 0x0c >= file_size) + stream->offset = file_size; /* move after 0x0c footer to reach EOF (shouldn't happen normally) */ + + + return 1; +fail: + return 0; +} + + +#endif diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index aac39747..a71d7618 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -4,31 +4,33 @@ #ifdef VGM_USE_MPEG #include +#include "mpeg_decoder.h" -#define AHX_EXPECTED_FRAME_SIZE 0x414 -#define MPEG_DEFAULT_BUFFER_SIZE 0x1000 /* should be >= AHX_EXPECTED_FRAME_SIZE */ -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; +/* TODO list for custom decoder + * - don't force channel param and get them from frame headers for some types (for MPEG_STANDARD) + * - use one stream per channel and advance streams as channels are done in case of streams like 2ch+1ch+1ch+2ch (not seen) + * (would also need to change sample_buffer copy) + * - improve validation of channels/samples_per_frame between streams + * - improve decoded samples to sample buffer copying (very picky with sizes) + * - use mpg123 stream_buffer, with flags per stream, and call copy to sample_buffer when all streams have some samples + * (so it could handle interleaved VBR frames). + * - AHX type 8 encryption + * - test encoder delays + * - improve error handling + */ + +/* mostly arbitrary max values */ +#define MPEG_DATA_BUFFER_SIZE 0x1000 +#define MPEG_MAX_CHANNELS 16 +#define MPEG_MAX_STREAM_FRAMES 10 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 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); +static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); +static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); +static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream); -/** - * Inits regular MPEG. - */ +/* Inits regular MPEG */ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) { mpeg_codec_data *data = NULL; @@ -36,7 +38,7 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset data = calloc(1,sizeof(mpeg_codec_data)); if (!data) goto fail; - data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE; + data->buffer_size = MPEG_DATA_BUFFER_SIZE; data->buffer = calloc(sizeof(uint8_t), data->buffer_size); if (!data->buffer) goto fail; @@ -64,7 +66,7 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset 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) { VGM_LOG("MPEG: unable to set up mpg123 @ 0x%08lx to 0x%08lx\n", start_offset, read_offset); - goto fail; //todo handle MPG123_DONE? + goto fail; //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); @@ -86,25 +88,12 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset if ((channels != -1 && channels_per_frame != channels)) goto fail; - if (mi.version == MPG123_1_0 && mi.layer == 1) - *coding_type = coding_MPEG1_L1; - else if (mi.version == MPG123_1_0 && mi.layer == 2) - *coding_type = coding_MPEG1_L2; - else if (mi.version == MPG123_1_0 && mi.layer == 3) - *coding_type = coding_MPEG1_L3; - else if (mi.version == MPG123_2_0 && mi.layer == 1) - *coding_type = coding_MPEG2_L1; - else if (mi.version == MPG123_2_0 && mi.layer == 2) - *coding_type = coding_MPEG2_L2; - else if (mi.version == MPG123_2_0 && mi.layer == 3) - *coding_type = coding_MPEG2_L3; - else if (mi.version == MPG123_2_5 && mi.layer == 1) - *coding_type = coding_MPEG25_L1; - else if (mi.version == MPG123_2_5 && mi.layer == 2) - *coding_type = coding_MPEG25_L2; - else if (mi.version == MPG123_2_5 && mi.layer == 3) - *coding_type = coding_MPEG25_L3; - else goto fail; + switch(mi.layer) { + case 1: *coding_type = coding_MPEG_layer1; break; + case 2: *coding_type = coding_MPEG_layer2; break; + case 3: *coding_type = coding_MPEG_layer3; break; + default: goto fail; + } if (mi.layer == 1) samples_per_frame = 384; @@ -116,9 +105,11 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset samples_per_frame = 576; else goto fail; - data->sample_rate_per_frame = sample_rate_per_frame; data->channels_per_frame = channels_per_frame; data->samples_per_frame = samples_per_frame; + if (channels_per_frame != channels) + goto fail; + /* reinit, to ignore the reading we've done so far */ mpg123_open_feed(main_m); @@ -131,99 +122,64 @@ fail: 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, mpeg_interleave_type interleave_type, uint32_t interleave_value) { + +/* Init custom MPEG, with given type and config. */ +mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) { mpeg_codec_data *data = NULL; + int stream_frames = 1; + int i, ok; /* init codec */ data = calloc(1,sizeof(mpeg_codec_data)); if (!data) goto fail; - data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE; + data->buffer_size = MPEG_DATA_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; + /* keep around to decode */ + data->custom = 1; + data->type = type; + memcpy(&data->config, config, sizeof(mpeg_custom_config)); + data->config.channels = channels; + /* init per subtype */ + switch(data->type) { + case MPEG_EAL31: + case MPEG_EAL32P: + case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break; + default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break; + } + if (!ok) + goto fail; - data->interleaved = 1; + if (channels <= 0 || channels > MPEG_MAX_CHANNELS) goto fail; + if (channels < data->channels_per_frame) goto fail; - /* 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->interleave_type = interleave_type; - data->interleave_value = interleave_value; - - /* 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; - - //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++) { - data->ms[i] = init_mpg123_handle(); - if (!data->ms[i]) goto fail; - } - - data->frame_buffer_size = sizeof(sample) * data->samples_per_frame * data->channels_per_frame; - data->frame_buffer = calloc(sizeof(uint8_t), data->frame_buffer_size); - if (!data->frame_buffer) goto fail; - - data->interleave_buffer_size = sizeof(sample) * data->samples_per_frame * channels; - data->interleave_buffer = calloc(sizeof(uint8_t), data->interleave_buffer_size); - if (!data->interleave_buffer) goto fail; + /* init stream decoders (separate as MPEG frames may need N previous frames from their stream to decode) */ + 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++) { + data->ms[i] = init_mpg123_handle(); + if (!data->ms[i]) goto fail; } + if (stream_frames > MPEG_MAX_STREAM_FRAMES) goto fail; - return data; + /* init stream buffer, big enough for one stream and N frames at a time (will be copied to sample buffer) */ + data->stream_buffer_size = sizeof(sample) * data->channels_per_frame * stream_frames * data->samples_per_frame; + data->stream_buffer = calloc(sizeof(uint8_t), data->stream_buffer_size); + if (!data->stream_buffer) goto fail; -fail: - free_mpeg(data); - return NULL; -} + /* init sample buffer, big enough for all streams/channels and N frames at a time */ + data->sample_buffer_size = sizeof(sample) * channels * stream_frames * data->samples_per_frame; + data->sample_buffer = calloc(sizeof(uint8_t), data->sample_buffer_size); + if (!data->sample_buffer) goto fail; -/** - * Inits MPEG for AHX, which ignores frame headers. - */ -mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count) { - 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; - + /* write output */ + config->interleave = data->config.interleave; /* for FSB */ return data; @@ -249,7 +205,7 @@ static mpg123_handle * init_mpg123_handle() { goto fail; } - mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); //todo fix gapless + mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); /* wonky support */ mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x10000); /* should be enough */ if (mpg123_open_feed(m) != MPG123_OK) { @@ -271,11 +227,10 @@ fail: void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { mpeg_codec_data * data = (mpeg_codec_data *) vgmstream->codec_data; - /* MPEGs streams contain one or two channels, so we may only need half VGMSTREAMCHANNELs for offsets */ - if (data->interleaved) { - decode_mpeg_interleave(vgmstream, data, outbuf, samples_to_do, channels); + if (!data->custom) { + decode_mpeg_standard(&vgmstream->ch[0], data, outbuf, samples_to_do, channels); } else { - decode_mpeg_default(&vgmstream->ch[0], data, outbuf, samples_to_do, channels); + decode_mpeg_custom(vgmstream, data, outbuf, samples_to_do, channels); } } @@ -283,7 +238,7 @@ void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, * Decode anything mpg123 can. * Feeds raw data and extracts decoded samples as needed. */ -static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { +static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { int samples_done = 0; mpg123_handle *m = data->m; @@ -315,7 +270,8 @@ static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data (samples_to_do-samples_done)*sizeof(sample)*channels, &bytes_done); data->buffer_used = 1; - } else { + } + else { rc = mpg123_decode(m, NULL,0, (unsigned char *)(outbuf+samples_done*channels), @@ -335,49 +291,59 @@ 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. + * Decode custom MPEG, allowing support for: single frames, interleave, mutant frames, multiple streams + * (1 frame = 1/2ch so Nch = 2ch*N/2 or 1ch*N or 2ch+1ch+2ch+...), etc. * - * 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 + * Decodes samples per each stream and muxes them into a single internal buffer before copying to outbuf * (to make sure channel samples are orderly copied between decode_mpeg calls). - * - * Interleave modes: - * - FIXED [XVAG]: fixed block_size per stream (unknown number of samples) - * (ex. b1 = N samples of ch1, b2 = N samples of ch2, b3 = M samples of ch1, etc) - * - FSB: single frames per stream with padding (block_size is frame_size+padding) [FSB] - * (ex. f1+f3+f5 = 1152*2 samples of ch1+2, f2+f4 = 1152*2 samples of ch3+4, etc) - * - P3D: unknown layout at the moment (possibly N + * decode_mpeg_custom_stream does the main decoding, while this handles layout and copying samples to output. */ -static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { +static void decode_mpeg_custom(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { int samples_done = 0, bytes_max, bytes_to_copy; while (samples_done < samples_to_do) { - if (data->bytes_used_in_interleave_buffer < data->bytes_in_interleave_buffer) { + if (data->bytes_used_in_sample_buffer < data->bytes_in_sample_buffer) { /* copy remaining samples */ - bytes_to_copy = data->bytes_in_interleave_buffer - data->bytes_used_in_interleave_buffer; + bytes_to_copy = data->bytes_in_sample_buffer - data->bytes_used_in_sample_buffer; bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels; if (bytes_to_copy > bytes_max) bytes_to_copy = bytes_max; - memcpy((uint8_t*)(outbuf+samples_done*channels), data->interleave_buffer + data->bytes_used_in_interleave_buffer, bytes_to_copy); + memcpy((uint8_t*)(outbuf+samples_done*channels), data->sample_buffer + data->bytes_used_in_sample_buffer, bytes_to_copy); /* update samples copied */ - data->bytes_used_in_interleave_buffer += bytes_to_copy; + data->bytes_used_in_sample_buffer += bytes_to_copy; samples_done += bytes_to_copy / sizeof(sample) / channels; } else { /* fill the internal sample buffer */ int i; - data->bytes_in_interleave_buffer = 0; - data->bytes_used_in_interleave_buffer = 0; + data->bytes_in_sample_buffer = 0; + data->bytes_used_in_sample_buffer = 0; + /* Handle offsets depending on the data layout (may only use half VGMSTREAMCHANNELs with 2ch streams) + * With multiple offsets it's expected offsets are set up pointing to the first frame of each stream. */ for (i=0; i < data->ms_size; i++) { - if (data->interleave_type == MPEG_P3D /* P3D have a strange way to interleave so just use first offset */ - || data->interleave_type == MPEG_EA) /* EA MPEG is simply frame by frame normal MPEG */ - decode_mpeg_interleave_samples(&vgmstream->ch[0], data, data->ms[i], channels, i, vgmstream->interleave_block_size); - else - decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size); + switch(data->type) { + //case MPEG_LYN: + case MPEG_FSB: + case MPEG_XVAG: + case MPEG_P3D: + /* multiple offsets, decodes 1 frame per stream until reaching interleave/block_size and skips it */ + decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i); + break; + + //case MPEG_EA: //? + case MPEG_AWC: + /* consecutive streams: multiple offsets, decodes 1 frame per stream */ + decode_mpeg_custom_stream(&vgmstream->ch[i], data, data->ms[i], channels, i); + break; + + default: + /* N frames: single offset, decodes all N frames per stream (sample buffer must be big enough for N) */ + decode_mpeg_custom_stream(&vgmstream->ch[0], data, data->ms[i], channels, i); + break; + } } /* discard (for looping): 'remove' decoded samples from the buffer */ @@ -385,115 +351,110 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data 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; + if (bytes_to_discard > data->bytes_in_sample_buffer) + bytes_to_discard = data->bytes_in_sample_buffer; /* pretend the samples were used up and readjust discard */ - data->bytes_used_in_interleave_buffer = bytes_to_discard; + data->bytes_used_in_sample_buffer = bytes_to_discard; data->samples_to_discard -= bytes_to_discard / sizeof(sample) / channels;; } } } - } -/** - * Decodes frames from a stream and muxes the samples into a intermediate buffer. - * Skips to the next interleaved block once reaching the stream's block end. - */ -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); +/* Decodes frames from a stream and muxes samples into a intermediate buffer and moves the stream offsets. */ +static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream) { + size_t bytes_done = 0; + size_t stream_size = get_streamfile_size(stream->streamfile); + int rc, ok; - /* decode samples from 1 full frame */ + + /* decode samples from one full-frame (as N data-frames = 1 full-frame) before exiting (to orderly copy to sample buffer) */ do { - int rc; - //int first_frame = stream->offset == stream->channel_start_offset; - - /* padded frame stuff */ - 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); - //VGM_LOG("off=%lx, st=%i, fs=%x, pd=%x\n", stream->offset, num_stream, data->current_frame_size, data->current_padding); - + //VGM_LOG("MPEG: new step of stream %i @ 0x%08lx\n", num_stream, stream->offset); /* 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; + if (stream->offset >= stream_size) { + memset(data->stream_buffer, 0, data->stream_buffer_size); + bytes_done = data->stream_buffer_size; + break; /* continue with other streams */ } - /* read more raw data (only 1 frame, to check interleave block end) */ + /* read more raw data */ if (!data->buffer_full) { //VGM_LOG("MPEG: reading more raw data\n"); - data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile); - - /* end of stream, fill frame buffer with 0s but continue normally with other streams */ - if (!data->bytes_in_buffer) { - memset(data->frame_buffer, 0, data->frame_buffer_size); - bytes_done = data->frame_buffer_size; - break; + switch(data->type) { + case MPEG_EAL31: + case MPEG_EAL32P: + case MPEG_EAL32S: ok = 0; break; //ok = mpeg_custom_parse_frame_ealayer3(stream, data); break; + case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break; + default: ok = mpeg_custom_parse_frame_default(stream, data); break; } + /* error/EOF, mpg123 can resync in some cases but custom MPEGs wouldn't need that */ + if (!ok || !data->bytes_in_buffer) { + VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset); + memset(data->stream_buffer, 0, data->stream_buffer_size); + bytes_done = data->stream_buffer_size; + break; /* continue with other streams */ + } + //VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset off=%lx\n", data->bytes_in_buffer, stream->offset); data->buffer_full = 1; data->buffer_used = 0; - - stream->offset += data->current_frame_size + data->current_padding; /* skip frame + FSB garbage */ - if (block_size && ((stream->offset - stream->channel_start_offset) % block_size==0)) { - stream->offset += block_size * (data->ms_size-1); /* skip a block per stream if block done */ - } + } #if 0 - //TODO: skip very first frame but add fake silence? only if first frame is fully decodable? - if (data->interleave_type == MPEG_FSB && first_frame) { - data->buffer_full = 0; + //TODO: in case of EALayer3 with "PCM flag" must put them in buffer + if (ea_pcm_flag) { + /* write some samples to data->stream_buffer *before* decoding this frame */ + bytes_done += read_streamfile(data->stream_buffer,offset,num_pcm_samples,stream->streamfile); + } +#endif +#if 0 + //TODO: FSB sometimes has garbage in the first frames, not sure why/when, no apparent patern + if (data->custom_type == MPEG_FSB && stream->offset == stream->channel_start_offset) { /* first frame */ + VGM_LOG("MPEG: skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset); - VGM_LOG("skip first frame @ %x - %x\n", stream->offset, stream->channel_start_offset); - memset(data->frame_buffer, 0, data->frame_buffer_size); - bytes_done = data->frame_buffer_size; + data->buffer_full = 0; + memset(data->stream_buffer, 0, data->stream_buffer_size); + bytes_done = data->stream_buffer_size; break; } #endif - } - /* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */ if (!data->buffer_used) { - //VGM_LOG("MPEG: get samples from new raw data\n"); + //VGM_LOG("MPEG: feed new data and get samples \n"); rc = mpg123_decode(m, data->buffer, data->bytes_in_buffer, - (unsigned char *)data->frame_buffer, data->frame_buffer_size, + (unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size, &bytes_done); data->buffer_used = 1; - } else { - //VGM_LOG("MPEG: get samples from old raw data\n"); + } + else { + //VGM_LOG("MPEG: get samples from old data\n"); rc = mpg123_decode(m, NULL,0, - (unsigned char *)data->frame_buffer, data->frame_buffer_size, + (unsigned char *)data->stream_buffer /*+bytes_done*/, data->stream_buffer_size, &bytes_done); } - /* samples per frame should be constant... */ - if (bytes_done > 0 && bytes_done < data->frame_buffer_size) { - 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; - } - /* not enough raw data, request more */ if (rc == MPG123_NEED_MORE) { - //VGM_LOG("MPEG: need more raw data\n"); + //VGM_LOG("MPEG: need more raw data to get samples\n"); + /* (apparently mpg123 can give bytes and request more at the same time and may mess up some calcs, when/how? */ + VGM_ASSERT(bytes_done > 0, "MPEG: bytes done but decoder requests more data\n"); data->buffer_full = 0; continue; } + //VGM_LOG("MPEG: got samples, bytes_done=0x%x (fsbs=0x%x)\n", bytes_done, data->stream_buffer_size); break; } while (1); - /* copy decoded frame to intermediate sample buffer, muxing channels + /* copy decoded full-frame to intermediate sample buffer, muxing channels * (ex stream1: ch1s1 ch1s2, stream2: ch2s1 ch2s2 > ch1s1 ch2s1 ch1s2 ch2s2) */ { size_t samples_done; @@ -506,159 +467,14 @@ static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_ for (i = 0; i < samples_done; i++) { /* decoded samples */ off_t in_offset = sz*i*channels_f + sz*fch; off_t out_offset = sz*i*channels + sz*(num_stream*channels_f + fch); - memcpy(data->interleave_buffer + out_offset, data->frame_buffer + in_offset, sz); + memcpy(data->sample_buffer + out_offset, data->stream_buffer + in_offset, sz); } } - data->bytes_in_interleave_buffer += bytes_done; + data->bytes_in_sample_buffer += bytes_done; } } -/** - * 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 int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, off_t offset) { - - if (data->interleave_type == MPEG_FIXED) { /* frames of fixed size */ - data->current_frame_size = data->interleave_value; - } - else if (data->interleave_type == MPEG_FSB) { /* padding between frames */ - mpeg_frame_info info; - - /* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */ - if ( !mpeg_get_frame_info(streamfile, offset, &info) ) - goto fail; - 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->interleave_value && info.layer == 3) { - data->current_padding = (data->current_frame_size % data->interleave_value) - ? data->interleave_value - (data->current_frame_size % data->interleave_value) - : 0; - } - - /* 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); - } - else if (data->interleave_type == MPEG_P3D) { /* varying frames size, even though the frame header says 0x120 */ - uint32_t header = read_32bitBE(offset,streamfile); - - if (read_32bitBE(offset+0x120,streamfile) == header) { - data->current_frame_size = 0x120; - } else if (read_32bitBE(offset+0xA0,streamfile) == header) { - data->current_frame_size = 0xA0; - } else if (read_32bitBE(offset+0x1C0,streamfile) == header) { - data->current_frame_size = 0x1C0; - } else if (read_32bitBE(offset+0x180,streamfile) == header) { - data->current_frame_size = 0x180; - //} else if (read_32bitBE(offset+0x120,streamfile) == -1) { /* at EOF */ - // data->current_frame_size = 0x120; - } else { - VGM_LOG("MPEG: unknown frame size @ %lx, %x\n", offset, read_32bitBE(offset+0x120,streamfile)); - goto fail; - } - } - else if (data->interleave_type == MPEG_EA) { /* straight frame by frame */ - mpeg_frame_info info; - - /* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */ - if ( !mpeg_get_frame_info(streamfile, offset, &info) ) - goto fail; - data->current_frame_size = info.frame_size; - } - - return 1; - -fail: - return 0; -} - -/** - * Decode AHX mono frames. - * mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they actually vary and are much shorter - */ -void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do) { - int samples_done = 0; - const int frame_size = AHX_EXPECTED_FRAME_SIZE; - mpg123_handle *m = data->m; - - while (samples_done < samples_to_do) { - size_t bytes_done; - int rc; - - /* read more raw data */ - if (!data->buffer_full) { - /* fill buffer up to next frame ending (or file ending) */ - int bytes_into_header = 0; - const uint8_t header[4] = {0xff,0xf5,0xe0,0xc0}; - off_t frame_offset = 0; - - /* assume that we are starting at a header, skip it and look for the next one */ - read_streamfile(data->buffer, stream->offset+frame_offset, 4, stream->streamfile); - frame_offset += 4; - - do { - uint8_t byte; - byte = read_8bit(stream->offset+frame_offset,stream->streamfile); - data->buffer[frame_offset] = byte; - frame_offset++; - - if (byte == header[bytes_into_header]) { - bytes_into_header++; - } else { - /* This might have been the first byte of the header, so - * we need to check again. - * No need to get more complicated than this, though, since - * there are no repeated characters in the search string. */ - if (bytes_into_header>0) { - frame_offset--; - } - bytes_into_header=0; - } - - if (bytes_into_header==4) { - break; - } - } while (frame_offset < frame_size); - - if (bytes_into_header==4) - frame_offset-=4; - memset(data->buffer+frame_offset,0,frame_size-frame_offset); - - data->buffer_full = 1; - data->buffer_used = 0; - - stream->offset += frame_offset; - } - - /* feed new raw data to the decoder if needed, copy decodec results to output */ - if (!data->buffer_used) { - rc = mpg123_decode(m, - data->buffer,frame_size, - (unsigned char *)(outbuf+samples_done), - (samples_to_do-samples_done)*sizeof(sample), - &bytes_done); - data->buffer_used = 1; - } else { - rc = mpg123_decode(m, - NULL,0, - (unsigned char *)(outbuf+samples_done), - (samples_to_do-samples_done)*sizeof(sample), - &bytes_done); - } - - /* not enough raw data, request more */ - if (rc == MPG123_NEED_MORE) { - data->buffer_full = 0; - } - - /* update copied samples */ - samples_done += bytes_done/sizeof(sample);/* mono */ - } -} /*********/ /* UTILS */ @@ -668,16 +484,18 @@ void free_mpeg(mpeg_codec_data *data) { if (!data) return; - mpg123_delete(data->m); - if (data->interleaved) { + if (!data->custom) { + mpg123_delete(data->m); + } + else { int i; for (i=0; i < data->ms_size; i++) { mpg123_delete(data->ms[i]); } free(data->ms); - free(data->interleave_buffer); - free(data->frame_buffer); + free(data->stream_buffer); + free(data->sample_buffer); } free(data->buffer); @@ -694,32 +512,37 @@ void reset_mpeg(VGMSTREAM *vgmstream) { off_t input_offset; mpeg_codec_data *data = vgmstream->codec_data; - /* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */ - mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); - /* reset multistream */ //todo check if stream offsets are properly reset - if (data->interleaved) { + + if (!data->custom) { + /* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */ + mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); + } + else { int i; for (i=0; i < data->ms_size; i++) { mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset); } - data->bytes_in_interleave_buffer = 0; - data->bytes_used_in_interleave_buffer = 0; + data->bytes_in_sample_buffer = 0; + data->bytes_used_in_sample_buffer = 0; + + /* initial delay */ + data->samples_to_discard = data->skip_samples; } } void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { - /* won't work for fake AHX MPEG */ off_t input_offset; mpeg_codec_data *data = vgmstream->codec_data; /* seek multistream */ - if (!data->interleaved) { - mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset); - if (vgmstream->loop_ch) - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset; - } else { + if (!data->custom) { + mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset); + if (vgmstream->loop_ch) + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset; + } + else { int i; /* re-start from 0 */ for (i=0; i < data->ms_size; i++) { @@ -727,11 +550,12 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { if (vgmstream->loop_ch) 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 */ - data->samples_to_discard = num_sample; + data->bytes_in_sample_buffer = 0; + data->bytes_used_in_sample_buffer = 0; - data->bytes_in_interleave_buffer = 0; - data->bytes_used_in_interleave_buffer = 0; + /* manually discard samples, since we don't really know the correct offset */ + data->samples_to_discard = num_sample; + data->samples_to_discard += data->skip_samples; } data->buffer_full = 0; @@ -740,26 +564,31 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) { - struct mpg123_frameinfo mi; - mpg123_handle *m = data->m; + /* if not found just return 0 and expect to fail (if used for num_samples) */ + if (!data->custom) { + struct mpg123_frameinfo mi; + mpg123_handle *m = data->m; - if (MPG123_OK != mpg123_info(m, &mi)) - return 0; + if (m == NULL || MPG123_OK != mpg123_info(m, &mi)) + return 0; - /* In this case just return 0 and expect to fail (if used for num_samples) - * We would need to read the number of frames in some frame header or count them to get samples */ - if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used - return 0; + /* We would need to read all VBR frames headers to count samples */ + if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used to get an approx + return 0; - return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000); + return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000); + } + else { + return 0; /* a bit too complex for what is worth */ + } } -/** - * disables/enables stderr output, useful for MPEG known to contain recoverable errors - */ +/* disables/enables stderr output, useful for MPEG known to contain recoverable errors */ void mpeg_set_error_logging(mpeg_codec_data * data, int enable) { - mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable); - if (data->interleaved) { + if (!data->custom) { + mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable); + } + else { int i; for (i=0; i < data->ms_size; i++) { mpg123_param(data->ms[i], MPG123_ADD_FLAGS, MPG123_QUIET, !enable); @@ -767,93 +596,4 @@ 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/coding/mpeg_decoder.h b/src/coding/mpeg_decoder.h new file mode 100644 index 00000000..544581a2 --- /dev/null +++ b/src/coding/mpeg_decoder.h @@ -0,0 +1,29 @@ +#ifndef _MPEG_DECODER_H_ +#define _MPEG_DECODER_H_ + +#include "../vgmstream.h" + +/* used by mpeg_decoder.c, but scattered in other .c files */ +#ifdef VGM_USE_MPEG +typedef struct { + int version; + int layer; + int bit_rate; + int sample_rate; + int frame_samples; + int frame_size; /* bytes */ + int channels; +} mpeg_frame_info; + +int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info * info); + +int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); +//int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type); + +int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data); +int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data); +//int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data); + +#endif/* VGM_USE_MPEG */ + +#endif/*_MPEG_DECODER_H_ */ diff --git a/src/coding/fsb_vorbis_data.h b/src/coding/vorbis_custom_data_fsb.h similarity index 99% rename from src/coding/fsb_vorbis_data.h rename to src/coding/vorbis_custom_data_fsb.h index f3f1e141..ddff376c 100644 --- a/src/coding/fsb_vorbis_data.h +++ b/src/coding/vorbis_custom_data_fsb.h @@ -2,7 +2,7 @@ #define _FSB_VORBIS_DATA_H_ /** - * fvs (FSB vorbis setup) data for fsb_vorbis_decoder.c + * fvs (FSB vorbis setup) data for FSB Vorbis. * They are Vorbis setup packets (type 0x05) binaries converted to c-arrays. * Extracted from FMOD by python-fsb5 (https://github.com/HearthSim/python-fsb5). */ diff --git a/src/coding/wwise_vorbis_data.h b/src/coding/vorbis_custom_data_wwise.h similarity index 98% rename from src/coding/wwise_vorbis_data.h rename to src/coding/vorbis_custom_data_wwise.h index 92887091..4946f37e 100644 --- a/src/coding/wwise_vorbis_data.h +++ b/src/coding/vorbis_custom_data_wwise.h @@ -2,7 +2,7 @@ #define _WWISE_VORBIS_DATA_H_ /** - * Codebook data for wwise_vorbis_decoder.c + * Codebook data for Wwise Vorbis. * They are Wwise Vorbis codebook binaries converted to c-arrays. * Extracted from Wwise SDK by ww2ogg. The originals have all codebooks together with an index table at the end. * Here each codebook is an array instead, and the index is converted to a list. diff --git a/src/coding/ogl_vorbis_decoder.c b/src/coding/vorbis_custom_decoder.c similarity index 51% rename from src/coding/ogl_vorbis_decoder.c rename to src/coding/vorbis_custom_decoder.c index 18ecb8c4..1fd256f2 100644 --- a/src/coding/ogl_vorbis_decoder.c +++ b/src/coding/vorbis_custom_decoder.c @@ -1,34 +1,40 @@ -#include "coding.h" #include +#include "coding.h" +#include "vorbis_custom_decoder.h" #ifdef VGM_USE_VORBIS #include -#define ogl_vorbis_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ +#define VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ -static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm); +static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm); /** - * Inits a raw OGL vorbis stream. + * Inits a vorbis stream of some custom variety. * - * 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. + * Normally Vorbis packets are stored in .ogg, which is divided into OggS pages/packets, and the first packets contain necessary + * Vorbis setup. For custom vorbis the OggS layer is replaced/optimized, the setup can be modified or stored elsewhere + * (i.e.- in the .exe) and raw Vorbis packets may be modified as well, presumably to shave off some kb and/or obfuscate. + * We'll manually read/modify the data and decode it with libvorbis calls. * - * OGL simply removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but - * the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning). + * Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html */ -vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t start_offset, off_t * data_start_offset) { - vorbis_codec_data * data = NULL; +vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) { + vorbis_custom_codec_data * data = NULL; + int ok; /* init stuff */ - data = calloc(1,sizeof(vorbis_codec_data)); + data = calloc(1,sizeof(vorbis_custom_codec_data)); if (!data) goto fail; - data->buffer_size = ogl_vorbis_DEFAULT_BUFFER_SIZE; + data->buffer_size = VORBIS_DEFAULT_BUFFER_SIZE; data->buffer = calloc(sizeof(uint8_t), data->buffer_size); if (!data->buffer) goto fail; + /* keep around to decode too */ + data->type = type; + memcpy(&data->config, config, sizeof(vorbis_custom_config)); + /* 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 */ @@ -38,35 +44,15 @@ vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t sta data->op.packet = data->buffer; data->op.b_o_s = 1; /* fake headers start */ - { - /* read 3 packets with triad (id/comment/setup), each with an OGL header */ - off_t offset = start_offset; - size_t packet_size; - - /* normal identificacion packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - offset += 2+packet_size; - - /* normal comment packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - offset += 2+packet_size; - - /* normal setup packet */ - packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; - if (packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - offset += 2+packet_size; - - /* data starts after triad */ - *data_start_offset = offset; + /* init header */ + switch(data->type) { + case VORBIS_FSB: ok = vorbis_custom_setup_init_fsb(streamFile, start_offset, data); break; + case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(streamFile, start_offset, data); break; + case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(streamFile, start_offset, data); break; + case VORBIS_SK: ok = vorbis_custom_setup_init_sk(streamFile, start_offset, data); break; + default: goto fail; } + if(!ok) goto fail; data->op.b_o_s = 0; /* end of fake headers */ @@ -75,19 +61,21 @@ vorbis_codec_data * init_ogl_vorbis_codec_data(STREAMFILE *streamFile, off_t sta if (vorbis_block_init(&data->vd,&data->vb) != 0) goto fail; + /* write output */ + config->data_start_offset = data->config.data_start_offset; + + return data; fail: - free_ogl_vorbis(data); + free_vorbis_custom(data); return NULL; } -/** - * Decodes raw OGL vorbis - */ -void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { +/* Decodes Vorbis packets into a libvorbis sample buffer, and copies them to outbuf */ +void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { VGMSTREAMCHANNEL *stream = &vgmstream->ch[0]; - vorbis_codec_data * data = vgmstream->codec_data; + vorbis_custom_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; @@ -95,8 +83,8 @@ void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_t 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)); + if (stream->offset >= stream_size) { + memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels); break; } @@ -131,37 +119,40 @@ void decode_ogl_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_t vorbis_synthesis_read(&data->vd, samples_to_get); } else { /* read more data */ - int rc; + int ok, rc; - /* this is not actually needed, but feels nicer */ - data->op.granulepos += samples_to_do; + /* not actually needed, but feels nicer */ + data->op.granulepos += samples_to_do; /* can be changed next if desired */ data->op.packetno++; - /* get next packet size from the OGL 16b header */ - data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; - stream->offset += 2; - if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { - goto decode_fail; /* eof or end padding */ + /* read/transform data into the ogg_packet buffer and advance offsets */ + switch(data->type) { + case VORBIS_FSB: ok = vorbis_custom_parse_packet_fsb(stream, data); break; + case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break; + case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break; + case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break; + default: goto decode_fail; + } + if(!ok) { + VGM_LOG("Vorbis: cannot parse packet @ around %lx\n",stream->offset); + goto decode_fail; } - /* 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(); + VGM_LOG("Vorbis: not an audio packet @ %lx\n",stream->offset); continue; /* not tested */ } else if (rc != 0) { + VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset); goto decode_fail; } /* finally decode the logical block into samples */ rc = vorbis_synthesis_blockin(&data->vd,&data->vb); if (rc != 0) { + VGM_LOG("Vorbis: cannot decode Vorbis block @ %lx\n",stream->offset); goto decode_fail; /* ? */ } @@ -176,9 +167,8 @@ decode_fail: memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample)); } - -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 */ +/* converts from internal Vorbis format to standard PCM (mostly from Xiph's decoder_example.c) */ +static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * outbuf, int samples_to_do, float ** pcm) { int i,j; /* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc) @@ -199,7 +189,7 @@ static void pcm_convert_float_to_16(vorbis_codec_data * data, sample * outbuf, i /* ********************************************** */ -void free_ogl_vorbis(vorbis_codec_data * data) { +void free_vorbis_custom(vorbis_custom_codec_data * data) { if (!data) return; @@ -212,19 +202,19 @@ void free_ogl_vorbis(vorbis_codec_data * data) { free(data); } -void reset_ogl_vorbis(VGMSTREAM *vgmstream) { - vorbis_codec_data *data = vgmstream->codec_data; +void reset_vorbis_custom(VGMSTREAM *vgmstream) { + vorbis_custom_codec_data *data = vgmstream->codec_data; - /* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead. + /* Seeking is provided by the Ogg layer, so with custom vorbis we'd 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 = 0; } -void seek_ogl_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { - vorbis_codec_data *data = vgmstream->codec_data; +void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample) { + vorbis_custom_codec_data *data = vgmstream->codec_data; - /* Seeking is provided by the Ogg layer, so with raw vorbis we need seek tables instead. + /* Seeking is provided by the Ogg layer, so with custom vorbis we'd 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; diff --git a/src/coding/vorbis_custom_decoder.h b/src/coding/vorbis_custom_decoder.h new file mode 100644 index 00000000..e3957b28 --- /dev/null +++ b/src/coding/vorbis_custom_decoder.h @@ -0,0 +1,19 @@ +#ifndef _VORBIS_CUSTOM_DECODER_H_ +#define _VORBIS_CUSTOM_DECODER_H_ + +#include "../vgmstream.h" + +/* used by vorbis_custom_decoder.c, but scattered in other .c files */ +#ifdef VGM_USE_VORBIS +int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); +int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); +int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); +int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data); + +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data); +#endif/* VGM_USE_VORBIS */ + +#endif/*_VORBIS_CUSTOM_DECODER_H_ */ diff --git a/src/coding/vorbis_custom_utils_fsb.c b/src/coding/vorbis_custom_utils_fsb.c new file mode 100644 index 00000000..3198da0c --- /dev/null +++ b/src/coding/vorbis_custom_utils_fsb.c @@ -0,0 +1,275 @@ +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */ +#if FSB_VORBIS_USE_PRECOMPILED_FVS +#include "vorbis_custom_data_fsb.h" +#endif + + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); +static int build_header_comment(uint8_t * buf, size_t bufsize); +static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); + +static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); +static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); +static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * FSB references an external setup packet by the setup_id, and packets have mini headers with the size. + * + * Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and + * fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor). + */ +int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { + vorbis_custom_config cfg = data->config; + + data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.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 = build_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 = build_header_setup(data->buffer, data->buffer_size, cfg.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 */ + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { + size_t bytes; + + /* 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) { + VGM_LOG("FSB Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2); + goto fail; /* EOF or end padding */ + } + + /* read raw block */ + bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); + stream->offset += bytes; + if (bytes != data->op.bytes) { + VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes); + goto fail; /* wrong packet? */ + } + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +static int build_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 build_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 build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { + int bytes; + + /* try to load from external files first */ + bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile); + if (bytes) + return bytes; + + bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile); + if (bytes) + return bytes; + + /* try to locate from the precompiled list */ + bytes = load_fvs_array(buf, bufsize, setup_id, streamFile); + if (bytes) + return bytes; + + /* not found */ + VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id); + return 0; +} + +static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { + STREAMFILE * streamFileSetup = NULL; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + + /* read "(dir/).fvs_{setup_id}" */ + streamFile->get_name(streamFile,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id); + streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + } + + if (streamFileSetup) { + /* file found, get contents into the buffer */ + size_t bytes = streamFileSetup->get_size(streamFileSetup); + if (bytes > bufsize) goto fail; + + if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes) + goto fail; + + streamFileSetup->close(streamFileSetup); + return bytes; + } + +fail: + if (streamFileSetup) streamFileSetup->close(streamFileSetup); + return 0; +} + +static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { + STREAMFILE * streamFileSetup = NULL; + + { + char setupname[PATH_LIMIT]; + char pathname[PATH_LIMIT]; + char *path; + + /* read "(dir/).fvs" */ + streamFile->get_name(streamFile,pathname,sizeof(pathname)); + path = strrchr(pathname,DIR_SEPARATOR); + if (path) + *(path+1) = '\0'; + else + pathname[0] = '\0'; + + snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname); + streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE); + } + + if (streamFileSetup) { + /* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */ + int entries, i; + uint32_t offset = 0, size = 0; + + if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */ + entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */ + if (entries <= 0) goto fail; + + for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */ + if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) { + offset = read_32bitLE(0x24 + i*0x10, streamFileSetup); + size = read_32bitLE(0x28 + i*0x10, streamFileSetup); + break; + } + } + if (!size || !offset || size > bufsize) goto fail; + + /* read into buf */ + if (read_streamfile(buf, offset, size, streamFileSetup) != size) + goto fail; + + streamFileSetup->close(streamFileSetup); + return size; + } + +fail: + if (streamFileSetup) streamFileSetup->close(streamFileSetup); + return 0; +} + +static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) { +#if FSB_VORBIS_USE_PRECOMPILED_FVS + int i, list_length; + + list_length = sizeof(fvs_list) / sizeof(fvs_info); + for (i=0; i < list_length; i++) { + if (fvs_list[i].id == setup_id) { + if (fvs_list[i].size > bufsize) goto fail; + /* found: copy data as-is */ + memcpy(buf,fvs_list[i].setup, fvs_list[i].size); + return fvs_list[i].size; + } + } + +fail: +#endif + return 0; +} + +#endif diff --git a/src/coding/vorbis_custom_utils_ogl.c b/src/coding/vorbis_custom_utils_ogl.c new file mode 100644 index 00000000..f75a912f --- /dev/null +++ b/src/coding/vorbis_custom_utils_ogl.c @@ -0,0 +1,77 @@ +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * OGL removes the Ogg layer and uses 16b packet headers, that have the size of the next packet, but + * the lower 2b need to be removed (usually 00 but 01 for the id packet, not sure about the meaning). + */ +int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { + off_t offset = start_offset; + size_t packet_size; + + /* read 3 packets with triad (id/comment/setup), each with an OGL header */ + + /* normal identificacion packet */ + packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + offset += 2+packet_size; + + /* normal comment packet */ + packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + offset += 2+packet_size; + + /* normal setup packet */ + packet_size = (uint16_t)read_16bitLE(offset, streamFile) >> 2; + if (packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+2,packet_size, streamFile); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + offset += 2+packet_size; + + /* data starts after triad */ + data->config.data_start_offset = offset; + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { + size_t bytes; + + /* get next packet size from the OGL 16b header (upper 14b) */ + data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile) >> 2; + stream->offset += 2; + if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) { + VGM_LOG("OGL Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2); + goto fail; /* EOF or end padding */ + } + + /* read raw block */ + bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); + stream->offset += data->op.bytes; + if (bytes != data->op.bytes) { + VGM_LOG("OGL Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes); + goto fail; /* wrong packet? */ + } + + return 1; + +fail: + return 0; +} + +#endif diff --git a/src/coding/vorbis_custom_utils_sk.c b/src/coding/vorbis_custom_utils_sk.c new file mode 100644 index 00000000..b730f537 --- /dev/null +++ b/src/coding/vorbis_custom_utils_sk.c @@ -0,0 +1,194 @@ +#include "vorbis_custom_decoder.h" + +#ifdef VGM_USE_VORBIS +#include + +/* **************************************************************************** */ +/* DEFS */ +/* **************************************************************************** */ + +static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet); +static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size); + + +/* **************************************************************************** */ +/* EXTERNAL API */ +/* **************************************************************************** */ + +/** + * SK just replaces the id 0x4F676753 ("OggS") by 0x11534B10 (\11"SK"\10), and the word "vorbis" by "SK" + * in init packets (for obfuscation, surely). So essentially we are parsing regular Ogg here. + * + * A simpler way to implement this would be in ogg_vorbis_file with read callbacks (pretend this is proof of concept). + */ +int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { + off_t offset = start_offset; + off_t id_offset = 0, comment_offset = 0, setup_offset = 0; + size_t id_size = 0, comment_size = 0, setup_size = 0; + int page_packets; + + /* rebuild header packets, they are standard except the "vorbis" keyword is replaced by "SK" */ + + /* first page has the id packet */ + if (!get_page_info(streamFile, offset, &id_offset, &id_size, &page_packets, 0)) goto fail; + if (page_packets != 1) goto fail; + offset = id_offset + id_size; + + /* second page has the comment and setup packets */ + if (!get_page_info(streamFile, offset, &comment_offset, &comment_size, &page_packets, 0)) goto fail; + if (page_packets != 2) goto fail; + if (!get_page_info(streamFile, offset, &setup_offset, &setup_size, &page_packets, 1)) goto fail; + if (page_packets != 2) goto fail; + offset = comment_offset + comment_size + setup_size; + + + /* init with all offsets found */ + data->op.bytes = build_header(data->buffer, data->buffer_size, streamFile, id_offset, id_size); + 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 = build_header(data->buffer, data->buffer_size, streamFile, comment_offset, comment_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 = build_header(data->buffer, data->buffer_size, streamFile, setup_offset, setup_size); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + + /* data starts after triad */ + data->config.data_start_offset = offset; + + return 1; + +fail: + VGM_LOG("SK Vorbis: failed to setup init packets @ around 0x%08lx\n", offset); + return 0; +} + + +int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { + off_t packet_offset = 0; + size_t packet_size = 0; + int page_packets; + + /* read OggS/SK page and get current packet */ + if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, data->current_packet)) goto fail; + data->current_packet++; + + /* read raw block */ + data->op.bytes = read_streamfile(data->buffer, packet_offset, packet_size, stream->streamfile); + if (data->op.bytes != packet_size) { + VGM_LOG("SK Vorbis: read error, 0x%x packet size vs 0x%lx bytes @ %lx\n", packet_size, data->op.bytes, packet_offset); + goto fail; /* wrong packet? */ + } + + + if (data->current_packet >= page_packets) { + /* processed all packets in page, go to next */ + if (!get_page_info(stream->streamfile, stream->offset, &packet_offset, &packet_size, &page_packets, -1)) goto fail; + stream->offset = packet_offset + packet_size; + data->current_packet = 0; + } + + + return 1; + +fail: + VGM_LOG("SK Vorbis: failed to parse packet @ around 0x%08lx\n", stream->offset); + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + +/** + * Get packet info from an Ogg page, from segment/packet N (-1 = all segments) + * + * Page format: + * 0x00(4): capture pattern ("OggS") + * 0x01(1): stream structure version + * 0x05(1): header type flag + * 0x06(8): absolute granule position + * 0x0e(4): stream serial number + * 0x12(4): page sequence number + * 0x16(4): page checksum + * 0x1a(1): page segments (total bytes in segment table) + * 0x1b(n): segment table (N bytes, 1 packet is sum of sizes until != 0xFF) + * 0x--(n): data + * Reference: https://xiph.org/ogg/doc/framing.html + */ +static int get_page_info(STREAMFILE *streamFile, off_t page_offset, off_t *out_packet_offset, size_t *out_packet_size, int *out_page_packets, int target_packet) { + off_t table_offset, current_packet_offset, target_packet_offset = 0; + size_t total_packets_size = 0, current_packet_size = 0, target_packet_size = 0; + int page_packets = 0; + uint8_t segments; + int i; + + + if (read_32bitBE(page_offset+0x00, streamFile) != 0x11534B10) /* \11"SK"\10 */ + goto fail; /* not a valid page */ + /* No point on validating other stuff, but they look legal enough (CRC too it seems) */ + + segments = (uint8_t)read_8bit(page_offset+0x1a, streamFile); + + table_offset = page_offset + 0x1b; + current_packet_offset = page_offset + 0x1b + segments; /* first packet starts after segments */ + + /* process segments */ + for (i = 0; i < segments; i++) { + uint8_t segment_size = (uint8_t)read_8bit(table_offset, streamFile); + total_packets_size += segment_size; + current_packet_size += segment_size; + table_offset += 0x01; + + if (segment_size != 0xFF) { /* packet complete */ + page_packets++; + + if (target_packet+1 == page_packets) { + target_packet_offset = current_packet_offset; + target_packet_size = current_packet_size; + } + + /* keep reading to fill page_packets */ + current_packet_offset += current_packet_size; /* move to next packet */ + current_packet_size = 0; + } + } + + /* < 0 is accepted and returns first offset and all packets sizes */ + if (target_packet+1 > page_packets) goto fail; + if (target_packet < 0) { + target_packet_offset = page_offset + 0x1b + segments; /* first */ + target_packet_size = total_packets_size; + } + + if (out_packet_offset) *out_packet_offset = target_packet_offset; + if (out_packet_size) *out_packet_size = target_packet_size; + if (out_page_packets) *out_page_packets = page_packets; + + return 1; + +fail: + VGM_LOG("SK Vorbis: failed to read page @ 0x%08lx\n", page_offset); + return 0; +} + +/* rebuild a "SK" header packet to a "vorbis" one */ +static int build_header(uint8_t * buf, size_t bufsize, STREAMFILE *streamFile, off_t packet_offset, size_t packet_size) { + int bytes; + + if (0x07+packet_size-0x03 > bufsize) return 0; + + put_8bit (buf+0x00, read_8bit(packet_offset,streamFile)); /* packet_type */ + memcpy (buf+0x01, "vorbis", 6); /* id */ + bytes = read_streamfile(buf+0x07,packet_offset+0x03, packet_size-0x03,streamFile); /* copy rest (all except id+"SK") */ + if (packet_size-0x03 != bytes) { + VGM_LOG("SK Vorbis: packet (size 0x%x) not copied correctly (bytes=%x) @ 0x%lx\n", packet_size, bytes, packet_offset); + return 0; + } + + return 0x07+packet_size-0x03; +} + +#endif diff --git a/src/coding/wwise_vorbis_utils.c b/src/coding/vorbis_custom_utils_wwise.c similarity index 73% rename from src/coding/wwise_vorbis_utils.c rename to src/coding/vorbis_custom_utils_wwise.c index 7b9d2577..8155ab56 100644 --- a/src/coding/wwise_vorbis_utils.c +++ b/src/coding/vorbis_custom_utils_wwise.c @@ -1,11 +1,11 @@ -#include "../vgmstream.h" +#include "vorbis_custom_decoder.h" + #ifdef VGM_USE_VORBIS -#include "wwise_vorbis_utils.h" +#include #define WWISE_VORBIS_USE_PRECOMPILED_WVC 1 /* if enabled vgmstream weights ~150kb more but doesn't need external .wvc packets */ - #if WWISE_VORBIS_USE_PRECOMPILED_WVC -#include "wwise_vorbis_data.h" +#include "vorbis_custom_data_wwise.h" #endif @@ -13,37 +13,131 @@ /* DEFS */ /* **************************************************************************** */ -/* A internal struct to pass around and simulate a bitstream. +/* An internal struct to pass around and simulate a bitstream. * Mainly to keep code cleaner and somewhat closer to ww2ogg */ typedef struct { uint8_t * buf; /* buffer to read/write*/ size_t bufsize; /* max size of the buffer */ off_t b_off; /* current offset in bits inside the buffer */ -} ww_stream; +} ww_bitstream; -static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian); -static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile); +static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long); +static int build_header_comment(uint8_t * buf, size_t bufsize); +static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian); +static int rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); +static int rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels); -static int codebook_library_copy(ww_stream * ow, ww_stream * iw); -static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_size, STREAMFILE *streamFile); -static int codebook_library_rebuild_by_id(ww_stream * ow, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile); -static int tremor_ilog(unsigned int v); -static unsigned int tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions); +static int ww2ogg_generate_vorbis_packet(ww_bitstream * ow, ww_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian); +static int ww2ogg_generate_vorbis_setup(ww_bitstream * ow, ww_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile); +static int ww2ogg_codebook_library_copy(ww_bitstream * ow, ww_bitstream * iw); +static int ww2ogg_codebook_library_rebuild(ww_bitstream * ow, ww_bitstream * iw, size_t cb_size, STREAMFILE *streamFile); +static int ww2ogg_codebook_library_rebuild_by_id(ww_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile); +static int ww2ogg_tremor_ilog(unsigned int v); +static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions); -static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile); +static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile); static int load_wvc_file(uint8_t * buf, size_t bufsize, uint32_t codebook_id, STREAMFILE *streamFile); -static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_type setup_type); +static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type); -static int r_bits(ww_stream * iw, int num_bits, uint32_t * value); -static int w_bits(ww_stream * ow, int num_bits, uint32_t value); +static int r_bits(ww_bitstream * iw, int num_bits, uint32_t * value); +static int w_bits(ww_bitstream * ow, int num_bits, uint32_t value); /* **************************************************************************** */ /* EXTERNAL API */ /* **************************************************************************** */ +/** + * Wwise stores a reduced setup, and packets have mini headers with the size, and data packets + * may reduced as well. The format evolved over time so there are many variations. + * The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter. + * + * Format reverse-engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg). + */ +int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { + size_t header_size, packet_size; + vorbis_custom_config cfg = data->config; + + if (cfg.setup_type == HEADER_TRIAD) { + /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ + off_t offset = start_offset; + + /* normal identificacion packet */ + header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); + if (!header_size || packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + offset += header_size + packet_size; + + /* normal comment packet */ + header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); + if (!header_size || packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ + offset += header_size + packet_size; + + /* normal setup packet */ + header_size = get_packet_header(streamFile, offset, cfg.header_type, (int*)&data->op.granulepos, &packet_size, cfg.big_endian); + if (!header_size || packet_size > data->buffer_size) goto fail; + data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + offset += header_size + packet_size; + } + else { + /* rebuild headers */ + + /* new identificacion packet */ + data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, cfg.blocksize_0_exp, cfg.blocksize_1_exp); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ + + /* new comment packet */ + data->op.bytes = build_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 */ + + /* rebuild setup packet */ + data->op.bytes = rebuild_setup(data->buffer, data->buffer_size, streamFile, start_offset, data, cfg.big_endian, cfg.channels); + if (!data->op.bytes) goto fail; + if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ + } + + return 1; + +fail: + return 0; +} + + +int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { + size_t header_size, packet_size = 0; + + /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */ + header_size = get_packet_header(stream->streamfile, stream->offset, data->config.header_type, (int*)&data->op.granulepos, &packet_size, data->config.big_endian); + if (!header_size || packet_size > data->buffer_size) { + VGM_LOG("Wwise Vorbis: wrong packet (0x%x) @ %lx\n", packet_size, stream->offset); + goto fail; + } + + data->op.bytes = rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, data->config.big_endian); + stream->offset += header_size + packet_size; + if (!data->op.bytes || data->op.bytes >= 0xFFFF) { + VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-header_size-packet_size); + goto fail; + } + + return 1; + +fail: + return 0; +} + +/* **************************************************************************** */ +/* INTERNAL HELPERS */ +/* **************************************************************************** */ + /* loads info from a wwise packet header */ -int wwise_vorbis_get_header(STREAMFILE *streamFile, off_t offset, wwise_header_type header_type, int * granulepos, size_t * packet_size, int big_endian) { +static int get_packet_header(STREAMFILE *streamFile, off_t offset, wwise_header_t header_type, int * granulepos, size_t * packet_size, int big_endian) { int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; @@ -70,8 +164,8 @@ int wwise_vorbis_get_header(STREAMFILE *streamFile, off_t offset, wwise_header_t } /* Transforms a Wwise data packet into a real Vorbis one (depending on config) */ -int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian) { - ww_stream ow, iw; +static int rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { + ww_bitstream ow, iw; int rc, granulepos; size_t header_size, packet_size; @@ -79,7 +173,7 @@ int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *str uint8_t ibuf[0x8000]; /* Wwise setup packet buffer */ if (obufsize < ibufsize) goto fail; /* arbitrary expected min */ - header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, &granulepos, &packet_size, big_endian); + header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian); if (!header_size || packet_size > obufsize) goto fail; /* load Wwise data into internal buffer */ @@ -95,7 +189,7 @@ int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *str iw.bufsize = ibufsize; iw.b_off = 0; - rc = generate_vorbis_packet(&ow,&iw, streamFile,offset, data, big_endian); + rc = ww2ogg_generate_vorbis_packet(&ow,&iw, streamFile,offset, data, big_endian); if (!rc) goto fail; if (ow.b_off % 8 != 0) { @@ -111,8 +205,8 @@ fail: /* Transforms a Wwise setup packet into a real Vorbis one (depending on config). */ -int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian, int channels) { - ww_stream ow, iw; +static int rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian, int channels) { + ww_bitstream ow, iw; int rc, granulepos; size_t header_size, packet_size; @@ -121,7 +215,7 @@ int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *stre if (obufsize < ibufsize) goto fail; /* arbitrary expected min */ /* read Wwise packet header */ - header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, &granulepos, &packet_size, big_endian); + header_size = get_packet_header(streamFile, offset, data->config.header_type, &granulepos, &packet_size, big_endian); if (!header_size || packet_size > ibufsize) goto fail; /* load Wwise setup into internal buffer */ @@ -137,7 +231,7 @@ int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *stre iw.bufsize = ibufsize; iw.b_off = 0; - rc = generate_vorbis_setup(&ow,&iw, data, channels, packet_size, streamFile); + rc = ww2ogg_generate_vorbis_setup(&ow,&iw, data, channels, packet_size, streamFile); if (!rc) goto fail; if (ow.b_off % 8 != 0) { @@ -151,11 +245,47 @@ fail: return 0; } +static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp) { + int bytes = 0x1e; + uint8_t blocksizes; + + if (bytes > bufsize) return 0; + + blocksizes = (blocksize_0_exp << 4) | (blocksize_1_exp); + + 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 build_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; +} /* **************************************************************************** */ /* INTERNAL WW2OGG STUFF */ /* **************************************************************************** */ -/* The following code was mostly converted from hcs's ww2ogg. +/* The following code was mostly and manually converted from hcs's ww2ogg. * Could be simplified but roughly tries to preserve the structure in case fixes have to be backported. * * Some validations are ommited (ex. read/write), as incorrect data should be rejected by libvorbis. @@ -165,11 +295,11 @@ fail: /* Copy packet as-is or rebuild first byte if mod_packets is used. * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-720004.3) */ -static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian) { +static int ww2ogg_generate_vorbis_packet(ww_bitstream * ow, ww_bitstream * iw, STREAMFILE *streamFile, off_t offset, vorbis_custom_codec_data * data, int big_endian) { int i,granule; size_t header_size, packet_size, data_size; - header_size = wwise_vorbis_get_header(streamFile,offset, data->header_type, &granule, &packet_size, big_endian); + header_size = get_packet_header(streamFile,offset, data->config.header_type, &granule, &packet_size, big_endian); if (!header_size || packet_size > iw->bufsize) goto fail; data_size = get_streamfile_size(streamFile);//todo get external data_size @@ -183,7 +313,7 @@ static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *st //VGM_ASSERT(granule < 0, "Wwise Vorbis: negative granule %i @ 0x%lx\n", granule, offset); - if (data->packet_type == MODIFIED) { + if (data->config.packet_type == MODIFIED) { /* rebuild first bits of packet type and window info (for the i-MDCT) */ uint32_t packet_type = 0, mode_number = 0, remainder = 0; @@ -213,14 +343,14 @@ static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *st size_t next_header_size, next_packet_size; int next_granule; - next_header_size = wwise_vorbis_get_header(streamFile,next_offset, data->header_type, &next_granule, &next_packet_size, big_endian); + next_header_size = get_packet_header(streamFile,next_offset, data->config.header_type, &next_granule, &next_packet_size, big_endian); if (!next_header_size) goto fail; if (next_packet_size > 0) { /* get next first byte to read next_mode_number */ uint32_t next_mode_number; uint8_t nbuf[1]; - ww_stream nw; + ww_bitstream nw; nw.buf = nbuf; nw.bufsize = 1; nw.b_off = 0; @@ -229,7 +359,7 @@ static int generate_vorbis_packet(ww_stream * ow, ww_stream * iw, STREAMFILE *st goto fail; r_bits(&nw, data->mode_bits,&next_mode_number); /* max 6b */ - + next_blockflag = data->mode_blockflag[next_mode_number]; } } @@ -279,7 +409,7 @@ fail: /* Rebuild a Wwise setup (simplified with removed stuff), recreating all six setup parts. * (ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-650004.2.4) */ -static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile) { +static int ww2ogg_generate_vorbis_setup(ww_bitstream * ow, ww_bitstream * iw, vorbis_custom_codec_data * data, int channels, size_t packet_size, STREAMFILE *streamFile) { int i,j,k; uint32_t codebook_count = 0, floor_count = 0, residue_count = 0; uint32_t codebook_count_less1 = 0; @@ -297,16 +427,16 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da w_bits(ow, 8, codebook_count_less1); codebook_count = codebook_count_less1 + 1; - if (data->setup_type == FULL_SETUP) { + if (data->config.setup_type == FULL_SETUP) { /* rebuild Wwise codebooks: untouched */ for (i = 0; i < codebook_count; i++) { - if(!codebook_library_copy(ow, iw)) goto fail; + if(!ww2ogg_codebook_library_copy(ow, iw)) goto fail; } } - else if (data->setup_type == INLINE_CODEBOOKS) { + else if (data->config.setup_type == INLINE_CODEBOOKS) { /* rebuild Wwise codebooks: inline in simplified format */ for (i = 0; i < codebook_count; i++) { - if(!codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail; + if(!ww2ogg_codebook_library_rebuild(ow, iw, 0, streamFile)) goto fail; } } else { @@ -317,7 +447,7 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da r_bits(iw, 10,&codebook_id); - rc = codebook_library_rebuild_by_id(ow, codebook_id, data->setup_type, streamFile); + rc = ww2ogg_codebook_library_rebuild_by_id(ow, codebook_id, data->config.setup_type, streamFile); if (!rc) goto fail; } } @@ -330,7 +460,7 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da w_bits(ow, 16, dummy_time_value); - if (data->setup_type == FULL_SETUP) { + if (data->config.setup_type == FULL_SETUP) { /* rest of setup is untouched, copy bits */ uint32_t bitly = 0; uint32_t total_bits_read = iw->b_off; @@ -553,8 +683,8 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da for (j = 0; j < coupling_steps; j++) { uint32_t magnitude = 0, angle = 0; - int magnitude_bits = tremor_ilog(channels-1); - int angle_bits = tremor_ilog(channels-1); + int magnitude_bits = ww2ogg_tremor_ilog(channels-1); + int angle_bits = ww2ogg_tremor_ilog(channels-1); r_bits(iw, magnitude_bits,&magnitude); w_bits(ow, magnitude_bits, magnitude); @@ -619,7 +749,7 @@ static int generate_vorbis_setup(ww_stream * ow, ww_stream * iw, vorbis_codec_da mode_count = mode_count_less1 + 1; memset(data->mode_blockflag, 0, sizeof(uint8_t)*(64+1)); /* up to max mode_count */ - data->mode_bits = tremor_ilog(mode_count-1); /* for mod_packets */ + data->mode_bits = ww2ogg_tremor_ilog(mode_count-1); /* for mod_packets */ for (i = 0; i < mode_count; i++) { uint32_t block_flag = 0, windowtype = 0, transformtype = 0, mapping = 0; @@ -668,7 +798,7 @@ fail: /* copies Vorbis codebooks (untouched, but size uncertain) */ -static int codebook_library_copy(ww_stream * ow, ww_stream * iw) { +static int ww2ogg_codebook_library_copy(ww_bitstream * ow, ww_bitstream * iw) { int i; uint32_t id = 0, dimensions = 0, entries = 0; uint32_t ordered = 0, lookup_type = 0; @@ -697,8 +827,8 @@ static int codebook_library_copy(ww_stream * ow, ww_stream * iw) { current_entry = 0; while (current_entry < entries) { uint32_t number = 0; - int number_bits = tremor_ilog(entries-current_entry); - + int number_bits = ww2ogg_tremor_ilog(entries-current_entry); + r_bits(iw, number_bits,&number); w_bits(ow, number_bits, number); current_entry += number; @@ -758,7 +888,7 @@ static int codebook_library_copy(ww_stream * ow, ww_stream * iw) { r_bits(iw, 1,&sequence_flag); w_bits(ow, 1, sequence_flag); - quantvals = tremor_book_maptype1_quantvals(entries, dimensions); + quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); for (i = 0; i < quantvals; i++) { uint32_t val = 0, val_bits = 0; val_bits = value_length+1; @@ -784,7 +914,7 @@ fail: /* rebuilds a Wwise codebook into a Vorbis codebook */ -static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_size, STREAMFILE *streamFile) { +static int ww2ogg_codebook_library_rebuild(ww_bitstream * ow, ww_bitstream * iw, size_t cb_size, STREAMFILE *streamFile) { int i; uint32_t id = 0, dimensions = 0, entries = 0; uint32_t ordered = 0, lookup_type = 0; @@ -809,8 +939,8 @@ static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_si current_entry = 0; while (current_entry < entries) { uint32_t number = 0; - int number_bits = tremor_ilog(entries-current_entry); - + int number_bits = ww2ogg_tremor_ilog(entries-current_entry); + r_bits(iw, number_bits,&number); w_bits(ow, number_bits, number); current_entry += number; @@ -876,7 +1006,7 @@ static int codebook_library_rebuild(ww_stream * ow, ww_stream * iw, size_t cb_si r_bits(iw, 1,&sequence_flag); w_bits(ow, 1, sequence_flag); - quantvals = tremor_book_maptype1_quantvals(entries, dimensions); + quantvals = ww2ogg_tremor_book_maptype1_quantvals(entries, dimensions); for (i = 0; i < quantvals; i++) { uint32_t val = 0, val_bits = 0; val_bits = value_length+1; @@ -908,11 +1038,11 @@ fail: } /* rebuilds an external Wwise codebook referenced by id to a Vorbis codebook */ -static int codebook_library_rebuild_by_id(ww_stream * ow, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile) { +static int ww2ogg_codebook_library_rebuild_by_id(ww_bitstream * ow, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { size_t ibufsize = 0x8000; /* arbitrary max size of a codebook */ uint8_t ibuf[0x8000]; /* Wwise codebook buffer */ size_t cb_size; - ww_stream iw; + ww_bitstream iw; cb_size = load_wvc(ibuf,ibufsize, codebook_id, setup_type, streamFile); if (cb_size == 0) goto fail; @@ -921,14 +1051,14 @@ static int codebook_library_rebuild_by_id(ww_stream * ow, uint32_t codebook_id, iw.bufsize = ibufsize; iw.b_off = 0; - return codebook_library_rebuild(ow, &iw, cb_size, streamFile); + return ww2ogg_codebook_library_rebuild(ow, &iw, cb_size, streamFile); fail: return 0; } /* fixed-point ilog from Xiph's Tremor */ -static int tremor_ilog(unsigned int v) { +static int ww2ogg_tremor_ilog(unsigned int v) { int ret=0; while(v){ ret++; @@ -937,9 +1067,9 @@ static int tremor_ilog(unsigned int v) { return(ret); } /* quantvals-something from Xiph's Tremor */ -static unsigned int tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) { +static unsigned int ww2ogg_tremor_book_maptype1_quantvals(unsigned int entries, unsigned int dimensions) { /* get us a starting hint, we'll polish it below */ - int bits=tremor_ilog(entries); + int bits=ww2ogg_tremor_ilog(entries); int vals=entries>>((bits-1)*(dimensions-1)/dimensions); while(1){ @@ -968,7 +1098,7 @@ static unsigned int tremor_book_maptype1_quantvals(unsigned int entries, unsigne /* **************************************************************************** */ /* loads an external Wwise Vorbis Codebooks file (wvc) referenced by ID and returns size */ -static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_type setup_type, STREAMFILE *streamFile) { +static int load_wvc(uint8_t * ibuf, size_t ibufsize, uint32_t codebook_id, wwise_setup_t setup_type, STREAMFILE *streamFile) { size_t bytes; /* try to load from external file (ignoring type, just use file if found) */ @@ -1038,7 +1168,7 @@ fail: return 0; } -static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_type setup_type) { +static int load_wvc_array(uint8_t * buf, size_t bufsize, uint32_t codebook_id, wwise_setup_t setup_type) { #if WWISE_VORBIS_USE_PRECOMPILED_WVC /* get pointer to array */ @@ -1102,7 +1232,7 @@ fail: /* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. * (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */ -static int r_bits(ww_stream * iw, int num_bits, uint32_t * value) { +static int r_bits(ww_bitstream * iw, int num_bits, uint32_t * value) { off_t off, pos; int i, bit_buf, bit_val; if (num_bits == 0) return 1; @@ -1133,7 +1263,7 @@ fail: /* Write bits (max 32) to buf and update the bit offset. Vorbis packs values in LSB order and byte by byte. * (ex. writing 1101011010 from b_off 2 we get 01101011 00001101 (value split, and 11 in the first byte skipped)*/ -static int w_bits(ww_stream * ow, int num_bits, uint32_t value) { +static int w_bits(ww_bitstream * ow, int num_bits, uint32_t value) { off_t off, pos; int i, bit_val, bit_buf; if (num_bits == 0) return 1; diff --git a/src/coding/wwise_vorbis_decoder.c b/src/coding/wwise_vorbis_decoder.c deleted file mode 100644 index ef62fa62..00000000 --- a/src/coding/wwise_vorbis_decoder.c +++ /dev/null @@ -1,305 +0,0 @@ -#include "coding.h" -#include - -#ifdef VGM_USE_VORBIS -#include - -#include "wwise_vorbis_utils.h" - - -#define WWISE_VORBIS_DEFAULT_BUFFER_SIZE 0x8000 /* should be at least the size of the setup header, ~0x2000 */ - -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); - -/** - * Inits a raw Wwise 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. - * - * Wwise stores a reduced setup, and the raw vorbis packets have mini headers with the block size, - * and vorbis packets themselves may reduced as well. The format evolved over time so there are some variations. - * The Wwise implementation uses Tremor (fixed-point Vorbis) but shouldn't matter. - * - * Format reverse engineered by hcs in ww2ogg (https://github.com/hcs64/ww2ogg). - * Info from the official docs (https://www.xiph.org/vorbis/doc/libvorbis/overview.html). - */ -vorbis_codec_data * init_wwise_vorbis_codec_data(STREAMFILE *streamFile, off_t start_offset, int channels, int sample_rate, int blocksize_0_exp, int blocksize_1_exp, - wwise_setup_type setup_type, wwise_header_type header_type, wwise_packet_type packet_type, int big_endian) { - vorbis_codec_data * data = NULL; - size_t header_size, packet_size; - - /* init stuff */ - data = calloc(1,sizeof(vorbis_codec_data)); - if (!data) goto fail; - - data->buffer_size = WWISE_VORBIS_DEFAULT_BUFFER_SIZE; - data->buffer = calloc(sizeof(uint8_t), data->buffer_size); - if (!data->buffer) goto fail; - - data->setup_type = setup_type; - data->header_type = header_type; - data->packet_type = packet_type; - - /* 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 */ - - if (setup_type == HEADER_TRIAD) { - /* read 3 Wwise packets with triad (id/comment/setup), each with a Wwise header */ - off_t offset = start_offset; - - /* normal identificacion packet */ - header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - offset += header_size + packet_size; - - /* normal comment packet */ - header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ - offset += header_size + packet_size; - - /* normal setup packet */ - header_size = wwise_vorbis_get_header(streamFile, offset, data->header_type, (int*)&data->op.granulepos, &packet_size, big_endian); - if (!header_size || packet_size > data->buffer_size) goto fail; - data->op.bytes = read_streamfile(data->buffer,offset+header_size,packet_size, streamFile); - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ - offset += header_size + packet_size; - } - else { - /* rebuild headers */ - - /* new identificacion packet */ - data->op.bytes = vorbis_make_header_identification(data->buffer, data->buffer_size, channels, sample_rate, blocksize_0_exp, blocksize_1_exp); - if (!data->op.bytes) goto fail; - if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ - - /* new comment packet */ - 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 */ - - /* rebuild setup packet */ - data->op.bytes = wwise_vorbis_rebuild_setup(data->buffer, data->buffer_size, streamFile, start_offset, data, big_endian, channels); - 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_wwise_vorbis(data); - return NULL; -} - -/** - * Decodes raw Wwise Vorbis - */ -void decode_wwise_vorbis(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { - 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; - pcm_convert_float_to_16(data, outbuf + samples_done * channels, samples_to_get, pcm); - samples_done += samples_to_get; - } - - /* 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; - size_t header_size, packet_size; - - data->op.packetno++; /* not actually needed, but feels nicer */ - - /* reconstruct a Wwise packet, if needed; final bytes may be bigger than packet_size so we get the header offsets here */ - header_size = wwise_vorbis_get_header(stream->streamfile, stream->offset, data->header_type, (int*)&data->op.granulepos, &packet_size, vgmstream->codec_endian); - if (!header_size || packet_size > data->buffer_size) { - VGM_LOG("Wwise Vorbis: wrong packet (0x%x) @ %lx\n", packet_size, stream->offset); - goto decode_fail; - } - - data->op.bytes = wwise_vorbis_rebuild_packet(data->buffer, data->buffer_size, stream->streamfile,stream->offset, data, vgmstream->codec_endian); - stream->offset += header_size + packet_size; /* update first to avoid infinite loops */ - if (!data->op.bytes || data->op.bytes >= 0xFFFF) { - VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset); - goto decode_fail; - } - - /* parse the fake ogg packet into a logical vorbis block */ - rc = vorbis_synthesis(&data->vb,&data->op); - if (rc == OV_ENOTAUDIO) { - VGM_LOG("Wwise Vorbis: not audio packet @ %lx\n",stream->offset); - continue; /* bad packet parsing */ - } else if (rc != 0) { - VGM_LOG("Wwise Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset); - goto decode_fail; - } - - /* finally decode the logical block into samples */ - rc = vorbis_synthesis_blockin(&data->vd,&data->vb); - if (rc != 0) { - VGM_LOG("Wwise Vorbis: cannot decode Vorbis block @ %lx\n",stream->offset); - 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)); -} - -/* *************************************************** */ - -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_0_exp, int blocksize_1_exp) { - int bytes = 0x1e; - uint8_t blocksizes; - - if (bytes > bufsize) return 0; - - blocksizes = (blocksize_0_exp << 4) | (blocksize_1_exp); - - 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; -} - -/* *************************************** */ - -void free_wwise_vorbis(vorbis_codec_data * data) { - 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); -} - -void reset_wwise_vorbis(VGMSTREAM *vgmstream) { - 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 = 0; -} - -void seek_wwise_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { - 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; - if (vgmstream->loop_ch) /* this func is only using for looping though */ - vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; -} - -#endif diff --git a/src/coding/wwise_vorbis_utils.h b/src/coding/wwise_vorbis_utils.h deleted file mode 100644 index a0156c41..00000000 --- a/src/coding/wwise_vorbis_utils.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _WWISE_VORBIS_UTILS_H_ -#define _WWISE_VORBIS_UTILS_H_ - -#include "coding.h" - -int wwise_vorbis_get_header(STREAMFILE *streamFile, off_t offset, wwise_header_type header_type, int * granulepos, size_t * packet_size, int big_endian); -int wwise_vorbis_rebuild_packet(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian); -int wwise_vorbis_rebuild_setup(uint8_t * obuf, size_t obufsize, STREAMFILE *streamFile, off_t offset, vorbis_codec_data * data, int big_endian, int channels); - - -#endif/*_WWISE_VORBIS_UTILS_H_ */ diff --git a/src/formats.c b/src/formats.c index 48c70d24..7e34ff74 100644 --- a/src/formats.c +++ b/src/formats.c @@ -465,21 +465,13 @@ static const coding_info coding_info_list[] = { #ifdef VGM_USE_VORBIS {coding_ogg_vorbis, "Ogg Vorbis"}, - {coding_fsb_vorbis, "FSB Vorbis"}, - {coding_wwise_vorbis, "Wwise Vorbis"}, - {coding_ogl_vorbis, "OGL Vorbis"}, + {coding_VORBIS_custom, "Custom Vorbis"}, #endif #ifdef VGM_USE_MPEG - {coding_fake_MPEG2_L2, "MPEG-2 Layer II Audio"}, - {coding_MPEG1_L1, "MPEG-1 Layer I Audio"}, - {coding_MPEG1_L2, "MPEG-1 Layer II Audio"}, - {coding_MPEG1_L3, "MPEG-1 Layer III Audio (MP3)"}, - {coding_MPEG2_L1, "MPEG-2 Layer I Audio"}, - {coding_MPEG2_L2, "MPEG-2 Layer II Audio"}, - {coding_MPEG2_L3, "MPEG-2 Layer III Audio (MP3)"}, - {coding_MPEG25_L1, "MPEG-2.5 Layer I Audio"}, - {coding_MPEG25_L2, "MPEG-2.5 Layer II Audio"}, - {coding_MPEG25_L3, "MPEG-2.5 Layer III Audio (MP3)"}, + {coding_MPEG_custom, "Custom MPEG Audio"}, + {coding_MPEG_layer1, "MPEG Layer I Audio (MP1)"}, + {coding_MPEG_layer2, "MPEG Layer II Audio (MP2)"}, + {coding_MPEG_layer3, "MPEG Layer III Audio (MP3)"}, #endif #ifdef VGM_USE_G7221 {coding_G7221, "ITU G.722.1 (Polycom Siren 7)"}, @@ -538,8 +530,7 @@ static const layout_info layout_info_list[] = { {layout_ogg_vorbis, "Ogg"}, #endif #ifdef VGM_USE_MPEG - {layout_fake_mpeg, "MPEG Audio stream with incorrect frame headers"}, - {layout_mpeg, "MPEG Audio stream"}, + {layout_mpeg_custom, "Custom MPEG Audio"}, #endif }; @@ -880,6 +871,8 @@ static const meta_info meta_info_list[] = { {meta_WII_04SW, "Reflections 04SW header"}, {meta_TXTH, "TXTH Generic Header"}, {meta_EA_BNK, "Electronic Arts BNK header"}, + {meta_SK_AUD, "Silicon Knights AUD header"}, + {meta_AHX, "CRI AHX header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, @@ -890,9 +883,6 @@ static const meta_info meta_info_list[] = { {meta_OGG_KOVS, "Ogg Vorbis, KOVS header"}, {meta_OGG_PSYCH, "Ogg Vorbis, Psychic Software obfuscation"}, #endif -#ifdef VGM_USE_MPEG - {meta_AHX, "CRI AHX header"}, -#endif #ifdef VGM_USE_MP4V2 {meta_MP4, "AAC header"}, #endif diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index f746b098..8c649fdc 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1297,19 +1297,31 @@ - - + + + + + + + + - - + + + + - - - - - - @@ -1454,6 +1458,26 @@ RelativePath=".\coding\xa_decoder.c" > + + + + + + + + + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 5f17ecb1..207c1551 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -416,7 +416,6 @@ - @@ -424,6 +423,8 @@ + + @@ -432,15 +433,17 @@ - + + + + + - - diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index cf07f355..67777aa7 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -790,9 +790,6 @@ coding\Source Files - - coding\Source Files - coding\Source Files @@ -811,6 +808,12 @@ coding\Source Files + + coding\Source Files + + + coding\Source Files + coding\Source Files @@ -835,9 +838,6 @@ coding\Source Files - - coding\Source Files - coding\Source Files @@ -856,10 +856,19 @@ coding\Source Files - + coding\Source Files - + + coding\Source Files + + + coding\Source Files + + + coding\Source Files + + coding\Source Files diff --git a/src/meta/ahx.c b/src/meta/ahx.c index 01503ed8..21f259a0 100644 --- a/src/meta/ahx.c +++ b/src/meta/ahx.c @@ -2,15 +2,11 @@ #include "../coding/coding.h" #include "../util.h" -#ifdef VGM_USE_MPEG - -/* AHX is a CRI format which contains an MPEG-2 Layer 2 audio stream. - * Although the MPEG frame headers are incorrect... */ +/* AHX - CRI format mainly for voices, contains MPEG-2 Layer 2 audio with lying frame headers */ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int channel_count = 1; - int loop_flag = 0; + int channel_count = 1, loop_flag = 0; /* check extension, case insensitive */ if ( !check_extensions(streamFile, "ahx") ) goto fail; @@ -19,42 +15,52 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; /* get stream offset, check for CRI signature just before */ - start_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; - if ((uint16_t)read_16bitBE(start_offset-6,streamFile)!=0x2863 ||/* "(c" */ - (uint32_t)read_32bitBE(start_offset-4,streamFile)!=0x29435249 /* ")CRI" */ - ) goto fail; + start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04; - /* check for encoding type */ - /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is - * ADX with exponential scale, 0x11 is AHX */ - /* Sappharad reports that old AHXs (Sonic Adventure 2) don't have this flag. - * When I see one perhaps I can add an exception for those. */ - if (read_8bit(4,streamFile) != 0x11) goto fail; + if ((uint16_t)read_16bitBE(start_offset-0x06,streamFile)!=0x2863 || /* "(c" */ + (uint32_t)read_32bitBE(start_offset-0x04,streamFile)!=0x29435249) /* ")CRI" */ + goto fail; + + /* check for encoding type (0x10 is AHX for DC with bigger frames, 0x11 is AHX, 0x0N are ADX) */ + if (read_8bit(0x04,streamFile) != 0x10 && + read_8bit(0x04,streamFile) != 0x11) goto fail; /* check for frame size (0 for AHX) */ - if (read_8bit(5,streamFile) != 0) goto fail; + if (read_8bit(0x05,streamFile) != 0) goto fail; /* check for bits per sample? (0 for AHX) */ - if (read_8bit(6,streamFile) != 0) goto fail; + if (read_8bit(0x06,streamFile) != 0) goto fail; - /* check channel count (only mono AHXs are known) */ - if (read_8bit(7,streamFile) != 1) goto fail; + /* check channel count (only mono AHXs can be created by the encoder) */ + if (read_8bit(0x07,streamFile) != 1) goto fail; - /* At this point we almost certainly have an AHX file, - * so let's build the VGMSTREAM. */ + /* check version signature */ + if (read_8bit(0x12,streamFile) != 0x06) 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(0xc,streamFile); - /* This is the One True Samplerate, the MPEG headers lie. */ - vgmstream->sample_rate = read_32bitBE(8,streamFile); + vgmstream->sample_rate = read_32bitBE(0x08,streamFile); /* real sample rate */ + vgmstream->num_samples = read_32bitBE(0x0c,streamFile); /* doesn't include encoder_delay (handled in decoder) */ - vgmstream->coding_type = coding_fake_MPEG2_L2; - vgmstream->layout_type = layout_fake_mpeg; vgmstream->meta_type = meta_AHX; - vgmstream->codec_data = init_mpeg_codec_data_ahx(streamFile, start_offset, channel_count); + + { +#ifdef VGM_USE_MPEG + mpeg_custom_config cfg; + + memset(&cfg, 0, sizeof(mpeg_custom_config)); + cfg.encryption = read_8bit(0x13,streamFile); /* 0x08 = keyword encryption */ + + vgmstream->layout_type = layout_none; + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg); + if (!vgmstream->codec_data) goto fail; +#else + goto fail; +#endif + } + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; @@ -65,5 +71,3 @@ fail: close_vgmstream(vgmstream); return NULL; } - -#endif diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index ce1c550b..e493813c 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -45,6 +45,7 @@ #define EA_CODEC2_XBOXADPCM 0x14 #define EA_CODEC2_MT5 0x16 #define EA_CODEC2_EALAYER3 0x17 +#define EA_CODEC2_ATRAC3PLUS 0x1B /* Medal of Honor Heroes 2 (PSP) */ #define EA_MAX_CHANNELS 6 @@ -127,7 +128,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { /* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */ if (!check_extensions(streamFile,"bnk,sdt,mus")) goto fail; - +VGM_LOG("1\n"); /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */ read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */ @@ -136,7 +137,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ else goto fail; - +VGM_LOG("2\n"); /* use header size as endianness flag */ if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) { read_32bit = read_32bitBE; @@ -151,7 +152,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { /* check multi-streams */ if (target_stream == 0) target_stream = 1; if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; - +VGM_LOG("3\n"); switch(bnk_version) { case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */ header_size = read_32bit(offset + 0x08,streamFile); /* full size */ @@ -169,10 +170,10 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { VGM_LOG("EA BNK: unknown version %x\n", bnk_version); goto fail; } - +VGM_LOG("4\n"); if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset)) goto fail; - +VGM_LOG("5\n"); /* fix absolute offsets so it works in next funcs */ if (offset) { for (i = 0; i < ea.channels; i++) { @@ -303,21 +304,39 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ #ifdef VGM_USE_MPEG case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */ case EA_CODEC2_LAYER3: { /* MPEG Layer III, aka MP3 */ + mpeg_custom_config cfg; off_t mpeg_start_offset = is_bnk ? start_offset : get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea); if (!mpeg_start_offset) goto fail; - vgmstream->codec_data = init_mpeg_codec_data_interleaved(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, 0); - if (!vgmstream->codec_data) goto fail; + memset(&cfg, 0, sizeof(mpeg_custom_config)); + /* layout is still blocks, but should work fine with the custom mpeg decoder */ + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg); + if (!vgmstream->codec_data) goto fail; + break; + } + + case EA_CODEC2_EALAYER3: { /* MP3 variant */ + mpeg_custom_config cfg; + off_t mpeg_start_offset = is_bnk ? + start_offset : + get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea); + if (!mpeg_start_offset) goto fail; + + memset(&cfg, 0, sizeof(mpeg_custom_config)); + + /* layout is still blocks, but should work fine with the custom mpeg decoder */ + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg); + if (!vgmstream->codec_data) goto fail; break; } #endif case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */ case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */ - case EA_CODEC2_EALAYER3: /* MP3 variant */ + case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */ default: VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform); goto fail; @@ -810,7 +829,7 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start block_size = read_32bitBE(block_offset+0x04,streamFile); if (id == 0x5343446C) { /* "SCDl" data block found */ - off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first channel offset is ok, MPEG channels share offsets */ + off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */ return block_offset + 0x0c + ea->channels*0x04 + offset; } else if (id == 0x5343436C) { /* "SCCl" data count found */ block_offset += block_size; /* size includes header */ diff --git a/src/meta/fsb.c b/src/meta/fsb.c index 224d249c..86f3429c 100644 --- a/src/meta/fsb.c +++ b/src/meta/fsb.c @@ -278,34 +278,25 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { if (fsbh.mode & FSOUND_MPEG) { /* FSB3: ?; FSB4: Shatter, Way of the Samurai 3/4, Forza Horizon 1/2, Dragon Age Origins */ #if defined(VGM_USE_MPEG) - mpeg_codec_data *mpeg_data = NULL; - coding_t mpeg_coding_type; - int fsb_padding = 0; + mpeg_custom_config cfg; + + memset(&cfg, 0, sizeof(mpeg_custom_config)); + cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : + (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 : + (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 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 /* needed by multichannel with no flags */ - fsb_padding = fsbh.numchannels > 2 ? 16 : 0; + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); + if (!vgmstream->codec_data) goto fail; - mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_FSB, fsb_padding); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; - vgmstream->coding_type = mpeg_coding_type; - vgmstream->layout_type = layout_mpeg; + /* both to setup initial interleave in vgmstream_open_stream */ + vgmstream->interleave_block_size = cfg.interleave; + vgmstream->layout_type = layout_mpeg_custom; - 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 */ - goto fail; #else - goto fail; + goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ #endif } else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */ diff --git a/src/meta/fsb5.c b/src/meta/fsb5.c index 29c9955b..649d2306 100644 --- a/src/meta/fsb5.c +++ b/src/meta/fsb5.c @@ -258,20 +258,17 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { #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; + mpeg_custom_config cfg; - fsb_padding = vgmstream->channels > 2 ? 16 : 4; /* observed default */ + memset(&cfg, 0, sizeof(mpeg_custom_config)); + cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */ - mpeg_data = init_mpeg_codec_data_interleaved(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels, MPEG_FSB, fsb_padding); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; - vgmstream->coding_type = mpeg_coding_type; - vgmstream->layout_type = layout_mpeg; + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); + if (!vgmstream->codec_data) goto fail; - 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 */ + /* both to setup initial interleave in vgmstream_open_stream */ + vgmstream->interleave_block_size = cfg.interleave; + vgmstream->layout_type = layout_mpeg_custom; break; } #endif @@ -286,10 +283,17 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { #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; + vorbis_custom_config cfg; + + memset(&cfg, 0, sizeof(vorbis_custom_config)); + cfg.channels = vgmstream->channels; + cfg.sample_rate = vgmstream->sample_rate; + cfg.setup_id = VorbisSetupId; + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_VORBIS_custom; + vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, StartOffset, VORBIS_FSB, &cfg); + if (!vgmstream->codec_data) goto fail; break; } diff --git a/src/meta/genh.c b/src/meta/genh.c index afb2e9e0..0849aa8a 100644 --- a/src/meta/genh.c +++ b/src/meta/genh.c @@ -70,7 +70,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case SDX2: coding = coding_SDX2; break; case DVI_IMA: coding = coding_DVI_IMA; break; #ifdef VGM_USE_MPEG - case MPEG: coding = coding_MPEG1_L3; break; /* we later find out exactly which */ + case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */ #endif case IMA: coding = coding_IMA; break; case AICA: coding = coding_AICA; break; @@ -258,8 +258,8 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { break; #ifdef VGM_USE_MPEG - case coding_MPEG1_L3: - vgmstream->layout_type = layout_mpeg; + case coding_MPEG_layer3: + vgmstream->layout_type = layout_none; vgmstream->codec_data = init_mpeg_codec_data(streamFile, start_offset, &coding, vgmstream->channels); if (!vgmstream->codec_data) goto fail; diff --git a/src/meta/meta.h b/src/meta/meta.h index 281e36cc..3b64b673 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -684,4 +684,6 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/ogl.c b/src/meta/ogl.c index af78171f..2f655b09 100644 --- a/src/meta/ogl.c +++ b/src/meta/ogl.c @@ -24,7 +24,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) { if (partial_file_size > get_streamfile_size(streamFile)) goto fail; loop_end_sample = num_samples; /* there is no data after num_samples (ie.- it's really num_samples) */ - /* this is actually peeking into the Vorbis id packet */ + /* actually peeking into the Vorbis id packet */ channel_count = read_8bit (0x21,streamFile); sample_rate = read_32bitLE(0x22,streamFile); @@ -33,7 +33,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sample_rate; + vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; @@ -41,10 +41,15 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) { #ifdef VGM_USE_VORBIS { - vgmstream->codec_data = init_ogl_vorbis_codec_data(streamFile, 0x14, &start_offset); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ogl_vorbis; + vorbis_custom_config cfg; + memset(&cfg, 0, sizeof(vorbis_custom_config)); + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_VORBIS_custom; + vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x14, VORBIS_OGL, &cfg); + if (!vgmstream->codec_data) goto fail; + + start_offset = cfg.data_start_offset; } #else goto fail; diff --git a/src/meta/p3d.c b/src/meta/p3d.c index 3978bec1..cac85b64 100644 --- a/src/meta/p3d.c +++ b/src/meta/p3d.c @@ -95,7 +95,6 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) { channel_count = read_32bitLE(parse_offset+0x10,streamFile); block_size = read_32bitLE(parse_offset+0x14,streamFile); num_samples = num_samples / channel_count; /* total samples */ - block_size = block_size * channel_count; /* seems ok? */ start_offset = parse_offset+0x18; break; @@ -141,17 +140,19 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x6D703300: { /* "mp3\0" (PS3) */ - mpeg_codec_data *mpeg_data = NULL; - coding_t mpeg_coding_type; + mpeg_custom_config cfg; - mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_P3D, 0); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; - vgmstream->layout_type = layout_mpeg; - vgmstream->coding_type = mpeg_coding_type; + memset(&cfg, 0, sizeof(mpeg_custom_config)); + cfg.interleave = 0x400; + /* block_size * 3 = frame size (0x60*3=0x120 or 0x40*3=0xC0) but doesn't seem to have any significance) */ - goto fail; //todo: not working right (unknown interleave) - //break; + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg); + if (!vgmstream->codec_data) goto fail; + + /* both to setup initial interleave in vgmstream_open_stream */ + vgmstream->interleave_block_size = cfg.interleave; + vgmstream->layout_type = layout_mpeg_custom; + break; } #endif diff --git a/src/meta/ps3_msf.c b/src/meta/ps3_msf.c index 18e52a37..86f3fb6e 100644 --- a/src/meta/ps3_msf.c +++ b/src/meta/ps3_msf.c @@ -178,7 +178,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { vgmstream->codec_data = mpeg_data; vgmstream->coding_type = ct; - vgmstream->layout_type = layout_mpeg; + vgmstream->layout_type = layout_none; vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data); vgmstream->num_samples -= vgmstream->num_samples % frame_size; if (loop_flag) { diff --git a/src/meta/ps3_xvag.c b/src/meta/ps3_xvag.c index b6d694b9..009376ee 100644 --- a/src/meta/ps3_xvag.c +++ b/src/meta/ps3_xvag.c @@ -82,22 +82,25 @@ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case 0x08: { /* MPEG: The Last of Us, Uncharted 3, Medieval Moves */ - mpeg_codec_data *mpeg_data = NULL; - coding_t mpeg_coding_type; + mpeg_custom_config cfg; int fixed_frame_size; + memset(&cfg, 0, sizeof(mpeg_custom_config)); + /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"mpin"*/ fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile); - mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_FIXED, fixed_frame_size); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; - vgmstream->layout_type = layout_mpeg; - vgmstream->coding_type = mpeg_coding_type; - vgmstream->interleave_block_size = fixed_frame_size * multiplier; + cfg.chunk_size = fixed_frame_size; + cfg.interleave = fixed_frame_size * multiplier; + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); + if (!vgmstream->codec_data) goto fail; + + /* both to setup initial interleave in vgmstream_open_stream */ + vgmstream->interleave_block_size = cfg.interleave; + vgmstream->layout_type = layout_mpeg_custom; break; } #endif diff --git a/src/meta/sk_aud.c b/src/meta/sk_aud.c new file mode 100644 index 00000000..ce3bc55f --- /dev/null +++ b/src/meta/sk_aud.c @@ -0,0 +1,75 @@ +#include "meta.h" +#include "../util.h" +#include "../coding/coding.h" + +static int get_sk_num_samples(STREAMFILE *streamFile, off_t start_offset); + +/* AUD/SK - Silicon Knights obfuscated Ogg (cutscene/voices) [Eternal Darkness (GC)] */ +VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag = 0, channel_count, sample_rate; + + /* check extension, case insensitive */ + if (!check_extensions(streamFile,"aud")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x11534B10) /* \11"SK"\10 */ + goto fail; + + /* the format is just mutant Ogg so actually peeking into the Vorbis id packet here */ + channel_count = read_8bit (0x23,streamFile); + sample_rate = read_32bitLE(0x24,streamFile); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = get_sk_num_samples(streamFile, 0); + vgmstream->meta_type = meta_SK_AUD; + +#ifdef VGM_USE_VORBIS + { + vorbis_custom_config cfg; + memset(&cfg, 0, sizeof(vorbis_custom_config)); + + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_VORBIS_custom; + vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x00, VORBIS_SK, &cfg); + if (!vgmstream->codec_data) goto fail; + + start_offset = cfg.data_start_offset; + } +#else + goto fail; +#endif + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +/* SK/Ogg doesn't have num_samples info, manually read total samples */ +static int get_sk_num_samples(STREAMFILE *streamFile, off_t start_offset) { + uint32_t expected_id = 0x11534B10; /* \11"SK"\10 (would read "OggS" by changing the ID) */ + off_t off = get_streamfile_size(streamFile) - 4-1-1-8-4-4-4; + + /* simplest way is to find last OggS/SK page from stream end */ + while (off >= start_offset) { + uint32_t current_id = read_32bitBE(off, streamFile); + if (current_id == expected_id) { /* last packet starts with 0x0004, if more checks are needed */ + return read_32bitLE(off+4+1+1, streamFile); /* get last granule = total samples (64b but whatevs) */ + } + + off--; + } + + return 0; +} diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index 17784a21..7dc008d0 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -252,7 +252,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { vgmstream->codec_data = mpeg_data; vgmstream->coding_type = ct; - vgmstream->layout_type = layout_mpeg; + vgmstream->layout_type = layout_none; vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); vgmstream->num_samples -= vgmstream->num_samples%576; if (loop_flag) { diff --git a/src/meta/txth.c b/src/meta/txth.c index a7b83bd4..d3080f28 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -127,7 +127,7 @@ found: case SDX2: coding = coding_SDX2; break; case DVI_IMA: coding = coding_DVI_IMA; break; #ifdef VGM_USE_MPEG - case MPEG: coding = coding_MPEG1_L3; break; /* we later find out exactly which */ + case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */ #endif case IMA: coding = coding_IMA; break; case AICA: coding = coding_AICA; break; @@ -272,8 +272,8 @@ found: break; #ifdef VGM_USE_MPEG - case coding_MPEG1_L3: - vgmstream->layout_type = layout_mpeg; + case coding_MPEG_layer3: + vgmstream->layout_type = layout_none; vgmstream->codec_data = init_mpeg_codec_data(streamFile, txth.start_offset, &coding, vgmstream->channels); if (!vgmstream->codec_data) goto fail; diff --git a/src/meta/ubi_raki.c b/src/meta/ubi_raki.c index b0b8859a..5c3304f8 100644 --- a/src/meta/ubi_raki.c +++ b/src/meta/ubi_raki.c @@ -125,7 +125,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { /* chunks: "MARK" optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */ vgmstream->codec_data = init_mpeg_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); if (!vgmstream->codec_data) goto fail; - vgmstream->layout_type = layout_mpeg; + vgmstream->layout_type = layout_none; vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data); break; diff --git a/src/meta/wwise.c b/src/meta/wwise.c index 3a732ab5..173b8eb5 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -214,11 +214,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* Wwise uses custom Vorbis, which changed over time (config must be detected to pass to the decoder). */ off_t vorb_offset, data_offsets, block_offsets; size_t vorb_size, setup_offset, audio_offset; + vorbis_custom_config cfg; - wwise_setup_type setup_type; - wwise_header_type header_type; - wwise_packet_type packet_type; - int blocksize_0_exp = 0, blocksize_1_exp = 0; + memset(&cfg, 0, sizeof(vorbis_custom_config)); + cfg.channels = ww.channels; + cfg.sample_rate = ww.sample_rate; + cfg.big_endian = ww.big_endian; if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */ @@ -231,25 +232,25 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest PC */ data_offsets = 0x18; block_offsets = 0; /* no need, full headers are present */ - header_type = TYPE_8; - packet_type = STANDARD; - setup_type = HEADER_TRIAD; + cfg.header_type = TYPE_8; + cfg.packet_type = STANDARD; + cfg.setup_type = HEADER_TRIAD; break; //case 0x32: /* ? */ case 0x34: /* common (2010~2011) */ data_offsets = 0x18; block_offsets = 0x30; - header_type = TYPE_6; - packet_type = STANDARD; - setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ + cfg.header_type = TYPE_6; + cfg.packet_type = STANDARD; + cfg.setup_type = EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */ break; case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */ data_offsets = 0x10; block_offsets = 0x28; - header_type = TYPE_2; - packet_type = MODIFIED; - setup_type = EXTERNAL_CODEBOOKS; + cfg.header_type = TYPE_2; + cfg.packet_type = MODIFIED; + cfg.setup_type = EXTERNAL_CODEBOOKS; break; default: VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size); @@ -260,8 +261,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { setup_offset = read_32bit(vorb_offset + data_offsets + 0x00, streamFile); /* within data (0 = no seek table) */ audio_offset = read_32bit(vorb_offset + data_offsets + 0x04, streamFile); /* within data */ if (block_offsets) { - blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, streamFile); /* small */ - blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, streamFile); /* big */ + cfg.blocksize_1_exp = read_8bit(vorb_offset + block_offsets + 0x00, streamFile); /* small */ + cfg.blocksize_0_exp = read_8bit(vorb_offset + block_offsets + 0x01, streamFile); /* big */ } /* detect setup type: @@ -274,16 +275,15 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { /* if the setup after header starts with "(data)BCV" it's an inline codebook) */ if ((id & 0x00FFFFFF) == 0x00424356) { /* 0"BCV" */ - setup_type = FULL_SETUP; + cfg.setup_type = FULL_SETUP; } /* if the setup is suspiciously big it's probably trimmed inline codebooks */ else if (setup_size > 0x200) { /* an external setup it's ~0x100 max + some threshold */ - setup_type = INLINE_CODEBOOKS; + cfg.setup_type = INLINE_CODEBOOKS; } } - vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset + setup_offset, ww.channels, ww.sample_rate, blocksize_0_exp,blocksize_1_exp, - setup_type,header_type,packet_type, ww.big_endian); + vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } else { @@ -295,13 +295,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { case 0x30: data_offsets = 0x10; block_offsets = 0x28; - header_type = TYPE_2; - packet_type = MODIFIED; + cfg.header_type = TYPE_2; + cfg.packet_type = MODIFIED; /* setup not detectable by header, so we'll try both; hopefully libvorbis will reject wrong codebooks * - standard: early (<2012), ex. The King of Fighters XIII X360 (2011/11), .ogg (cbs are from aoTuV, too) * - aoTuV603: later (>2012), ex. Sonic & All-Stars Racing Transformed PC (2012/11), .wem */ - setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ + cfg.setup_type = is_wem ? AOTUV603_CODEBOOKS : EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */ break; //case 0x2a: /* Rocksmith 2011 X360? */ @@ -312,34 +312,32 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { } vgmstream->num_samples = read_32bit(extra_offset + 0x00, streamFile); - setup_offset = read_32bit(extra_offset + data_offsets + 0x00, streamFile); /* within data*/ - audio_offset = read_32bit(extra_offset + data_offsets + 0x04, streamFile); /* within data */ - blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, streamFile); /* small */ - blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, streamFile); /* big */ + setup_offset = read_32bit(extra_offset + data_offsets + 0x00, streamFile); /* within data */ + audio_offset = read_32bit(extra_offset + data_offsets + 0x04, streamFile); /* within data */ + cfg.blocksize_1_exp = read_8bit(extra_offset + block_offsets + 0x00, streamFile); /* small */ + cfg.blocksize_0_exp = read_8bit(extra_offset + block_offsets + 0x01, streamFile); /* big */ /* Normal packets are used rarely (ex. Oddworld New 'n' Tasty! PSV). They are hard to detect (decoding * will mostly work with garbage results) but we'll try. Setup size and "fmt" bitrate fields may matter too. */ if (ww.extra_size == 0x30) { /* all blocksizes I've seen are 0x08+0x0B except Oddworld PSV, that uses 0x09+0x09 * (maybe lower spec machines = needs simpler packets) */ - if (blocksize_0_exp == blocksize_1_exp) - packet_type = STANDARD; + if (cfg.blocksize_0_exp == cfg.blocksize_1_exp) + cfg.packet_type = STANDARD; } /* try with the selected codebooks */ - vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset + setup_offset, ww.channels, ww.sample_rate, blocksize_0_exp,blocksize_1_exp, - setup_type,header_type,packet_type, ww.big_endian); + vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) { /* codebooks failed: try again with the other type */ - setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS; - vgmstream->codec_data = init_wwise_vorbis_codec_data(streamFile, start_offset + setup_offset, ww.channels, ww.sample_rate, blocksize_0_exp,blocksize_1_exp, - setup_type,header_type,packet_type, ww.big_endian); + cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS; + vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg); if (!vgmstream->codec_data) goto fail; } } - vgmstream->coding_type = coding_wwise_vorbis; vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_VORBIS_custom; vgmstream->codec_endian = ww.big_endian; start_offset = start_offset + audio_offset; diff --git a/src/vgmstream.c b/src/vgmstream.c index 6d3084b4..a585b050 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -84,9 +84,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_aifc, init_vgmstream_str_snds, init_vgmstream_ws_aud, -#ifdef VGM_USE_MPEG init_vgmstream_ahx, -#endif init_vgmstream_ivb, init_vgmstream_amts, init_vgmstream_svs, @@ -370,6 +368,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_wii_04sw, init_vgmstream_ea_bnk, init_vgmstream_ea_schl_fixed, + init_vgmstream_sk_aud, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG @@ -494,16 +493,8 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { reset_ogg_vorbis(vgmstream); } - if (vgmstream->coding_type==coding_fsb_vorbis) { - reset_fsb_vorbis(vgmstream); - } - - if (vgmstream->coding_type==coding_wwise_vorbis) { - reset_wwise_vorbis(vgmstream); - } - - if (vgmstream->coding_type==coding_ogl_vorbis) { - reset_ogl_vorbis(vgmstream); + if (vgmstream->coding_type==coding_VORBIS_custom) { + reset_vorbis_custom(vgmstream); } #endif @@ -518,8 +509,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_MPEG - if (vgmstream->layout_type==layout_mpeg || - vgmstream->layout_type==layout_fake_mpeg) { + if (vgmstream->coding_type==coding_MPEG_custom || + vgmstream->coding_type==coding_MPEG_layer1 || + vgmstream->coding_type==coding_MPEG_layer2 || + vgmstream->coding_type==coding_MPEG_layer3) { reset_mpeg(vgmstream); } #endif @@ -678,18 +671,8 @@ 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; - } - - if (vgmstream->coding_type==coding_wwise_vorbis) { - free_wwise_vorbis(vgmstream->codec_data); - vgmstream->codec_data = NULL; - } - - if (vgmstream->coding_type==coding_ogl_vorbis) { - free_ogl_vorbis(vgmstream->codec_data); + if (vgmstream->coding_type==coding_VORBIS_custom) { + free_vorbis_custom(vgmstream->codec_data); vgmstream->codec_data = NULL; } #endif @@ -714,8 +697,10 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_MPEG - if (vgmstream->layout_type==layout_fake_mpeg || - vgmstream->layout_type==layout_mpeg) { + if (vgmstream->coding_type==coding_MPEG_custom || + vgmstream->coding_type==coding_MPEG_layer1 || + vgmstream->coding_type==coding_MPEG_layer2 || + vgmstream->coding_type==coding_MPEG_layer3) { free_mpeg(vgmstream->codec_data); vgmstream->codec_data = NULL; } @@ -911,8 +896,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_ogg_vorbis: #endif #ifdef VGM_USE_MPEG - case layout_fake_mpeg: - case layout_mpeg: + case layout_mpeg_custom: #endif case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); @@ -991,21 +975,13 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_ULAW: #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: - case coding_fsb_vorbis: - case coding_wwise_vorbis: - case coding_ogl_vorbis: + case coding_VORBIS_custom: #endif #ifdef VGM_USE_MPEG - case coding_fake_MPEG2_L2: - case coding_MPEG1_L1: - case coding_MPEG1_L2: - case coding_MPEG1_L3: - case coding_MPEG2_L1: - case coding_MPEG2_L2: - case coding_MPEG2_L3: - case coding_MPEG25_L1: - case coding_MPEG25_L2: - case coding_MPEG25_L3: + case coding_MPEG_custom: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: #endif case coding_SDX2: case coding_SDX2_int: @@ -1526,20 +1502,8 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels); break; - case coding_fsb_vorbis: - decode_fsb_vorbis(vgmstream, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); - break; - - case coding_wwise_vorbis: - decode_wwise_vorbis(vgmstream, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); - break; - - case coding_ogl_vorbis: - decode_ogl_vorbis(vgmstream, + case coding_VORBIS_custom: + decode_vorbis_custom(vgmstream, buffer+samples_written*vgmstream->channels,samples_to_do, vgmstream->channels); break; @@ -1667,21 +1631,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; #ifdef VGM_USE_MPEG - case coding_fake_MPEG2_L2: - decode_fake_mpeg2_l2( - &vgmstream->ch[0], - vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do); - break; - case coding_MPEG1_L1: - case coding_MPEG1_L2: - case coding_MPEG1_L3: - case coding_MPEG2_L1: - case coding_MPEG2_L2: - case coding_MPEG2_L3: - case coding_MPEG25_L1: - case coding_MPEG25_L2: - case coding_MPEG25_L3: + case coding_MPEG_custom: + case coding_MPEG_layer1: + case coding_MPEG_layer2: + case coding_MPEG_layer3: decode_mpeg( vgmstream, buffer+samples_written*vgmstream->channels, @@ -1889,16 +1842,8 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { seek_ogg_vorbis(vgmstream, vgmstream->loop_sample); } - if (vgmstream->coding_type==coding_fsb_vorbis) { - seek_fsb_vorbis(vgmstream, vgmstream->loop_start_sample); - } - - if (vgmstream->coding_type==coding_wwise_vorbis) { - seek_wwise_vorbis(vgmstream, vgmstream->loop_start_sample); - } - - if (vgmstream->coding_type==coding_ogl_vorbis) { - seek_ogl_vorbis(vgmstream, vgmstream->loop_start_sample); + if (vgmstream->coding_type==coding_VORBIS_custom) { + seek_vorbis_custom(vgmstream, vgmstream->loop_start_sample); } #endif @@ -1921,8 +1866,11 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { #endif #ifdef VGM_USE_MPEG - if (vgmstream->layout_type==layout_mpeg) { - seek_mpeg(vgmstream, vgmstream->loop_sample); /* won't work for fake MPEG */ + if (vgmstream->coding_type==coding_MPEG_custom || + vgmstream->coding_type==coding_MPEG_layer1 || + vgmstream->coding_type==coding_MPEG_layer2 || + vgmstream->coding_type==coding_MPEG_layer3) { + seek_mpeg(vgmstream, vgmstream->loop_sample); } #endif diff --git a/src/vgmstream.h b/src/vgmstream.h index feec43eb..13b07501 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -158,24 +158,15 @@ typedef enum { coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */ #ifdef VGM_USE_VORBIS - coding_ogg_vorbis, /* Xiph Vorbis (MDCT-based) */ - coding_fsb_vorbis, /* FMOD Vorbis without Ogg layer */ - coding_wwise_vorbis, /* Audiokinetic Vorbis without Ogg layer */ - coding_ogl_vorbis, /* Shin'en Vorbis without Ogg layer */ + coding_ogg_vorbis, /* Xiph Vorbis with Ogg layer (MDCT-based) */ + coding_VORBIS_custom, /* Xiph Vorbis with custom layer (MDCT-based) */ #endif #ifdef VGM_USE_MPEG - coding_fake_MPEG2_L2, /* MPEG-2 Layer 2 (AHX), with lying headers */ - /* MPEG audio variations (depending on sample rate and other config) */ - coding_MPEG1_L1, /* MP1 */ - coding_MPEG1_L2, /* MP2 */ - coding_MPEG1_L3, /* good ol' MPEG-1 Layer 3 (MP3) */ - coding_MPEG2_L1, - coding_MPEG2_L2, - coding_MPEG2_L3, - coding_MPEG25_L1, - coding_MPEG25_L2, - coding_MPEG25_L3, + coding_MPEG_custom, /* MPEG audio with custom features (MDCT-based) */ + coding_MPEG_layer1, /* MP1 MPEG audio (MDCT-based) */ + coding_MPEG_layer2, /* MP2 MPEG audio (MDCT-based) */ + coding_MPEG_layer3, /* MP3 MPEG audio (MDCT-based) */ #endif #ifdef VGM_USE_G7221 @@ -251,8 +242,7 @@ typedef enum { layout_ogg_vorbis, /* ogg vorbis file */ #endif #ifdef VGM_USE_MPEG - layout_fake_mpeg, /* MPEG audio stream with bad frame headers (AHX) */ - layout_mpeg, /* proper MPEG audio stream */ + layout_mpeg_custom, /* usually straight data but may setup offset/interleave somehow */ #endif } layout_t; @@ -494,8 +484,8 @@ typedef enum { meta_MUS_ACM, /* MUS playlist of InterPlay ACM files */ meta_DE2, /* Falcom (Gurumin) .de2 */ meta_VS, /* Men in Black .vs */ - meta_FFXI_BGW, /* FFXI BGW */ - meta_FFXI_SPW, /* FFXI SPW */ + meta_FFXI_BGW, /* FFXI (PC) BGW */ + meta_FFXI_SPW, /* FFXI (PC) SPW */ meta_STS_WII, /* Shikigami No Shiro 3 STS Audio File */ meta_PS2_P2BT, /* Pop'n'Music 7 Audio File */ meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */ @@ -564,7 +554,7 @@ typedef enum { meta_PS3_MSF, /* MSF header */ meta_NUB_VAG, /* Namco VAG from NUB archives */ meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ - meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ + meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ meta_NGCA, /* GoldenEye 007 (Wii) */ meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */ @@ -599,8 +589,8 @@ typedef enum { meta_FSTM, // Nintendo Wii U FSTM meta_3DS_IDSP, // Nintendo 3DS/Wii U IDSP meta_KT_WIIBGM, // Koei Tecmo WiiBGM - meta_MCA, // Capcom MCA "MADP" - meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX + meta_MCA, /* Capcom MCA "MADP" */ + meta_XB3D_ADX, /* Xenoblade Chronicles 3D ADX */ meta_HCA, /* CRI HCA */ meta_PS2_SVAG_SNK, /* SNK PS2 SVAG */ meta_PS2_VDS_VDM, /* Graffiti Kingdom */ @@ -623,6 +613,8 @@ typedef enum { meta_PC_XA30, /* Driver - Parallel Lines (PC) */ meta_WII_04SW, /* Driver - Parallel Lines (Wii) */ meta_TXTH, /* generic text header */ + meta_SK_AUD, /* Silicon Knights .AUD (Eternal Darkness GC) */ + meta_AHX, /* CRI AHX header */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ @@ -633,9 +625,6 @@ typedef enum { meta_OGG_KOVS, /* Ogg Vorbis with exta header and 0x100 bytes XOR */ meta_OGG_PSYCH, /* Ogg Vorbis with all bytes -0x23*/ #endif -#ifdef VGM_USE_MPEG - meta_AHX, /* CRI AHX header (same structure as ADX) */ -#endif #ifdef VGM_USE_MP4V2 meta_MP4, /* AAC (iOS) */ #endif @@ -799,12 +788,41 @@ typedef struct { ogg_vorbis_streamfile ov_streamfile; } ogg_vorbis_codec_data; -/* config for Wwise Vorbis */ -typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_type; -typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_type; -typedef enum { STANDARD, MODIFIED } wwise_packet_type; -/* any raw Vorbis without Ogg layer */ +/* custom Vorbis modes */ +typedef enum { + VORBIS_FSB, /* simplified/external setup packets, custom packet headers */ + VORBIS_WWISE, /* many variations (custom setup, headers and data) */ + VORBIS_OGL, /* custom packet headers */ + VORBIS_SK /* "OggS" replaced by "SK" */ +} vorbis_custom_t; + +/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ +typedef enum { HEADER_TRIAD, FULL_SETUP, INLINE_CODEBOOKS, EXTERNAL_CODEBOOKS, AOTUV603_CODEBOOKS } wwise_setup_t; /* Vorbis setup style */ +typedef enum { TYPE_8, TYPE_6, TYPE_2 } wwise_header_t; /* size of packet headers */ +typedef enum { STANDARD, MODIFIED } wwise_packet_t; /* type of Vorbis packets */ + +typedef struct { + /* to reconstruct init packets */ + int channels; + int sample_rate; + int blocksize_0_exp; + int blocksize_1_exp; + + uint32_t setup_id; /* external setup */ + int big_endian; /* flag */ + + /* Wwise Vorbis config */ + wwise_setup_t setup_type; + wwise_header_t header_type; + wwise_packet_t packet_type; + + /* output (kinda ugly here but to simplify) */ + off_t data_start_offset; + +} vorbis_custom_config; + +/* custom Vorbis without Ogg layer */ typedef struct { vorbis_info vi; /* stream settings */ vorbis_comment vc; /* stream comments */ @@ -817,52 +835,75 @@ typedef struct { size_t samples_to_discard; /* for looping purposes */ int samples_full; /* flag, samples available in vorbis buffers */ - /* Wwise Vorbis config */ - wwise_setup_type setup_type; - wwise_header_type header_type; - wwise_packet_type packet_type; - /* saved data to reconstruct modified packets */ + vorbis_custom_t type; /* Vorbis subtype */ + vorbis_custom_config config; /* config depending on the mode */ + + /* Wwise Vorbis: saved data to reconstruct modified packets */ uint8_t mode_blockflag[64+1]; /* max 6b+1; flags 'n stuff */ int mode_bits; /* bits to store mode_number */ uint8_t prev_blockflag; /* blockflag in the last decoded packet */ + /* Ogg-style Vorbis: packet within a page */ + int current_packet; -} vorbis_codec_data; +} vorbis_custom_codec_data; #endif #ifdef VGM_USE_MPEG -typedef enum { /*MPEG_NONE,*/ MPEG_FIXED, MPEG_FSB, MPEG_P3D, MPEG_EA } mpeg_interleave_type; + +/* Custom MPEG modes, mostly differing in the data layout */ +typedef enum { + MPEG_STANDARD, /* 1 stream */ + MPEG_AHX, /* 1 stream with false frame headers */ + MPEG_XVAG, /* N streams of fixed interleave (frame-aligned, several data-frames of fixed size) */ + MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */ + MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */ + MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */ + MPEG_EAL31, /* EALayer3 v1, custom frames */ + MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), altered custom frames */ + MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), altered custom frames */ + MPEG_LYN, /* N streams of fixed interleave */ + MPEG_AWC /* N streams in absolute offsets (consecutive) */ +} mpeg_custom_t; + typedef struct { - uint8_t *buffer; /* raw (coded) data buffer */ + int channels; /* max channels */ + int fsb_padding; /* fsb padding mode */ + int chunk_size; /* size of a data portion */ + int interleave; /* size of stream interleave */ + int encryption; /* encryption mode */ +} mpeg_custom_config; + +typedef struct { + uint8_t *buffer; /* internal raw data buffer */ size_t buffer_size; size_t bytes_in_buffer; int buffer_full; /* raw buffer has been filled */ int buffer_used; /* raw buffer has been fed to the decoder */ - mpg123_handle *m; /* "base" MPEG stream */ + mpg123_handle *m; /* regular/single MPEG decoder */ - /* base values, assumed to be constant in the file */ - int sample_rate_per_frame; + /* for internal use, assumed to be constant for all frames */ int channels_per_frame; - size_t samples_per_frame; + int samples_per_frame; - size_t samples_to_discard; /* for interleaved looping */ + /* custom MPEG internals */ + int custom; /* flag */ + mpeg_custom_t type; /* mpeg subtype */ + mpeg_custom_config config; /* config depending on the mode */ - /* interleaved MPEG internals */ - int interleaved; - mpeg_interleave_type interleave_type; /* flag */ - uint32_t interleave_value; /* varies with type */ - - mpg123_handle **ms; /* array of MPEG streams */ + mpg123_handle **ms; /* custom MPEG decoder array */ size_t ms_size; - uint8_t *frame_buffer; /* temp buffer with samples from a single decoded frame */ - size_t frame_buffer_size; - uint8_t *interleave_buffer; /* intermediate buffer with samples from all channels */ - size_t interleave_buffer_size; - size_t bytes_in_interleave_buffer; - size_t bytes_used_in_interleave_buffer; - size_t current_frame_size; - size_t current_padding; /* FSB padding between frames */ + uint8_t *stream_buffer; /* contains samples from N frames of one stream */ + size_t stream_buffer_size; + uint8_t *sample_buffer; /* contains samples from N frames of all streams/channels */ + size_t sample_buffer_size; + size_t bytes_in_sample_buffer; + size_t bytes_used_in_sample_buffer; + + + size_t samples_to_discard; /* for custom mpeg looping */ + size_t skip_samples; /* base encoder delay */ } mpeg_codec_data; #endif