From 7875090f0fbf28e1e8d3cb2657697a4e803d95eb Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 27 Aug 2017 21:54:43 +0200 Subject: [PATCH 01/10] Improve PCMFLOAT --- src/coding/pcm_decoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coding/pcm_decoder.c b/src/coding/pcm_decoder.c index db3914e2..ed676748 100644 --- a/src/coding/pcm_decoder.c +++ b/src/coding/pcm_decoder.c @@ -124,11 +124,11 @@ void decode_pcmfloat(VGMSTREAM *vgmstream, VGMSTREAMCHANNEL * stream, sample * o for (i=first_sample,sample_count=0; ioffset+i*4,stream->streamfile); - float sample_float; + float* sample_float; int sample_pcm; - memcpy(&sample_float, &sample_int, 4); /* maybe unorthodox but simplest */ - sample_pcm = floor(sample_float * 32767.f + .5f); + sample_float = (float*)&sample_int; + sample_pcm = floor((*sample_float) * 32767.f + .5f); outbuf[sample_count] = clamp16(sample_pcm); } From 5d12d6fb67fb55ae59463b3d865923b6691296da Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 27 Aug 2017 22:17:13 +0200 Subject: [PATCH 02/10] Add big endian mode to coding_PCM16LE_int and rename to coding_PCM16_int --- src/coding/coding.h | 4 ++-- src/coding/pcm_decoder.c | 12 ++++++------ src/formats.c | 4 ++-- src/layout/ea_block.c | 2 +- src/meta/ea_old.c | 2 +- src/meta/his.c | 2 +- src/meta/pc_sob.c | 2 +- src/meta/sqex_scd.c | 2 +- src/vgmstream.c | 24 +++++++++++++----------- src/vgmstream.h | 2 +- 10 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 36addb8d..2dc72d39 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -54,16 +54,16 @@ void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci /* pcm_decoder */ void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16LE_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcmfloat(VGMSTREAM *vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ diff --git a/src/coding/pcm_decoder.c b/src/coding/pcm_decoder.c index ed676748..ea350ae3 100644 --- a/src/coding/pcm_decoder.c +++ b/src/coding/pcm_decoder.c @@ -68,12 +68,12 @@ void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channe } } -void decode_pcm16LE_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; - int32_t sample_count; +void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { + int i, sample_count; + int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile); + outbuf[sample_count]=read_16bit(stream->offset+i*2*channelspacing,stream->streamfile); } } @@ -118,9 +118,9 @@ void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, } } -void decode_pcmfloat(VGMSTREAM *vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { int i, sample_count; - int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; + int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; for (i=first_sample,sample_count=0; ioffset+i*4,stream->streamfile); diff --git a/src/formats.c b/src/formats.c index f716a17f..64dba691 100644 --- a/src/formats.c +++ b/src/formats.c @@ -395,10 +395,10 @@ typedef struct { static const coding_info coding_info_list[] = { - {coding_PCM16BE, "Big Endian 16-bit PCM"}, {coding_PCM16LE, "Little Endian 16-bit PCM"}, - {coding_PCM16LE_int, "Little Endian 16-bit PCM with 2 byte interleave"}, {coding_PCM16LE_XOR_int, "Little Endian 16-bit PCM with 2 byte interleave and XOR obfuscation"}, + {coding_PCM16BE, "Big Endian 16-bit PCM"}, + {coding_PCM16_int, "16-bit PCM with 2 byte interleave"}, {coding_PCM8, "8-bit PCM"}, {coding_PCM8_U, "8-bit unsigned PCM"}, {coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave"}, diff --git a/src/layout/ea_block.c b/src/layout/ea_block.c index a7a46b9e..244aad6d 100644 --- a/src/layout/ea_block.c +++ b/src/layout/ea_block.c @@ -211,7 +211,7 @@ void eacs_block_update(off_t block_offset, VGMSTREAM * vgmstream) { } else { for (i=0;ichannels;i++) { - if(vgmstream->coding_type==coding_PCM16LE_int) + if(vgmstream->coding_type==coding_PCM16_int) vgmstream->ch[i].offset = block_offset+(i*2); else vgmstream->ch[i].offset = block_offset+i; diff --git a/src/meta/ea_old.c b/src/meta/ea_old.c index 8e40a596..d32878be 100644 --- a/src/meta/ea_old.c +++ b/src/meta/ea_old.c @@ -61,7 +61,7 @@ VGMSTREAM * init_vgmstream_eacs(STREAMFILE *streamFile) { vgmstream->sample_rate = ea_header->dwSampleRate; if(ea_header->bCompression==0) { - vgmstream->coding_type = coding_PCM16LE_int; + vgmstream->coding_type = coding_PCM16_int; if(ea_header->bBits==1) vgmstream->coding_type = coding_PCM8_int; } diff --git a/src/meta/his.c b/src/meta/his.c index 63e2b2c2..9ed5d078 100644 --- a/src/meta/his.c +++ b/src/meta/his.c @@ -68,7 +68,7 @@ VGMSTREAM * init_vgmstream_his(STREAMFILE *streamFile) { vgmstream->coding_type = coding_PCM16LE; if (channel_count == 2) { - vgmstream->coding_type = coding_PCM16LE_int; + vgmstream->coding_type = coding_PCM16_int; vgmstream->interleave_block_size = 2; } } diff --git a/src/meta/pc_sob.c b/src/meta/pc_sob.c index d6a5d729..3754a2c2 100644 --- a/src/meta/pc_sob.c +++ b/src/meta/pc_sob.c @@ -53,7 +53,7 @@ VGMSTREAM * init_vgmstream_sab(STREAMFILE *streamFile) { vgmstream->current_block_offset=8+32*numSounds; vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x20,streamFile); - vgmstream->coding_type = coding_PCM16LE_int; + vgmstream->coding_type = coding_PCM16_int; vgmstream->num_samples = (int32_t)((get_streamfile_size(streamFile)-vgmstream->current_block_offset)/2/channel_count); if(loop_flag) { diff --git a/src/meta/sqex_scd.c b/src/meta/sqex_scd.c index 09f68bc2..a9560de4 100644 --- a/src/meta/sqex_scd.c +++ b/src/meta/sqex_scd.c @@ -225,7 +225,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { switch (codec_id) { case 0x1: /* PCM */ - vgmstream->coding_type = coding_PCM16LE_int; + vgmstream->coding_type = coding_PCM16_int; vgmstream->layout_type = layout_none; vgmstream->num_samples = stream_size / 2 / channel_count; diff --git a/src/vgmstream.c b/src/vgmstream.c index 72c3fb11..17ab6077 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -968,9 +968,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_NGC_DSP: return 14; case coding_PCM16LE: - case coding_PCM16LE_int: case coding_PCM16LE_XOR_int: case coding_PCM16BE: + case coding_PCM16_int: case coding_PCM8: case coding_PCM8_U: case coding_PCM8_int: @@ -1124,9 +1124,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_NGC_DSP: return 8; case coding_PCM16LE: - case coding_PCM16LE_int: case coding_PCM16LE_XOR_int: case coding_PCM16BE: + case coding_PCM16_int: return 2; case coding_PCM8: case coding_PCM8_U: @@ -1306,13 +1306,6 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; - case coding_PCM16LE_int: - for (chan=0;chanchannels;chan++) { - decode_pcm16LE_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; case coding_PCM16LE_XOR_int: for (chan=0;chanchannels;chan++) { decode_pcm16LE_XOR_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1327,6 +1320,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; + case coding_PCM16_int: + for (chan=0;chanchannels;chan++) { + decode_pcm16_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do, + vgmstream->codec_endian); + } + break; case coding_PCM8: for (chan=0;chanchannels;chan++) { decode_pcm8(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1371,9 +1372,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; case coding_PCMFLOAT: for (chan=0;chanchannels;chan++) { - decode_pcmfloat(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + decode_pcmfloat(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + samples_to_do, + vgmstream->codec_endian); } break; diff --git a/src/vgmstream.h b/src/vgmstream.h index 5bcd50ea..ba45cb54 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -74,9 +74,9 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ typedef enum { /* PCM */ coding_PCM16LE, /* little endian 16-bit PCM */ - coding_PCM16LE_int, /* little endian 16-bit PCM with sample-level interleave */ coding_PCM16LE_XOR_int, /* little endian 16-bit PCM with sample-level xor */ coding_PCM16BE, /* big endian 16-bit PCM */ + coding_PCM16_int, /* 16-bit PCM with sample-level interleave */ coding_PCM8, /* 8-bit PCM */ coding_PCM8_int, /* 8-Bit PCM with sample-level interleave */ From 5bf0ddbfac551a51be57c2dd240b8789b9e6732f Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 27 Aug 2017 22:18:08 +0200 Subject: [PATCH 03/10] Fix RWS PCM16BE [The Legend of Spyro (X360)] --- src/meta/rws.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/meta/rws.c b/src/meta/rws.c index b5db46f9..fee8a170 100644 --- a/src/meta/rws.c +++ b/src/meta/rws.c @@ -160,9 +160,10 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { switch(codec) { case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */ - /* The Legend of Spyro (X360) */ - vgmstream->coding_type = coding_PCM16BE; - //vgmstream->interleave_block_size = block_size / 2; //0x2; //todo 2ch PCM not working correctly (interleaved PCM not ok?) + /* ex. The Legend of Spyro (X360) */ + vgmstream->coding_type = coding_PCM16_int; + vgmstream->codec_endian = 1; /* big */ + vgmstream->interleave_block_size = 0x02; /* only used to setup channels */ vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); break; From c4a6e6e194b2ae824dcecb64e018f082b892211f Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 15:11:52 +0200 Subject: [PATCH 04/10] Add read_64bitBE/LE for Rockstar AWC They use it encode 3 values in 64b and save one byte in the process, whee. --- src/streamfile.h | 12 ++++++++++++ src/util.h | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/src/streamfile.h b/src/streamfile.h index bae85afc..22b94742 100644 --- a/src/streamfile.h +++ b/src/streamfile.h @@ -134,6 +134,18 @@ static inline int32_t read_32bitBE(off_t offset, STREAMFILE * streamfile) { if (read_streamfile(buf,offset,4,streamfile)!=4) return -1; return get_32bitBE(buf); } +static inline int64_t read_64bitLE(off_t offset, STREAMFILE * streamfile) { + uint8_t buf[8]; + + if (read_streamfile(buf,offset,8,streamfile)!=8) return -1; + return get_64bitLE(buf); +} +static inline int64_t read_64bitBE(off_t offset, STREAMFILE * streamfile) { + uint8_t buf[8]; + + if (read_streamfile(buf,offset,8,streamfile)!=8) return -1; + return get_64bitBE(buf); +} static inline int8_t read_8bit(off_t offset, STREAMFILE * streamfile) { uint8_t buf[1]; diff --git a/src/util.h b/src/util.h index ef95e52a..43f4f1f3 100644 --- a/src/util.h +++ b/src/util.h @@ -25,6 +25,14 @@ static inline int32_t get_32bitLE(uint8_t * p) { return (p[0]) | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); } +static inline int64_t get_64bitBE(uint8_t * p) { + return (uint64_t)(((uint64_t)p[0]<<56) | ((uint64_t)p[1]<<48) | ((uint64_t)p[2]<<40) | ((uint64_t)p[3]<<32) | ((uint64_t)p[4]<<24) | ((uint64_t)p[5]<<16) | ((uint64_t)p[6]<<8) | ((uint64_t)p[7])); +} + +static inline int64_t get_64bitLE(uint8_t * p) { + return (uint64_t)(((uint64_t)p[0]) | ((uint64_t)p[1]<<8) | ((uint64_t)p[2]<<16) | ((uint64_t)p[3]<<24) | ((uint64_t)p[4]<<32) | ((uint64_t)p[5]<<40) | ((uint64_t)p[6]<<48) | ((uint64_t)p[7]<<56)); +} + void put_8bit(uint8_t * buf, int8_t i); void put_16bitLE(uint8_t * buf, int16_t i); From 953022b9832f3f4c2b2069ff050b7a23b6dba88f Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 15:14:24 +0200 Subject: [PATCH 05/10] Add AWC MP3 [Red Dead Redemption (PS3), GTA5 (PS3)] --- fb2k/foo_filetypes.h | 1 + readme.txt | 2 +- src/coding/mpeg_custom_utils.c | 1 - src/coding/mpeg_custom_utils_awc.c | 138 ++++++++++++ src/coding/mpeg_custom_utils_ealayer3.c | 2 +- src/coding/mpeg_decoder.c | 16 +- src/coding/mpeg_decoder.h | 2 + src/formats.c | 2 + src/libvgmstream.vcproj | 4 + src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/awc.c | 272 ++++++++++++++++++++++++ src/meta/meta.h | 2 + src/vgmstream.c | 1 + src/vgmstream.h | 10 +- 15 files changed, 445 insertions(+), 12 deletions(-) create mode 100644 src/coding/mpeg_custom_utils_awc.c create mode 100644 src/meta/awc.c diff --git a/fb2k/foo_filetypes.h b/fb2k/foo_filetypes.h index 11a2622a..4c21659b 100644 --- a/fb2k/foo_filetypes.h +++ b/fb2k/foo_filetypes.h @@ -49,6 +49,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("AST", ast); VGMSTREAM_DECLARE_FILE_TYPE("ATRAC3plus", at3); VGMSTREAM_DECLARE_FILE_TYPE("AUD", aud); VGMSTREAM_DECLARE_FILE_TYPE("AUS", aus); +VGMSTREAM_DECLARE_FILE_TYPE("AWC", awc); VGMSTREAM_DECLARE_FILE_TYPE("B1S", b1s); VGMSTREAM_DECLARE_FILE_TYPE("BAF", baf); diff --git a/readme.txt b/readme.txt index 6e6ee560..28a9809c 100644 --- a/readme.txt +++ b/readme.txt @@ -63,7 +63,7 @@ supports Winamp plugins you may also use in_vgmstream.dll instead. Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream exts, you need to manually fix it by going to options > plugins > input > vgmstream -and in the "priority filetypes" put: ckd,fsb,genh,msf,p3d,rak,scd,xvag +and in the "priority filetypes" put: awc,ckd,fsb,genh,msf,p3d,rak,scd,xvag --- foo_input_vgmstream --- Every should be installed automatically by the .fb2k-component bundle. diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c index 05c5a6cb..ae1f244c 100644 --- a/src/coding/mpeg_custom_utils.c +++ b/src/coding/mpeg_custom_utils.c @@ -54,7 +54,6 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m break; case MPEG_LYN: - case MPEG_AWC: goto fail; /* not fully implemented */ case MPEG_STANDARD: diff --git a/src/coding/mpeg_custom_utils_awc.c b/src/coding/mpeg_custom_utils_awc.c new file mode 100644 index 00000000..02629710 --- /dev/null +++ b/src/coding/mpeg_custom_utils_awc.c @@ -0,0 +1,138 @@ +#include "mpeg_decoder.h" + +#ifdef VGM_USE_MPEG + +/** + * AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames + * per block, so it's unsuitable for the normal "blocked" layout and parsed here instead. + * Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes + * the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). + */ + + +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) { + size_t header_size = 0; + int i; + int entries = data->config.channels; + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; + + for (i = 0; i < entries; i++) { + header_size += 0x18; + header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */ + } + + if (header_size % 0x800) /* padded */ + header_size += 0x800 - (header_size % 0x800); + + return header_size; +} + + +/* init config and validate */ +int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) { + mpeg_frame_info info; + int is_music; + + /* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */ + is_music = read_32bitBE(start_offset, streamFile) == 0x00000000; + if (is_music) + start_offset += get_block_header_size(streamFile, start_offset, data); + + /* 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 */ + if (is_music) { + if (data->config.chunk_size <= 0) + goto fail; /* needs block size */ + } + + /* no big encoder delay added (for sfx they can start in less than ~300 samples) */ + + return 1; +fail: + return 0; +} + + +/* writes data to the buffer and moves offsets, parsing AWC blocks */ +int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) { + mpeg_custom_stream *ms = data->streams[num_stream]; + mpeg_frame_info info; + size_t current_data_size = 0, data_offset; + size_t file_size = get_streamfile_size(stream->streamfile); + int i; + + + /* blocked layout used for music */ + if (data->config.chunk_size) { + + /* block ended for this channel, move to next block start */ + if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) { + //mpg123_open_feed(ms->m); //todo reset maybe needed? + + data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */ + data_offset -= data_offset % data->config.chunk_size; /* start of current block */ + stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size; + + ms->current_size_count = 0; + ms->current_size_target = 0; + } + + /* just in case, shouldn't happen */ + if (stream->offset >= file_size) { + goto fail; + } + + /* block starts for this channel, point to mpeg data */ + if (ms->current_size_count == 0) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE; + off_t channel_offset = 0; + + /* block has a header with base info per channel and table per channel (see blocked_awc.c) */ + ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile); + ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile); + + for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */ + size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile); + if (channel_size % 0x10) /* 32b aligned */ + channel_size += 0x10 - channel_size % 0x10; + + channel_offset += channel_size; + } + + //VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset); + //;VGM_LOG("AWC: s%i off=%lx to %lx\n", num_stream, stream->offset, stream->offset + channel_offset + get_block_header_size(stream->streamfile, stream->offset, data)); + + stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data); + } + } + + + /* update frame */ + if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) ) + goto fail; + current_data_size = info.frame_size; + + data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile); + + stream->offset += current_data_size; + + ms->current_size_count += current_data_size; + + return 1; +fail: + return 0; +} + +#endif diff --git a/src/coding/mpeg_custom_utils_ealayer3.c b/src/coding/mpeg_custom_utils_ealayer3.c index 4f00ce52..b233b3bf 100644 --- a/src/coding/mpeg_custom_utils_ealayer3.c +++ b/src/coding/mpeg_custom_utils_ealayer3.c @@ -579,7 +579,7 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d if (decode_to_discard == 576) decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number; - data->decode_to_discard += decode_to_discard; + ms->decode_to_discard += decode_to_discard; } } diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index b28e385c..0d9e62c6 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -140,6 +140,7 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start case MPEG_EAL31: case MPEG_EAL32P: case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break; + case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break; default: ok = mpeg_custom_setup_init_default(streamFile, start_offset, data, coding_type); break; } if (!ok) @@ -393,6 +394,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data case MPEG_EAL32P: case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break; case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break; + case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break; default: ok = mpeg_custom_parse_frame_default(stream, data); break; } if (!ok) { @@ -440,16 +442,16 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data } samples_filled = (bytes_done / sizeof(sample) / data->channels_per_frame); - /* for EALayer3, that discards decoded samples and writes PCM blocks instead */ - if (data->decode_to_discard) { + /* discard for weird features (EALayer3 and PCM blocks, AWC and repeated frames) */ + if (ms->decode_to_discard) { size_t bytes_to_discard = 0; - size_t decode_to_discard = data->decode_to_discard; + size_t decode_to_discard = ms->decode_to_discard; if (decode_to_discard > samples_filled) decode_to_discard = samples_filled; bytes_to_discard = sizeof(sample)*decode_to_discard*data->channels_per_frame; bytes_done -= bytes_to_discard; - data->decode_to_discard -= decode_to_discard; + ms->decode_to_discard -= decode_to_discard; ms->samples_used += decode_to_discard; } @@ -522,10 +524,10 @@ void reset_mpeg(VGMSTREAM *vgmstream) { mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset); data->streams[i]->samples_filled = 0; data->streams[i]->samples_used = 0; + data->streams[i]->decode_to_discard = 0; } data->samples_to_discard = data->skip_samples; /* initial delay */ - data->decode_to_discard = 0; } } @@ -546,6 +548,7 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { mpg123_feedseek(data->streams[i]->m,0,SEEK_SET,&input_offset); data->streams[i]->samples_filled = 0; data->streams[i]->samples_used = 0; + data->streams[i]->decode_to_discard = 0; if (vgmstream->loop_ch) vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset; @@ -558,7 +561,6 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { data->buffer_full = 0; data->buffer_used = 0; - data->decode_to_discard = 0; } /* resets mpg123 decoder and its internals (with mpg123_open_feed as mpg123_feedseek won't work) */ @@ -577,10 +579,10 @@ void flush_mpeg(mpeg_codec_data * data) { mpg123_open_feed(data->streams[i]->m); data->streams[i]->samples_filled = 0; data->streams[i]->samples_used = 0; + data->streams[i]->decode_to_discard = 0; } data->samples_to_discard = data->skip_samples; /* initial delay */ - data->decode_to_discard = 0; } data->bytes_in_buffer = 0; diff --git a/src/coding/mpeg_decoder.h b/src/coding/mpeg_decoder.h index 465fd2c9..d6a0a678 100644 --- a/src/coding/mpeg_decoder.h +++ b/src/coding/mpeg_decoder.h @@ -19,10 +19,12 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_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_setup_init_awc(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, int num_stream); +int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream); #endif/* VGM_USE_MPEG */ diff --git a/src/formats.c b/src/formats.c index 64dba691..88d3b267 100644 --- a/src/formats.c +++ b/src/formats.c @@ -42,6 +42,7 @@ static const char* extension_list[] = { "at3", "aud", "aus", + "awc", "b1s", "baf", @@ -880,6 +881,7 @@ static const meta_info meta_info_list[] = { {meta_STM, "Angel Studios/Rockstar San Diego STMA header"}, {meta_BINK, "RAD Game Tools Bink header"}, {meta_EA_SNU, "Electronic Arts SNU header"}, + {meta_AWC, "Rockstar AWC header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 40b2d004..7c5a09c3 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1398,6 +1398,10 @@ RelativePath=".\coding\mpeg_custom_utils_ahx.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 5ee9e7f2..14d74eae 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -427,6 +427,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index f3a30769..f760f38b 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -817,6 +817,9 @@ coding\Source Files + + coding\Source Files + coding\Source Files diff --git a/src/meta/awc.c b/src/meta/awc.c new file mode 100644 index 00000000..02728f01 --- /dev/null +++ b/src/meta/awc.c @@ -0,0 +1,272 @@ +#include "meta.h" +#include "../coding/coding.h" + +typedef struct { + int big_endian; + int is_encrypted; + int is_music; + + int total_streams; + + int channel_count; + int sample_rate; + int codec; + int num_samples; + + int block_chunk; + + off_t stream_offset; + off_t stream_size; + +} awc_header; + +static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc); + + +/* AWC - from RAGE (Rockstar Advanced Game Engine) audio (Red Dead Redemption, Max Payne 3, GTA5) */ +VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + awc_header awc; + + /* check extension */ + if (!check_extensions(streamFile,"awc")) + goto fail; + + /* check header */ + if (!parse_awc_header(streamFile, &awc)) + goto fail; + + if (awc.is_encrypted) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(awc.channel_count, 0); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = awc.sample_rate; + vgmstream->num_samples = awc.num_samples; + vgmstream->num_streams = awc.total_streams; + vgmstream->meta_type = meta_AWC; + + + switch(awc.codec) { + //case 0x01: /* PCM (PC/PS3) */ + // vgmstream->coding_type = coding_PCM!6; + // vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none; + // break; + + //case 0x04: /* IMA (PC) */ + // vgmstream->coding_type = coding_AWC_IMA; + // vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none; + // break; + + +#ifdef VGM_USE_MPEG + case 0x07: { /* MPEG (PS3) */ + mpeg_custom_config cfg; + memset(&cfg, 0, sizeof(mpeg_custom_config)); + + cfg.chunk_size = awc.block_chunk; + cfg.big_endian = awc.big_endian; + + vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + break; + } +#endif + + case 0x05: /* XMA2 (X360) */ + default: + VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec); + goto fail; + } + + + /* open files; channel offsets are updated below */ + if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset)) + goto fail; + + //if (vgmstream->layout_type == layout_blocked_awc) + // update_ + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv). + * Made of entries for N streams, each with a number of tags pointing to chunks (header, data, events, etc). */ +static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) { + int64_t (*read_64bit)(off_t,STREAMFILE*) = NULL; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + int i, ch, entries; + uint32_t flags, info_header, tag_count = 0, tags_skip = 0; + off_t off; + int target_stream = streamFile->stream_index; + + memset(awc,0,sizeof(awc_header)); + + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x41444154 && /* "ADAT" (LE) */ + read_32bitBE(0x00,streamFile) != 0x54414441) /* "TADA" (BE) */ + goto fail; + + awc->big_endian = read_32bitBE(0x00,streamFile) == 0x54414441; + if (awc->big_endian) { + read_64bit = read_64bitBE; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_64bit = read_64bitLE; + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + + flags = read_32bit(0x04,streamFile); + entries = read_32bit(0x08,streamFile); + //header_size = read_32bit(0x0c,streamFile); /* after to stream id/tags, not including chunks */ + + off = 0x10; + + if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) { + VGM_LOG("AWC: unknown flags 0x%08x\n", flags); + goto fail; + } + + if (flags & 0x00010000) /* some kind of mini offset table */ + off += 0x2 * entries; + //if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */ + // ... + //if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */ + // awc->is_music = 1; + if (flags & 0x00080000) /* encrypted data chunk (most of GTA5 PC) */ + awc->is_encrypted = 1; + + + /* Music when the first id is 0 (base/fake entry with info for all channels), sfx pack otherwise. + * sfx = N single streams, music = N-1 interleaved mono channels (even for MP3/XMA). + * Music seems layered (N-1/2 stereo pairs), maybe set with events? */ + awc->is_music = (read_32bit(off + 0x00,streamFile) & 0x1FFFFFFF) == 0x00000000; + if (awc->is_music) { /* all streams except id 0 is a channel */ + awc->total_streams = 1; + target_stream = 1; /* we only need id 0, though channels may have its own tags/chunks */ + } + else { /* each stream is a single sound */ + awc->total_streams = entries; + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || target_stream > awc->total_streams || awc->total_streams < 1) goto fail; + } + + + /* get stream base info */ + for (i = 0; i < entries; i++) { + info_header = read_32bit(off + 0x04*i, streamFile); + tag_count = (info_header >> 29) & 0x7; /* 3b */ + //id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ + if (target_stream-1 == i) + break; + tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ + } + off += 0x04*entries; + off += 0x08*tags_skip; + + /* get stream tags */ + for (i = 0; i < tag_count; i++) { + uint64_t tag_header, tag, size, offset; + + tag_header = (uint64_t)read_64bit(off + 0x08*i,streamFile); + tag = (tag_header >> 56) & 0xFF; /* 8b */ + size = (tag_header >> 28) & 0x0FFFFFFF; /* 28b */ + offset = (tag_header >> 0) & 0x0FFFFFFF; /* 28b */ + + /* Tags are apparently part of a hash derived from a word ("data", "format", etc). + * If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */ + switch(tag) { + case 0x55: /* data */ + awc->stream_offset = offset; + awc->stream_size = size; + break; + + case 0x48: /* music header */ + if (!awc->is_music) { + VGM_LOG("AWC: music header found in sfx\n"); + goto fail; + } + + /* 0x00(32): unknown (some count?) */ + awc->block_chunk = read_32bit(offset + 0x04,streamFile); + awc->channel_count = read_32bit(offset + 0x08,streamFile); + + if (awc->channel_count != entries - 1) { /* not counting id-0 */ + VGM_LOG("AWC: number of music channels doesn't match entries\n"); + goto fail; + } + + for (ch = 0; ch < awc->channel_count; ch++) { + int num_samples, sample_rate, codec; + /* 0x00(32): stream id (not always in the header entries order) */ + /* 0x08(16): headroom?, 0x0d(8): round size?, 0x0e(16): unknown (zero?) */ + num_samples = read_32bit(offset + 0x0c + 0x10*ch + 0x04,streamFile); + sample_rate = (uint16_t)read_16bit(offset + 0x0c + 0x10*ch + 0x0a,streamFile); + codec = read_8bit(offset + 0x0c + 0x10*ch + 0x0c, streamFile); + + /* validate as all channels should repeat this */ + if ((awc->num_samples && awc->num_samples != num_samples) || + (awc->sample_rate && awc->sample_rate != sample_rate) || + (awc->codec && awc->codec != codec)) { + VGM_LOG("AWC: found header diffs between channels\n"); /* can rarely happen in stereo pairs */ + goto fail; + } + + awc->num_samples = num_samples; + awc->sample_rate = sample_rate; + awc->codec = codec; + } + break; + + case 0xFA: /* sfx header */ + if (awc->is_music) { + VGM_LOG("AWC: sfx header found in music\n"); + goto fail; + } + /* 0x04(32): -1?, 0x0a(16x4): unknown x4, 0x12: null? */ + awc->num_samples = read_32bit(offset + 0x00,streamFile); + awc->sample_rate = (uint16_t)read_16bit(offset + 0x08,streamFile); + awc->codec = read_8bit(offset + 0x13, streamFile); + awc->channel_count = 1; + break; + + case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) */ + case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */ + default: /* 0x5C=animation/RSC?, 0x68=midi?, 0x36/0x2B/0x5A/0xD9=? */ + //VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag); + break; + } + } + + if (!awc->stream_offset) { + VGM_LOG("AWC: stream offset not found\n"); + goto fail; + } + + /* If music, data is divided into blocks of block_chunk size with padding. + * Each block has a header/seek table and interleaved data for all channels */ + if (awc->is_music && read_32bit(awc->stream_offset, streamFile) != 0) { + VGM_LOG("AWC: music found, but block doesn't start with seek table\n"); + goto fail; + } + + + return 1; +fail: + return 0; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 2baecf00..f920bb11 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -684,4 +684,6 @@ VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_awc(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index 17ab6077..169ec9a7 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -367,6 +367,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_sk_aud, init_vgmstream_stm, init_vgmstream_ea_snu, + init_vgmstream_awc, init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG diff --git a/src/vgmstream.h b/src/vgmstream.h index ba45cb54..095f2094 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -621,6 +621,7 @@ typedef enum { meta_STM, /* Angel Studios/Rockstar San Diego Games */ meta_BINK, /* RAD Game Tools BINK audio/video */ meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */ + meta_AWC, /* Rockstar AWC (GTA5, RDR) */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ @@ -874,7 +875,7 @@ typedef enum { MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), custom frames with v2 header */ MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), custom frames with v2 header */ MPEG_LYN, /* N streams of fixed interleave */ - MPEG_AWC /* N streams in absolute offsets (consecutive) */ + MPEG_AWC /* N streams in block layout (music) or absolute offsets (sfx) */ } mpeg_custom_t; /* config for the above modes */ @@ -884,6 +885,7 @@ typedef struct { int chunk_size; /* size of a data portion */ int interleave; /* size of stream interleave */ int encryption; /* encryption mode */ + int big_endian; /* for AHX */ int cri_type; uint16_t cri_key1; @@ -899,6 +901,11 @@ typedef struct { size_t output_buffer_size; size_t samples_filled; /* data in the buffer (in samples) */ size_t samples_used; /* data extracted from the buffer */ + + size_t current_size_count; /* data read (if the parser needs to know) */ + size_t current_size_target; /* max data, until something happens */ + size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */ + } mpeg_custom_stream; typedef struct { @@ -924,7 +931,6 @@ typedef struct { size_t skip_samples; /* base encoder delay */ size_t samples_to_discard; /* for custom mpeg looping */ - size_t decode_to_discard; /* for EALayer3, that discards decoded samples and writes PCM blocks in their place */ } mpeg_codec_data; #endif From 41b22c26998f38e275d3de4ee36079b4c1d20039 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 16:07:09 +0200 Subject: [PATCH 06/10] Add AWC PCM/IMA (non-encrypted only) [GTA5 (PC)] --- src/coding/coding.h | 3 +- src/coding/ima_decoder.c | 37 ++++++++++++++++++++ src/formats.c | 2 ++ src/layout/blocked.c | 3 ++ src/layout/blocked_awc.c | 58 ++++++++++++++++++++++++++++++++ src/layout/layout.h | 2 ++ src/libvgmstream.vcproj | 4 +++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/awc.c | 26 ++++++++------ src/vgmstream.c | 12 +++++++ src/vgmstream.h | 2 ++ 12 files changed, 141 insertions(+), 12 deletions(-) create mode 100644 src/layout/blocked_awc.c diff --git a/src/coding/coding.h b/src/coding/coding.h index 2dc72d39..3ec4efca 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -30,7 +30,8 @@ void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * out void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t ima_bytes_to_samples(size_t bytes, int channels); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index 1f73a3aa..8901653b 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -678,6 +678,43 @@ void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou stream->adpcm_step_index = step_index; } +void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + //internal interleave, mono + int block_samples = (0x800 - 4) * 2; + first_sample = first_sample % block_samples; + + //inverted header + if (first_sample == 0) { + off_t header_offset = stream->offset; + + step_index = read_16bitLE(header_offset,stream->streamfile); + hist1 = read_16bitLE(header_offset+2,stream->streamfile); + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + } + + for (i=first_sample,sample_count=0; ioffset + 4 + i/2; + int nibble_shift = (i&1?4:0); //low nibble first + + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + //internal interleave: increment offset on complete frame + if (i == block_samples) stream->offset += 0x800; + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + + + size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { /* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels; diff --git a/src/formats.c b/src/formats.c index 88d3b267..dcac95a7 100644 --- a/src/formats.c +++ b/src/formats.c @@ -449,6 +449,7 @@ static const coding_info coding_info_list[] = { {coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"}, {coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, + {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_ACM, "InterPlay ACM"}, {coding_NWA0, "NWA DPCM Level 0"}, @@ -533,6 +534,7 @@ static const layout_info layout_info_list[] = { {layout_aax, "AAX blocked, 18-byte interleaved"}, {layout_scd_int, "SCD multistream interleave"}, {layout_ea_sns_blocked, "Electronic Arts SNS blocked"}, + {layout_blocked_awc, "blocked (AWC)"}, #ifdef VGM_USE_VORBIS {layout_ogg_vorbis, "Ogg"}, #endif diff --git a/src/layout/blocked.c b/src/layout/blocked.c index b8f13193..832abaaf 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -143,6 +143,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * break; case layout_ea_sns_blocked: ea_sns_block_update(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_awc: + block_update_awc(vgmstream->next_block_offset,vgmstream); break; default: break; diff --git a/src/layout/blocked_awc.c b/src/layout/blocked_awc.c new file mode 100644 index 00000000..6e197352 --- /dev/null +++ b/src/layout/blocked_awc.c @@ -0,0 +1,58 @@ +#include "layout.h" +#include "../coding/coding.h" +#include "../vgmstream.h" + +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian); + +/* AWC music chunks */ +void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; + size_t header_size, entries, block_size, block_samples; + int i; + + /* assumed only AWC_IMA enters here, MPEG/XMA2 need special parsing as blocked layout is too limited */ + + entries = read_32bit(block_offset + 0x18*0 + 0x04, streamFile); /* assumed same for all channels */ + block_samples = entries * (0x800-4)*2; + block_size = vgmstream->full_block_size; + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + vgmstream->current_block_samples = block_samples; + + /* starts with a header block */ + /* for each channel + * 0x00: start entry within channel (ie. entries * ch) + * 0x04: entries + * 0x08: samples to discard in the beginning of this block (MPEG only?) + * 0x0c: samples in channel (for MPEG/XMA2 can vary between channels) + * 0x10: MPEG only: close to number of frames but varies a bit? + * 0x14: MPEG only: channel usable data size (not counting padding) + * for each channel + * 32b * entries = global samples per frame in each block (for MPEG probably per full frame) + */ + + header_size = get_block_header_size(streamFile, block_offset, vgmstream->channels, vgmstream->codec_endian); + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + header_size + 0x800*entries*i; + } + +} + +static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian) { + size_t header_size = 0; + int i; + int entries = channels; + int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; + + for (i = 0; i < entries; i++) { + header_size += 0x18; + header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */ + } + + if (header_size % 0x800) /* padded */ + header_size += 0x800 - (header_size % 0x800); + + return header_size; +} diff --git a/src/layout/layout.h b/src/layout/layout.h index b67044e7..eea9221e 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -65,6 +65,8 @@ void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_awc(off_t block_ofset, VGMSTREAM * vgmstream); + /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 7c5a09c3..2b7213e3 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1534,6 +1534,10 @@ RelativePath=".\layout\blocked.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 14d74eae..d6a90881 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -454,6 +454,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index f760f38b..472f2f9b 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -898,6 +898,9 @@ layout\Source Files + + layout\Source Files + layout\Source Files diff --git a/src/meta/awc.c b/src/meta/awc.c index 02728f01..b91ccb3a 100644 --- a/src/meta/awc.c +++ b/src/meta/awc.c @@ -1,5 +1,6 @@ #include "meta.h" #include "../coding/coding.h" +#include "../layout/layout.h" typedef struct { int big_endian; @@ -51,16 +52,19 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { switch(awc.codec) { - //case 0x01: /* PCM (PC/PS3) */ - // vgmstream->coding_type = coding_PCM!6; - // vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none; - // break; - - //case 0x04: /* IMA (PC) */ - // vgmstream->coding_type = coding_AWC_IMA; - // vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none; - // break; + case 0x01: /* PCM (PC/PS3) [sfx, rarely] */ + if (awc.is_music) goto fail; /* blocked_awc needs to be prepared */ + vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + break; + case 0x04: /* IMA (PC) */ + vgmstream->coding_type = coding_AWC_IMA; + vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none; + vgmstream->full_block_size = awc.block_chunk; + vgmstream->codec_endian = awc.big_endian; + break; #ifdef VGM_USE_MPEG case 0x07: { /* MPEG (PS3) */ @@ -89,8 +93,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset)) goto fail; - //if (vgmstream->layout_type == layout_blocked_awc) - // update_ + if (vgmstream->layout_type == layout_blocked_awc) + block_update_awc(awc.stream_offset, vgmstream); return vgmstream; diff --git a/src/vgmstream.c b/src/vgmstream.c index 169ec9a7..cfa50582 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -934,6 +934,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_rws_blocked: case layout_hwas_blocked: case layout_ea_sns_blocked: + case layout_blocked_awc: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_interleave_byte: @@ -1053,6 +1054,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_WWISE_IMA: case coding_REF_IMA: return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels; + case coding_AWC_IMA: + return (0x800-4)*2; case coding_RAD_IMA_mono: return 32; case coding_NDS_PROCYON: @@ -1156,6 +1159,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_WWISE_IMA: case coding_REF_IMA: return vgmstream->interleave_block_size; + case coding_AWC_IMA: + return 0x800; case coding_RAD_IMA_mono: return 0x14; case coding_NGC_DTK: @@ -1653,6 +1658,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; + case coding_AWC_IMA: + for (chan=0;chanchannels;chan++) { + decode_awc_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_WS: for (chan=0;chanchannels;chan++) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 095f2094..f66071cd 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -132,6 +132,7 @@ typedef enum { coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */ coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */ coding_REF_IMA, /* Reflections IMA ADPCM */ + coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */ coding_MSADPCM, /* Microsoft ADPCM */ coding_WS, /* Westwood Studios VBR ADPCM */ @@ -236,6 +237,7 @@ typedef enum { layout_rws_blocked, layout_hwas_blocked, layout_ea_sns_blocked, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ + layout_blocked_awc, /* Rockstar AWC */ /* otherwise odd */ layout_acm, /* libacm layout */ From 3001b7acba854b462e8c4127d3acf96f4897c434 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 17:47:19 +0200 Subject: [PATCH 07/10] Fix NGC STM regression [Fire Emblem Path of Radiance (GC)] --- src/vgmstream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vgmstream.c b/src/vgmstream.c index cfa50582..f4e04f3b 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -47,6 +47,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_rxws, init_vgmstream_ps2_rxw, init_vgmstream_ps2_int, + init_vgmstream_ngc_dsp_stm, init_vgmstream_ps2_exst, init_vgmstream_ps2_svag, init_vgmstream_ps2_mib, From b758e8f60dd1c8886b74621142df877e46d2893d Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 18:26:55 +0200 Subject: [PATCH 08/10] Fix VAG interleave in rare cases [The Simpsons Wrestling (PS)] --- src/meta/ps2_vag.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/meta/ps2_vag.c b/src/meta/ps2_vag.c index 18688901..96f9552d 100644 --- a/src/meta/ps2_vag.c +++ b/src/meta/ps2_vag.c @@ -63,7 +63,11 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { break; case 'p': /* "VAGp" (extended) [most common, ex Ratchet & Clank] */ - if ((version <= 0x00000004) && (datasize < filesize / 2)) { /* two VAGp in the same file */ + if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ + channel_count = 2; /* The Simpsons Wrestling PSX interleave */ + loop_flag = 0; + } + else if ((version <= 0x00000004) && (datasize < filesize / 2)) { /* two VAGp in the same file */ if (is_swag) loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); else @@ -129,7 +133,15 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { case 'p': // VAGp interleave=0x10; - if ((version == 0x00000004) && (datasize < filesize / 2)) { + if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ + interleave = 0x6000; /* The Simpsons Wrestling PSX interleave, includes header */ + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_PS2_VAGs; + + vgmstream->num_samples = datasize / 16 * 28; + start_offset = 0x30; + } + else if ((version == 0x00000004) && (datasize < filesize / 2)) { vgmstream->channels=2; vgmstream->layout_type=layout_interleave; vgmstream->meta_type=meta_PS2_VAGs; From d46996a642a6834a530368677468cf59d8d69cf7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 20:05:03 +0200 Subject: [PATCH 09/10] Improve Guitar Hero II multi-streams with block layout Fixes partially by ignoring last stream, as it can be half sample rate --- src/formats.c | 3 +- src/layout/blocked.c | 3 + src/layout/blocked_awc.c | 2 + src/layout/blocked_vgs.c | 32 +++++++++ src/layout/layout.h | 3 +- src/libvgmstream.vcproj | 4 ++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 + src/meta/vgs.c | 116 ++++++++++++------------------- src/vgmstream.c | 1 + src/vgmstream.h | 1 + 11 files changed, 97 insertions(+), 72 deletions(-) create mode 100644 src/layout/blocked_vgs.c diff --git a/src/formats.c b/src/formats.c index dcac95a7..7764ab40 100644 --- a/src/formats.c +++ b/src/formats.c @@ -535,6 +535,7 @@ static const layout_info layout_info_list[] = { {layout_scd_int, "SCD multistream interleave"}, {layout_ea_sns_blocked, "Electronic Arts SNS blocked"}, {layout_blocked_awc, "blocked (AWC)"}, + {layout_blocked_vgs, "blocked (VGS)"}, #ifdef VGM_USE_VORBIS {layout_ogg_vorbis, "Ogg"}, #endif @@ -719,7 +720,7 @@ static const meta_info meta_info_list[] = { {meta_MSVP, "MSVP Header"}, {meta_NGC_SSM, "SSM DSP Header"}, {meta_PS2_JOE, "Disney/Pixar JOE Header"}, - {meta_VGS, "Guitar Hero Encore Rocks the 80's Header"}, + {meta_VGS, "Guitar Hero VGS Header"}, {meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"}, {meta_WII_SMP, "SMP DSP Header"}, {meta_EMFF_PS2, "Eidos Music File Format Header"}, diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 832abaaf..c9d372e7 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -146,6 +146,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * break; case layout_blocked_awc: block_update_awc(vgmstream->next_block_offset,vgmstream); + break; + case layout_blocked_vgs: + block_update_vgs(vgmstream->next_block_offset,vgmstream); break; default: break; diff --git a/src/layout/blocked_awc.c b/src/layout/blocked_awc.c index 6e197352..763f046d 100644 --- a/src/layout/blocked_awc.c +++ b/src/layout/blocked_awc.c @@ -1,7 +1,9 @@ #include "layout.h" #include "../coding/coding.h" +#include "../layout/layout.h" #include "../vgmstream.h" + static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian); /* AWC music chunks */ diff --git a/src/layout/blocked_vgs.c b/src/layout/blocked_vgs.c new file mode 100644 index 00000000..de5c0e5a --- /dev/null +++ b/src/layout/blocked_vgs.c @@ -0,0 +1,32 @@ +#include "layout.h" +#include "../coding/coding.h" +#include "../layout/layout.h" +#include "../vgmstream.h" + + +/* VGS multistream frames */ +void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; + size_t file_size = get_streamfile_size(vgmstream->ch[0].streamfile); + int i; + size_t channel_size = 0x10; + + + /* set offsets */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + channel_size*i; + } + + vgmstream->current_block_size = channel_size; + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + channel_size*vgmstream->channels; + + /* skip unhandled tracks: flag can be 0x0n per track, of 0x8x for last frame */ + while (vgmstream->next_block_offset < file_size) { + if ((read_8bit(vgmstream->next_block_offset + 0x01, streamFile) & 0x0F) == 0x00) + break; + + vgmstream->next_block_offset += channel_size; + } + +} diff --git a/src/layout/layout.h b/src/layout/layout.h index eea9221e..b3af1efc 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -65,7 +65,8 @@ void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream); void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream); -void block_update_awc(off_t block_ofset, VGMSTREAM * vgmstream); +void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_vgs(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 2b7213e3..6ff01eb3 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1537,6 +1537,10 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 472f2f9b..40ae6a17 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -901,6 +901,9 @@ layout\Source Files + + layout\Source Files + layout\Source Files diff --git a/src/meta/vgs.c b/src/meta/vgs.c index a2d8616a..ba2e66de 100644 --- a/src/meta/vgs.c +++ b/src/meta/vgs.c @@ -1,100 +1,76 @@ #include "meta.h" #include "../util.h" +#include "../coding/coding.h" +#include "../layout/layout.h" -/* VGS (from Guitar Hero Encore - Rocks the 80s) */ +/* VGS - from Guitar Hero Encore - Rocks the 80s, Guitar Hero II PS2 */ VGMSTREAM * init_vgmstream_vgs(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + size_t channel_size = 0, stream_data_size, stream_frame_count; + int channel_count = 0, loop_flag = 0, sample_rate = 0, stream_sample_rate; + int i; - int loop_flag; - int channel_flag; - int channel_flag_offset; - int channel_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vgs",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"vgs")) + goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x56675321) /* "VgS!" */ goto fail; + /* 0x04: version? */ - loop_flag = 0; - channel_flag_offset = get_streamfile_size(streamFile)-0x10; - channel_flag = read_32bitBE(channel_flag_offset,streamFile); - - /* Only seen files up to 5 channels, but just - to be sure we will look up to 8 chanels */ - switch (channel_flag) { - case 0x00800000: - channel_count = 1; - break; - case 0x00810000: - channel_count = 2; - break; - case 0x00820000: - channel_count = 3; - break; - case 0x00830000: - channel_count = 4; - break; - case 0x00840000: - channel_count = 5; - break; - case 0x00850000: - channel_count = 6; - break; - case 0x00860000: - channel_count = 7; - break; - case 0x00870000: - channel_count = 8; - break; - default: - goto fail; - } + /* contains N streams, which can have one less frame, or half frame and sample rate */ + for (i = 0; i < 8; i++) { + stream_sample_rate = read_32bitLE(0x08 + 0x08*i + 0x00,streamFile); + stream_frame_count = read_32bitLE(0x08 + 0x08*i + 0x04,streamFile); + stream_data_size = stream_frame_count*0x10; + + if (stream_sample_rate == 0) + break; + + if (!sample_rate || !channel_size) { + sample_rate = stream_sample_rate; + channel_size = stream_data_size; + } + + /* some streams end 1 frame early */ + if (channel_size - 0x10 == stream_data_size) { + channel_size -= 0x10; + } + + /* Guitar Hero II sometimes uses half sample rate for last stream */ + if (sample_rate != stream_sample_rate) { + VGM_LOG("VGS: ignoring stream %i\n", i); + //total_streams++; // todo handle substreams + break; + } + + channel_count++; + } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ start_offset = 0x80; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->coding_type = coding_PSX_badflags; - vgmstream->num_samples = (read_32bitLE(0x0C,streamFile)*channel_count*0x10)*28/16/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitLE(0x0C,streamFile)*channel_count*0x10)*28/16/channel_count; - } + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size*channel_count, channel_count); - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; + vgmstream->coding_type = coding_PSX_badflags; /* flag = stream/channel number */ + vgmstream->layout_type = layout_blocked_vgs; vgmstream->meta_type = meta_VGS; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + /* open files; channel offsets are updated below */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + block_update_vgs(start_offset, vgmstream); return vgmstream; - - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/src/vgmstream.c b/src/vgmstream.c index f4e04f3b..2ef7dbe0 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -936,6 +936,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_hwas_blocked: case layout_ea_sns_blocked: case layout_blocked_awc: + case layout_blocked_vgs: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_interleave_byte: diff --git a/src/vgmstream.h b/src/vgmstream.h index f66071cd..939cca88 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -238,6 +238,7 @@ typedef enum { layout_hwas_blocked, layout_ea_sns_blocked, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */ layout_blocked_awc, /* Rockstar AWC */ + layout_blocked_vgs, /* Guitar Hero II */ /* otherwise odd */ layout_acm, /* libacm layout */ From ab4e1234d967a80dabeadf18b090a342865724c9 Mon Sep 17 00:00:00 2001 From: bnnm Date: Mon, 28 Aug 2017 20:42:37 +0200 Subject: [PATCH 10/10] Remove unneeded MT info --- src/coding/ea_xa_decoder.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/coding/ea_xa_decoder.c b/src/coding/ea_xa_decoder.c index 8e82c31a..0a9e03ba 100644 --- a/src/coding/ea_xa_decoder.c +++ b/src/coding/ea_xa_decoder.c @@ -212,23 +212,3 @@ void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac stream->offset=0; } } - - -/* EA MicroTalk 10:1 / 5:1 */ -/** - * Rarely used but can be found in the wild: FIFA 2001 (PS2), FIFA Soccer 2002 (PS2) - * - * Decoding algorithm is unknown; some info found by analyzing sx.exe output: - * - Comes in 10:1 or 5:1 compression varieties (the later's byte layout looks similar but has roughly double frame size) - * - Also with "PCM samples" flag before each frame (later version) or without (first version) - * - When PCM flag is 0xEE it has 16b ? + 16b num_samples + PCM samples placed right after the frame, but they - * are written *before* (presumably so they have something while the frame is decoded), like EALayer3. - * - VBR ADPCM, apparently similar to Westwood VBR ADPCM: first byte seems a header with mode+count, but after it may - * be 8 bytes(?) of coefs/hist (unlike Westwood's), then data. Samples per frame changes with the mode used. - * ex. decoding pure silence (0000) takes 0x2E (10:1) or 0x48 (5:1) into 432 samples (RLE mode) - * - Variable frame size but seems to range from 0x20 to 0x80 (in 5:1 at least) - * - After a new SCDl block, first byte (in each channel) is a flag but various values have no effect in the output - * (01=first block, 00=normal block?) and should be skipped in the block parser. - * - */ -//void decode_ea_mt10(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {