From d5745c9d6ecbca778473198790cd5bc1d854b853 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Sat, 21 Jul 2018 23:09:44 +0300 Subject: [PATCH] EAAC tweaks --- src/coding/coding.h | 2 +- src/coding/ffmpeg_decoder_utils_ea_xma.c | 5 +- src/meta/ea_eaac.c | 77 ++++++++++++++---------- src/meta/ea_eaac_streamfile.h | 8 ++- 4 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index b9fb13d1..cc701d6b 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -250,7 +250,7 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); -size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile); +size_t ffmpeg_get_eaxma_virtual_size(int channels, int streamed, off_t real_offset, size_t real_size, STREAMFILE *streamFile); size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile); diff --git a/src/coding/ffmpeg_decoder_utils_ea_xma.c b/src/coding/ffmpeg_decoder_utils_ea_xma.c index 559c6fe6..a9729d36 100644 --- a/src/coding/ffmpeg_decoder_utils_ea_xma.c +++ b/src/coding/ffmpeg_decoder_utils_ea_xma.c @@ -199,7 +199,7 @@ int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data) { } /* needed to know in meta for fake RIFF */ -size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t real_size, STREAMFILE *streamFile) { +size_t ffmpeg_get_eaxma_virtual_size(int channels, int streamed, off_t real_offset, size_t real_size, STREAMFILE *streamFile) { size_t virtual_size = 0; size_t real_end_offset = real_offset + real_size; /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */ @@ -219,7 +219,6 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */ break; - max_packets = get_block_max_packets(num_streams, packets_offset, streamFile); if (max_packets == 0) goto fail; @@ -228,7 +227,7 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea real_offset += block_size; - if (block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */ + if (!streamed || block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */ break; } diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index e2bdc416..c6a9b94d 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -6,6 +6,7 @@ /* EAAudioCore formats, EA's current audio middleware */ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type); +static size_t get_snr_size(STREAMFILE *streamFile, off_t offset); /* .SNR+SNS - from EA latest games (~2008-2013), v0 header */ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { @@ -18,14 +19,7 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) { /* SNR headers normally need an external SNS file, but some have data [Burnout Paradise, NFL2013 (iOS)] */ if (get_streamfile_size(streamFile) > 0x10) { - off_t start_offset; - - switch(read_8bit(0x04,streamFile)) { /* flags */ - case 0x60: start_offset = 0x10; break; - case 0x20: start_offset = 0x0c; break; - default: start_offset = 0x08; break; - } - + off_t start_offset = get_snr_size(streamFile, 0x00); vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS); if (!vgmstream) goto fail; } @@ -214,7 +208,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { for (i = 0; i < total_sounds; i++) { snr_offset = (off_t)read_16bitBE(0x10 + (0x02+userdata_size) * i, streamFile) + 0x04; - flags = (read_8bit(snr_offset + 0x04, sthFile) & 0xE0) >> 4; + flags = (read_8bit(snr_offset + 0x04, sthFile) >> 4) & 0x0F; if (i == target_stream - 1) break; @@ -233,7 +227,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) { sns_offset += block_size; - if (flags & 2) { + if (flags & 4) { if (block_id == 0x80) break; } @@ -269,18 +263,26 @@ fail: static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type) { VGMSTREAM * vgmstream = NULL; STREAMFILE* temp_streamFile = NULL; - int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags; - uint32_t num_samples, loop_start = 0, loop_end = 0; + int channel_count, loop_flag = 0, streamed, version, codec, channel_config, flags; + int32_t header1, header2, sample_rate, num_samples, loop_start = 0, loop_end = 0; /* EA SNR/SPH header */ - version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0x0F; - codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0x0F; - channel_config = read_8bit(header_offset + 0x01,streamHead) & 0xFE; - sample_rate = read_32bitBE(header_offset + 0x00,streamHead) & 0x1FFFF; /* some Dead Space 2 (PC) uses 96000 */ - flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead) & 0xFE; //todo upper nibble only? (the first bit is part of size) - num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x01FFFFFF; - /* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): - * &0x20: 1 int (usually 0x00), &0x00/40: nothing, &0x60: 2 ints (usually 0x00 and 0x14) */ + /* 4 bits: version */ + /* 4 bits: codec */ + /* 6 bits: channel config */ + /* 18 bits: sample rate */ + /* 4 bits: flags */ + /* 28 bits: number of samples */ + header1 = read_32bitBE(header_offset + 0x00, streamHead); + header2 = read_32bitBE(header_offset + 0x04, streamHead); + version = (header1 >> 28) & 0x0F; + codec = (header1 >> 24) & 0x0F; + channel_config = (header1 >> 18) & 0x3F; + sample_rate = (header1 & 0x03FFFF); /* some Dead Space 2 (PC) uses 96000 */ + flags = (header2 >> 28) & 0x0F; // TODO: maybe even 3 bits and not 4? + num_samples = (header2 & 0x0FFFFFFF); + /* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): */ + /* 0x02: loop start sample, 0x00/04: nothing, 0x06: loop start sample and loop start block offset */ /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than the block flags used) */ if (version != 0 && version != 1) { @@ -288,27 +290,30 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST goto fail; } - /* 0x40: stream asset, 0x20: full loop, 0x00: default/RAM asset */ - if (flags != 0x60 && flags != 0x40 && flags != 0x20 && flags != 0x00) { + /* 0x04: stream asset, 0x02: full loop, 0x00: default/RAM asset */ + if (flags != 0x06 && flags != 0x04 && flags != 0x02 && flags != 0x00) { VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags); goto fail; } - /* seen in sfx and Dead Space ambient tracks */ - if (flags & 0x20) { + /* TODO: Properly implement looping, needed for Need for Speed: World (PC) */ + if (flags & 0x02) { loop_flag = 1; loop_start = 0; loop_end = num_samples; } + + /* Non-streamed sounds are stored as a single block */ + streamed = (flags & 0x04) != 0; /* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */ - //channel_count = ((channel_config >> 2) & 0xf) + 1; /* likely, but better fail with unknown values */ + /* fail with unknown values just in case */ switch(channel_config) { case 0x00: channel_count = 1; break; - case 0x04: channel_count = 2; break; - case 0x0c: channel_count = 4; break; - case 0x14: channel_count = 6; break; - case 0x1c: channel_count = 8; break; + case 0x01: channel_count = 2; break; + case 0x03: channel_count = 4; break; + case 0x05: channel_count = 6; break; + case 0x07: channel_count = 8; break; default: VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config); goto fail; @@ -342,7 +347,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST ffmpeg_custom_config cfg = {0}; stream_size = get_streamfile_size(streamData) - start_offset; - virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, start_offset,stream_size, streamData); + virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, streamed, start_offset,stream_size, streamData); block_size = 0x10000; /* todo unused and not correctly done by the parser */ block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); @@ -376,7 +381,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST /* remove blocks on reads for some edge cases in L32P and to properly apply discard modes * (otherwise, and removing discards, it'd work with layout_blocked_ea_sns) */ - temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, 0); + temp_streamFile = setup_eaac_streamfile(streamData, version, codec, streamed, start_offset, 0); if (!temp_streamFile) goto fail; start_offset = 0x00; /* must point to the custom streamfile's beginning */ @@ -412,7 +417,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST vgmstream->layout_type = layout_none; /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ - temp_streamFile = setup_eaac_streamfile(streamData, version, codec, start_offset, total_size); + temp_streamFile = setup_eaac_streamfile(streamData, version, codec, streamed, start_offset, total_size); if (!temp_streamFile) goto fail; start_offset = 0x00; /* must point to the custom streamfile's beginning */ @@ -448,3 +453,11 @@ fail: close_vgmstream(vgmstream); return NULL; } + +static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) { + switch (read_8bit(offset + 0x04, streamFile) >> 4 & 0x0F) { /* flags */ + case 0x06: return 0x10; + case 0x02: return 0x0C; + default: return 0x08; + } +} diff --git a/src/meta/ea_eaac_streamfile.h b/src/meta/ea_eaac_streamfile.h index 65fe8912..1e768156 100644 --- a/src/meta/ea_eaac_streamfile.h +++ b/src/meta/ea_eaac_streamfile.h @@ -11,6 +11,7 @@ typedef struct { /* config */ int version; int codec; + int streamed; off_t start_offset; size_t total_size; /* size of the resulting substream */ } eaac_io_data; @@ -110,7 +111,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, data->logical_offset += data_size; } - if (data->version == 0 && block_flag == 0x80) + if (data->version == 0 && (!data->streamed || block_flag == 0x80)) break; /* stop on last block */ } @@ -170,7 +171,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { physical_offset += block_size; total_size += data_size; - if (data->version == 0 && block_flag == 0x80) + if (data->version == 0 && (!data->streamed || block_flag == 0x80)) break; /* stop on last block */ } @@ -184,7 +185,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { * - EALayer3: MPEG granule 1 can go in the next block (in V2"P" mainly, others could use layout blocked_sns) * - EATrax: ATRAC9 frames can be split between blooks */ -static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, off_t start_offset, size_t total_size) { +static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, int streamed, off_t start_offset, size_t total_size) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; eaac_io_data io_data = {0}; size_t io_data_size = sizeof(eaac_io_data); @@ -192,6 +193,7 @@ static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, in io_data.version = version; io_data.codec = codec; io_data.start_offset = start_offset; + io_data.streamed = streamed; io_data.total_size = total_size; /* optional */ io_data.physical_offset = start_offset;