From 39b5480ca6ac3cebe6a9680041e2b38349ff595b Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 13:52:09 +0300 Subject: [PATCH 01/12] EA SCHl: Removed IDX/BIG parser --- src/meta/ea_schl.c | 89 ---------------------------------------------- src/meta/meta.h | 1 - src/vgmstream.c | 1 - 3 files changed, 91 deletions(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index d1611eee..c11ae759 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -351,95 +351,6 @@ fail: return NULL; } -/* EA IDX/BIG combo - basically a set of HDR/DAT compiled into one file */ -VGMSTREAM * init_vgmstream_ea_idx_big(STREAMFILE *streamFile) { - int target_stream = streamFile->stream_index, total_sounds, subsound_index; - uint32_t i, num_hdr; - uint16_t hdr_id, hdr_subid; - uint8_t userdata_size, hdr_sounds; - off_t entry_offset, hdr_offset, base_offset, schl_offset, offset_mult; - //size_t hdr_size; - char stream_name[STREAM_NAME_SIZE]; - STREAMFILE *bigFile = NULL; - VGMSTREAM *vgmstream = NULL; - int32_t (*read_32bit)(off_t,STREAMFILE*); - int16_t (*read_16bit)(off_t,STREAMFILE*); - - /* seems to always start with 0x00000001 */ - if (read_32bitLE(0x00, streamFile) != 0x00000001 && - read_32bitBE(0x00, streamFile) != 0x00000001) - goto fail; - - bigFile = open_streamfile_by_ext(streamFile, "big"); - if (!bigFile) - goto fail; - - if (read_32bitBE(0x00, bigFile) != EA_BLOCKID_HEADER) - goto fail; - - /* use number of files for endianness check */ - if (guess_endianness32bit(0x04,streamFile)) { - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - } else { - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - } - - num_hdr = read_32bit(0x04, streamFile); - if (read_32bit(0x54,streamFile) != num_hdr) - goto fail; - - if (target_stream == 0) target_stream = 1; - schl_offset = 0; - total_sounds = 0; - schl_offset = 0xFFFFFFFF; - - for (i = 0; i < num_hdr; i++) { - entry_offset = 0x58 + 0x10 * i; - //hdr_size = read_32bit(entry_offset + 0x04, streamFile); - hdr_offset = read_32bit(entry_offset + 0x08, streamFile); - base_offset = read_32bit(entry_offset + 0x0C, streamFile); - - hdr_id = read_16bit(hdr_offset + 0x00, streamFile); - hdr_subid = read_16bit(hdr_offset + 0x02, streamFile); - userdata_size = read_8bit(hdr_offset + 0x04, streamFile) & 0x0F; - hdr_sounds = read_8bit(hdr_offset + 0x05, streamFile); - offset_mult = (off_t)read_8bit(hdr_offset + 0x07, streamFile) * 0x0100 + 0x0100; - - if (target_stream > total_sounds && target_stream <= total_sounds + hdr_sounds) { - schl_offset = base_offset + (off_t)read_16bitBE(hdr_offset + 0x0C + (0x02+userdata_size) * (target_stream-total_sounds-1), streamFile) * offset_mult; - subsound_index = target_stream - total_sounds; - - /* There are no filenames but we can add IDs to stream name for better organization */ - if (hdr_subid != 0xFFFF) - snprintf(stream_name, STREAM_NAME_SIZE, "%03d_%02d_%d", hdr_id, hdr_subid, subsound_index); - else - snprintf(stream_name, STREAM_NAME_SIZE, "%03d_%d", hdr_id, subsound_index); - } - - total_sounds += hdr_sounds; - } - - if (schl_offset == 0xFFFFFFFF) - goto fail; - - if (read_32bitBE(schl_offset, bigFile) != EA_BLOCKID_HEADER) - goto fail; - - vgmstream = parse_schl_block(bigFile, schl_offset, total_sounds); - if (!vgmstream) - goto fail; - - strncpy(vgmstream->stream_name, stream_name, STREAM_NAME_SIZE); - close_streamfile(bigFile); - return vgmstream; - -fail: - close_streamfile(bigFile); - return NULL; -} - /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams) { off_t start_offset, header_offset; diff --git a/src/meta/meta.h b/src/meta/meta.h index 9b392cea..6075842d 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -645,7 +645,6 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ea_idx_big(STREAMFILE * steeamFile); VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index fc7cb3cb..19201e8f 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -352,7 +352,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ea_bnk, init_vgmstream_ea_abk, init_vgmstream_ea_hdr_dat, - init_vgmstream_ea_idx_big, init_vgmstream_ea_schl_fixed, init_vgmstream_sk_aud, init_vgmstream_stm, From 808188dbcdd26637f5f8fccdedbf3b2c2f449f66 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 20:01:36 +0300 Subject: [PATCH 02/12] EA SCHl: Added MPF/MUS format --- src/formats.c | 1 + src/meta/ea_schl.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ src/meta/meta.h | 1 + src/vgmstream.c | 1 + 4 files changed, 90 insertions(+) diff --git a/src/formats.c b/src/formats.c index 4eb018c5..47b713fa 100644 --- a/src/formats.c +++ b/src/formats.c @@ -239,6 +239,7 @@ static const char* extension_list[] = { //"mpc", //common "mpdsp", "mpds", + "mpf", "mps", //txth/reserved [Scandal (PS2)] "ms", "msa", diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index c11ae759..a2c051be 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -351,6 +351,93 @@ fail: return NULL; } +/* EA MPF/MUS combo - used in newer 6th gen games for storing music */ +VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { + off_t section_offset, entry_offset, subentry_num, eof_offset, schl_offset; + uint16_t sec1_num; + uint8_t version, sub_version, sec2_num; + int32_t(*read_32bit)(off_t, STREAMFILE*); + int16_t(*read_16bit)(off_t, STREAMFILE*); + STREAMFILE *musFile = NULL; + VGMSTREAM *vgmstream = NULL; + int target_stream = streamFile->stream_index, total_streams; + + /* check extension */ + if (!check_extensions(streamFile, "mpf")) + goto fail; + + /* detect endianness */ + if (read_32bitBE(0x00, streamFile) == 0x50464478) { /* "PFDx" */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else if (read_32bitBE(0x00, streamFile) == 0x78444650) { /* "xDFP" */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } else { + goto fail; + } + + musFile = open_streamfile_by_ext(streamFile, "mus"); + if (!musFile) goto fail; + + version = read_8bit(0x04, streamFile); + sub_version = read_8bit(0x05, streamFile); + + if (version < 0x04 || version > 0x05) goto fail; + if (version == 0x05 && sub_version > 0x02) goto fail; /* newer version using SNR/SNS */ + + if (version == 0x04) { + /* we need to go through the first two sections to find sound table */ + sec1_num = read_16bit(0x12, streamFile); + sec2_num = read_8bit(0x0f, streamFile); + + /* get the last entry offset */ + section_offset = 0x20; + entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; + + /* HACK: there's some weird bitstream here that's store differently in LE and BE */ + /* I can't figure it out, so let's just use a workaround for now */ + if (read_32bitBE(0x00, streamFile) == 0x50464478) { + subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 15) & 0xFF; + } else { + subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 20) & 0xFF; + } + + section_offset = entry_offset + 0x10 + subentry_num * 0x04; + entry_offset = read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04; + subentry_num = read_16bit(entry_offset + 0x0e, streamFile); + + section_offset = entry_offset + 0x10 + subentry_num * 0x10; + entry_offset = read_32bit(section_offset, streamFile) * 0x04; + section_offset = read_32bit(entry_offset + 0x00, streamFile) * 0x04; + eof_offset = read_32bit(entry_offset + 0x04, streamFile) * 0x04; + total_streams = (eof_offset - section_offset) / 0x08; + } else if (version == 0x05) { + section_offset = read_32bit(0x34, streamFile); + eof_offset = read_32bit(0x38, streamFile); + total_streams = (eof_offset - section_offset) / 0x08; + } + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || total_streams == 0 || target_stream > total_streams) + goto fail; + + schl_offset = read_32bit(section_offset + (target_stream - 1) * 0x08 + 0x00, streamFile) * 0x80; + if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) + goto fail; + + vgmstream = parse_schl_block(musFile, schl_offset, total_streams); + if (!vgmstream) + goto fail; + + close_streamfile(musFile); + return vgmstream; + +fail: + close_streamfile(musFile); + return NULL; +} + /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams) { off_t start_offset, header_offset; diff --git a/src/meta/meta.h b/src/meta/meta.h index 6075842d..a84067d9 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -645,6 +645,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE * steeamFile); VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index 19201e8f..8e93282e 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -352,6 +352,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ea_bnk, init_vgmstream_ea_abk, init_vgmstream_ea_hdr_dat, + init_vgmstream_ea_mpf_mus, init_vgmstream_ea_schl_fixed, init_vgmstream_sk_aud, init_vgmstream_stm, From 53b3991ae83addce392a8d39c3740077a43d55a0 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 20:27:03 +0300 Subject: [PATCH 03/12] EA SCHl: Added MAP/MUS format --- src/formats.c | 2 ++ src/meta/ea_schl.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ src/meta/meta.h | 1 + src/vgmstream.c | 1 + 4 files changed, 63 insertions(+) diff --git a/src/formats.c b/src/formats.c index 47b713fa..02d1ab36 100644 --- a/src/formats.c +++ b/src/formats.c @@ -203,6 +203,7 @@ static const char* extension_list[] = { "lac3", //fake extension for .ac3, FFmpeg/not parsed "leg", "lflac", //fake extension for .flac, FFmpeg/not parsed + "lin", "lmp2", //fake extension for .mp2, FFmpeg/not parsed "lmp3", //fake extension for .mp3, FFmpeg/not parsed "lmp4", //fake extension for .mp4 @@ -219,6 +220,7 @@ static const char* extension_list[] = { "lwma", //fake extension for .wma, FFmpeg/not parsed "mab", + "map", "matx", "mc3", "mca", diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index a2c051be..709a5f9b 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -351,6 +351,65 @@ fail: return NULL; } +/* EA MAP/MUS combo - used in some old games for interactive music info */ +VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { + uint8_t num_sounds, num_userdata; + off_t section_offset, schl_offset; + STREAMFILE *musFile = NULL; + VGMSTREAM *vgmstream = NULL; + int target_stream = streamFile->stream_index; + + /* check extension */ + if (!check_extensions(streamFile, "map,lin")) + goto fail; + + /* always big endian */ + if (read_32bitBE(0x00, streamFile) != 0x50464478) /* "PFDx" */ + goto fail; + + musFile = open_streamfile_by_ext(streamFile, "mus"); + if (!musFile) goto fail; + + /* + * 0x04: ??? + * 0x05: intro segment + * 0x06: number of segments + * 0x07: userdata entry size (incorrect?) + * 0x08: three zeroes + * 0x0b: number of userdata entries + */ + num_sounds = read_8bit(0x06, streamFile); + num_userdata = read_8bit(0x0b, streamFile); + + section_offset = 0x0c; + + /* section 1: contains information about segment playback order */ + section_offset += num_sounds * 0x1c; + + /* section 2: userdata, specific to game and track */ + section_offset += num_userdata * 0x10; + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds) + goto fail; + + /* section 3: sound offset table */ + schl_offset = read_32bitBE(section_offset + (target_stream - 1) * 0x04, streamFile); + if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) + goto fail; + + vgmstream = parse_schl_block(musFile, schl_offset, num_sounds); + if (!vgmstream) + goto fail; + + close_streamfile(musFile); + return vgmstream; + +fail: + close_streamfile(musFile); + return NULL; +} + /* EA MPF/MUS combo - used in newer 6th gen games for storing music */ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { off_t section_offset, entry_offset, subentry_num, eof_offset, schl_offset; diff --git a/src/meta/meta.h b/src/meta/meta.h index a84067d9..832b5546 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -645,6 +645,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE * steeamFile); VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE * steeamFile); VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index 8e93282e..22ba134c 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -352,6 +352,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ea_bnk, init_vgmstream_ea_abk, init_vgmstream_ea_hdr_dat, + init_vgmstream_ea_map_mus, init_vgmstream_ea_mpf_mus, init_vgmstream_ea_schl_fixed, init_vgmstream_sk_aud, From 268a6a4319e450c7e9e35d3562779ab1064fa88d Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 20:41:02 +0300 Subject: [PATCH 04/12] EA SCHl: Removed sound file merging hack --- src/layout/blocked_ea_schl.c | 19 -------- src/meta/ea_schl.c | 86 +++++++++++------------------------- 2 files changed, 26 insertions(+), 79 deletions(-) diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index bfa07412..a55c960c 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -6,7 +6,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - int new_schl = 0; size_t block_size, block_samples; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; @@ -49,24 +48,11 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; } - /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ - if (block_id == 0x5343486C) - new_schl = 1; - - /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ - if (block_id == 0x00000000) - block_size = 0x04; - /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { block_size = 0x04; block_samples = 0; } - - /* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */ - if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) { - block_size += 0x04 - ((block_offset + block_size) % 0x04); - } } @@ -165,11 +151,6 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; } - /* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */ - if (new_schl) { - flush_mpeg(vgmstream->codec_data); - } - break; #endif /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 709a5f9b..5e5b6c32 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -97,13 +97,12 @@ typedef struct { int codec_config; } ea_header; -static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams); -static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams); +static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset); +static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded); static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); -static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); -static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int total_streams); +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk); /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { @@ -126,7 +125,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. * Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language=EN/FR/GE/IT/SP/RU/JA). * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ - return parse_schl_block(streamFile, 0x00, 0); + return parse_schl_block(streamFile, 0x00); fail: return NULL; @@ -261,7 +260,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { goto fail; bnk_target_stream = read_32bit(target_entry_offset + 0x04, streamFile) + 1; - vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, total_sounds); + vgmstream = parse_bnk_header(streamFile, bnk_offset, bnk_target_stream, 1); if (!vgmstream) goto fail; break; @@ -280,7 +279,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(astData, schl_offset, total_sounds); + vgmstream = parse_schl_block(astData, schl_offset); if (!vgmstream) goto fail; break; @@ -290,6 +289,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { break; } + vgmstream->num_streams = total_sounds; close_streamfile(astData); return vgmstream; @@ -339,10 +339,11 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(datFile, schl_offset, total_sounds); + vgmstream = parse_schl_block(datFile, schl_offset); if (!vgmstream) goto fail; + vgmstream->num_streams = total_sounds; close_streamfile(datFile); return vgmstream; @@ -398,10 +399,11 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(musFile, schl_offset, num_sounds); + vgmstream = parse_schl_block(musFile, schl_offset); if (!vgmstream) goto fail; + vgmstream->num_streams = num_sounds; close_streamfile(musFile); return vgmstream; @@ -485,10 +487,11 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(musFile, schl_offset, total_streams); + vgmstream = parse_schl_block(musFile, schl_offset); if (!vgmstream) goto fail; + vgmstream->num_streams = total_streams; close_streamfile(musFile); return vgmstream; @@ -498,7 +501,7 @@ fail: } /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ -static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams) { +static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset) { off_t start_offset, header_offset; size_t header_size; ea_header ea = { 0 }; @@ -519,19 +522,20 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ /* rest is common */ - return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, total_streams); + return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0); fail: return NULL; } /* EA BNK with variable header - from EA games SFXs; also created by sx.exe */ -static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int total_streams) { +static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded) { off_t header_offset, start_offset, test_offset, table_offset; size_t header_size; ea_header ea = {0}; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + VGMSTREAM *vgmstream = NULL; int i, bnk_version; int total_bnk_sounds, real_bnk_sounds = 0; @@ -582,7 +586,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta real_bnk_sounds++; /* ABK points at absolute indexes, i.e. with dummies included */ - if (total_streams != 0) { + if (is_embedded != 0) { if (target_stream - 1 == i) header_offset = offset + table_offset + 0x04 * i + test_offset; } @@ -609,14 +613,20 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ /* rest is common */ - return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams ? total_streams : real_bnk_sounds); + vgmstream = init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version); + if (!vgmstream) goto fail; + if (!is_embedded) { + vgmstream->num_streams = real_bnk_sounds; + } + + return vgmstream; fail: return NULL; } /* inits VGMSTREAM from a EA header */ -static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version, int total_streams) { +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version) { VGMSTREAM * vgmstream = NULL; int i, ch; int is_bnk = bnk_version; @@ -663,7 +673,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->layout_type = layout_blocked_ea_schl; } - vgmstream->num_streams = total_streams; //vgmstream->stream_size = ; //todo needed for kbps info /* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and @@ -848,15 +857,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } } } - else if (vgmstream->layout_type == layout_blocked_ea_schl) { - /* regular SCHls, except ATRAC3plus */ - if (total_streams == 0) { - /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */ - int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream); - if (total_samples > vgmstream->num_samples) - vgmstream->num_samples = total_samples; - } - } return vgmstream; @@ -1245,40 +1245,6 @@ fail: return 0; } -/* Get total samples by parsing block headers, needed when multiple files are stitched together. - * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped - * music (.map/lin). Subfiles always share header, except num_samples. */ -static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) { - int num_samples = 0; - int multiple_schl = 0; - - /* calc num_samples as playable data size varies between files/blocks */ - { - vgmstream->next_block_offset = start_offset; - do { - uint32_t block_id = read_32bitBE(vgmstream->next_block_offset+0x00,streamFile); - if (block_id == EA_BLOCKID_HEADER) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ - multiple_schl = 1; - - block_update_ea_schl(vgmstream->next_block_offset,vgmstream); - num_samples += vgmstream->current_block_samples; - } - while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - - /* reset after getting samples */ - block_update(start_offset,vgmstream); - } - - /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ - if (multiple_schl) { - ;VGM_LOG("EA SCHl: multiple SCHl found\n"); - return num_samples; - } - else { - return 0; - } -} - /* find data start offset inside the first SCDl; not very elegant but oh well */ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { size_t file_size = get_streamfile_size(streamFile); From 54cf2e04fcb6b1db1aa948a8f1ec7fd222fe097e Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 21:20:28 +0300 Subject: [PATCH 05/12] EA SCHl: Calculate stream size for bitrate --- src/meta/ea_schl.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 5e5b6c32..e71162e3 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -103,6 +103,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk); +static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream); /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { @@ -378,10 +379,10 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { * 0x07: userdata entry size (incorrect?) * 0x08: three zeroes * 0x0b: number of userdata entries + * 0x0c: section 1 start */ num_sounds = read_8bit(0x06, streamFile); num_userdata = read_8bit(0x0b, streamFile); - section_offset = 0x0c; /* section 1: contains information about segment playback order */ @@ -456,7 +457,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { section_offset = 0x20; entry_offset = read_16bit(section_offset + (sec1_num - 1) * 0x02, streamFile) * 0x04; - /* HACK: there's some weird bitstream here that's store differently in LE and BE */ + /* HACK: there's some weird bitstream here that's stored differently in LE and BE */ /* I can't figure it out, so let's just use a workaround for now */ if (read_32bitBE(0x00, streamFile) == 0x50464478) { subentry_num = (read_32bitBE(entry_offset + 0x04, streamFile) >> 15) & 0xFF; @@ -673,8 +674,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->layout_type = layout_blocked_ea_schl; } - //vgmstream->stream_size = ; //todo needed for kbps info - /* EA usually implements their codecs in all platforms (PS2/WII do EAXA/MT/EALAYER3) and * favors them over platform's natives (ex. EAXA vs VAG/DSP). * Unneeded codecs are removed over time (ex. LAYER3 when EALAYER3 was introduced). */ @@ -856,6 +855,11 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->ch[i].offset = ea->offsets[i]; } } + + /* TODO: Figure out how to get stream size for BNK sounds */ + } + else { + vgmstream->stream_size = get_ea_stream_size(streamFile, start_offset, vgmstream); } return vgmstream; @@ -1245,6 +1249,33 @@ fail: return 0; } +/* Stream size is almost never provided in bank files so we have to calc it manually */ +static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream) { + size_t stream_size, block_size; + uint32_t block_id; + off_t block_offset; + + stream_size = 0; + block_offset = start_offset; + + + while (1) { + block_id = read_32bitBE(block_offset + 0x00, streamFile); + if (block_id == EA_BLOCKID_END) + break; + + if (vgmstream->codec_config & 0x02) /* size is always LE, except in early SS/MAC */ + block_size = read_32bitBE(block_offset + 0x04, streamFile); + else + block_size = read_32bitLE(block_offset + 0x04, streamFile); + + stream_size += block_size - 0x08; + block_offset += block_size; + } + + return stream_size; +} + /* find data start offset inside the first SCDl; not very elegant but oh well */ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { size_t file_size = get_streamfile_size(streamFile); From 430ea27f85736ca058240b9d11bc1b43edd1b788 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 21:26:04 +0300 Subject: [PATCH 06/12] EAAC: Added MPF/MUS format --- src/meta/ea_eaac.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/meta/meta.h | 1 + src/vgmstream.c | 1 + 3 files changed, 81 insertions(+) diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 80f3f370..ba7b7040 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -462,6 +462,85 @@ fail: return NULL; } +/* EA MPF/MUS combo - used in older 7th gen games for storing music */ +VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE *streamFile) { + uint32_t num_sounds; + uint8_t version, sub_version, block_id; + off_t table_offset, entry_offset, snr_offset, sns_offset; + size_t snr_size, sns_size; + int32_t(*read_32bit)(off_t, STREAMFILE*); + int16_t(*read_16bit)(off_t, STREAMFILE*); + STREAMFILE *musFile = NULL; + VGMSTREAM *vgmstream = NULL; + int target_stream = streamFile->stream_index, total_streams; + + /* check extension */ + if (!check_extensions(streamFile, "mpf")) + goto fail; + + /* detect endianness */ + if (read_32bitBE(0x00, streamFile) == 0x50464478) { /* "PFDx" */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else if (read_32bitBE(0x00, streamFile) == 0x78444650) { /* "xDFP" */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } else { + goto fail; + } + + musFile = open_streamfile_by_ext(streamFile, "mus"); + if (!musFile) goto fail; + + /* MPF format is unchanged but we don't really care about its contents since + * MUS conveniently contains sound offset table */ + + version = read_8bit(0x04, streamFile); + sub_version = read_8bit(0x05, streamFile); + if (version != 0x05 || sub_version != 0x03) goto fail; + + /* number of files is always little endian */ + num_sounds = read_32bitLE(0x04, musFile); + table_offset = 0x28; + + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || num_sounds == 0 || target_stream > num_sounds) + goto fail; + + /* + * 0x00: hash? + * 0x04: index + * 0x06: zero + * 0x08: SNR offset + * 0x0c: SNS offset + * 0x10: SNR size + * 0x14: SNS size + * 0x18: zero + */ + entry_offset = table_offset + (target_stream - 1) * 0x1c; + snr_offset = read_32bit(entry_offset + 0x08, musFile) * 0x10; + sns_offset = read_32bit(entry_offset + 0x0c, musFile) * 0x80; + snr_size = read_32bit(entry_offset + 0x10, musFile); + sns_size = read_32bit(entry_offset + 0x14, musFile); + + block_id = read_8bit(sns_offset, musFile); + if (block_id != EAAC_BLOCKID0_DATA && block_id != EAAC_BLOCKID0_END) + goto fail; + + vgmstream = init_vgmstream_eaaudiocore_header(musFile, musFile, snr_offset, sns_offset, meta_EA_SNR_SNS); + if (!vgmstream) + goto fail; + + vgmstream->num_streams = num_sounds; + vgmstream->stream_size = sns_size; + close_streamfile(musFile); + return vgmstream; + +fail: + close_streamfile(musFile); + return NULL; +} + /* ************************************************************************* */ typedef struct { diff --git a/src/meta/meta.h b/src/meta/meta.h index 832b5546..a16a651c 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -684,6 +684,7 @@ VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile); diff --git a/src/vgmstream.c b/src/vgmstream.c index 22ba134c..f7242849 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -378,6 +378,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ea_sps, init_vgmstream_ea_abk_new, init_vgmstream_ea_hdr_sth_dat, + init_vgmstream_ea_mpf_mus_new, init_vgmstream_ngc_vid1, init_vgmstream_flx, init_vgmstream_mogg, From 4024016d1026ade06e7297094430344315353ff3 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 21:52:37 +0300 Subject: [PATCH 07/12] EA SCHl: Big endian fix --- src/meta/ea_schl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index e71162e3..ec600998 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -467,7 +467,13 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { section_offset = entry_offset + 0x10 + subentry_num * 0x04; entry_offset = read_16bit(section_offset + (sec2_num - 1) * 0x02, streamFile) * 0x04; - subentry_num = read_16bit(entry_offset + 0x0e, streamFile); + + /* more weird stuff */ + if (read_32bitBE(0x00, streamFile) == 0x50464478) { + subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 10) & 0xFF; + } else { + subentry_num = (read_32bitBE(entry_offset + 0x0c, streamFile) >> 8) & 0xFF; + } section_offset = entry_offset + 0x10 + subentry_num * 0x10; entry_offset = read_32bit(section_offset, streamFile) * 0x04; From 6553786f8a3e9266fe3f9fb69f5038b1151419df Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 21:58:41 +0300 Subject: [PATCH 08/12] EA SCHl: Fixed get_ea_stream_size --- src/meta/ea_schl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index ec600998..383bd224 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -1275,7 +1275,7 @@ static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTR else block_size = read_32bitLE(block_offset + 0x04, streamFile); - stream_size += block_size - 0x08; + stream_size += block_size - 0x12; block_offset += block_size; } From 6231905a79ad51d57f277a9179aa5f2b17df4dd6 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 22:18:26 +0300 Subject: [PATCH 09/12] EA SCHl: Added EOF check --- src/meta/ea_schl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 383bd224..96ab82d2 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -1264,7 +1264,6 @@ static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTR stream_size = 0; block_offset = start_offset; - while (1) { block_id = read_32bitBE(block_offset + 0x00, streamFile); if (block_id == EA_BLOCKID_END) @@ -1275,6 +1274,9 @@ static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTR else block_size = read_32bitLE(block_offset + 0x04, streamFile); + if (block_size == 0x00 || block_size > 0xFFFFF) /* EOF read */ + break; + stream_size += block_size - 0x12; block_offset += block_size; } From 7418e32482f801ec61875586100760227f8ebe3d Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Thu, 27 Dec 2018 23:16:59 +0300 Subject: [PATCH 10/12] EA SCHl: Restored sound merging hack Too many sets rely on it, not worth it --- src/layout/blocked_ea_schl.c | 19 +++++++++++ src/meta/ea_schl.c | 63 +++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index a55c960c..bfa07412 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -6,6 +6,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; + int new_schl = 0; size_t block_size, block_samples; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; @@ -48,11 +49,24 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { break; } + /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ + if (block_id == 0x5343486C) + new_schl = 1; + + /* padding between "SCEl" and next "SCHl" (when subfiles exist) */ + if (block_id == 0x00000000) + block_size = 0x04; + /* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */ if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) { block_size = 0x04; block_samples = 0; } + + /* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */ + if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) { + block_size += 0x04 - ((block_offset + block_size) % 0x04); + } } @@ -151,6 +165,11 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; } + /* SCHl with multiple SCHl need to reset their MPEG decoder as there are trailing samples in the buffers */ + if (new_schl) { + flush_mpeg(vgmstream->codec_data); + } + break; #endif /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 96ab82d2..41457c2a 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -97,13 +97,14 @@ typedef struct { int codec_config; } ea_header; -static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset); +static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone); static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int target_stream, int is_embedded); static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version); static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); -static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk); +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int standalone); static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream); +static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream); /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { @@ -126,7 +127,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. * Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language=EN/FR/GE/IT/SP/RU/JA). * The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */ - return parse_schl_block(streamFile, 0x00); + return parse_schl_block(streamFile, 0x00, 1); fail: return NULL; @@ -280,7 +281,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, astData) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(astData, schl_offset); + vgmstream = parse_schl_block(astData, schl_offset, 0); if (!vgmstream) goto fail; break; @@ -340,7 +341,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, datFile) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(datFile, schl_offset); + vgmstream = parse_schl_block(datFile, schl_offset, 0); if (!vgmstream) goto fail; @@ -400,7 +401,7 @@ VGMSTREAM * init_vgmstream_ea_map_mus(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(musFile, schl_offset); + vgmstream = parse_schl_block(musFile, schl_offset, 0); if (!vgmstream) goto fail; @@ -494,7 +495,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus(STREAMFILE *streamFile) { if (read_32bitBE(schl_offset, musFile) != EA_BLOCKID_HEADER) goto fail; - vgmstream = parse_schl_block(musFile, schl_offset); + vgmstream = parse_schl_block(musFile, schl_offset, 0); if (!vgmstream) goto fail; @@ -508,7 +509,7 @@ fail: } /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ -static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset) { +static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone) { off_t start_offset, header_offset; size_t header_size; ea_header ea = { 0 }; @@ -529,7 +530,7 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset) { start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ /* rest is common */ - return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0); + return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, standalone); fail: return NULL; @@ -620,7 +621,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ /* rest is common */ - vgmstream = init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version); + vgmstream = init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, 0); if (!vgmstream) goto fail; if (!is_embedded) { vgmstream->num_streams = real_bnk_sounds; @@ -633,7 +634,7 @@ fail: } /* inits VGMSTREAM from a EA header */ -static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version) { +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version, int standalone) { VGMSTREAM * vgmstream = NULL; int i, ch; int is_bnk = bnk_version; @@ -866,6 +867,14 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } else { vgmstream->stream_size = get_ea_stream_size(streamFile, start_offset, vgmstream); + + /* regular SCHls, except ATRAC3plus */ + if (standalone) { + /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */ + int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream); + if (total_samples > vgmstream->num_samples) + vgmstream->num_samples = total_samples; + } } return vgmstream; @@ -1284,6 +1293,38 @@ static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTR return stream_size; } +/* Get total samples by parsing block headers, needed when multiple files are stitched together. + * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped + * music (.map/lin). Subfiles always share header, except num_samples. */ +static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) { + int num_samples = 0; + int multiple_schl = 0; + + /* calc num_samples as playable data size varies between files/blocks */ + { + vgmstream->next_block_offset = start_offset; + do { + uint32_t block_id = read_32bitBE(vgmstream->next_block_offset + 0x00, streamFile); + if (block_id == EA_BLOCKID_HEADER) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ + multiple_schl = 1; + + block_update_ea_schl(vgmstream->next_block_offset, vgmstream); + num_samples += vgmstream->current_block_samples; + } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + + /* reset after getting samples */ + block_update(start_offset, vgmstream); + } + + /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ + if (multiple_schl) { + ; VGM_LOG("EA SCHl: multiple SCHl found\n"); + return num_samples; + } else { + return 0; + } +} + /* find data start offset inside the first SCDl; not very elegant but oh well */ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { size_t file_size = get_streamfile_size(streamFile); From 9b29ba9599137ad27c518505a7d008110fc87b38 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Fri, 28 Dec 2018 02:02:36 +0300 Subject: [PATCH 11/12] EA SCHl: fixed stream size calc not working with merged sound files --- src/meta/ea_schl.c | 98 ++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 60 deletions(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 41457c2a..eaaf8f8d 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -103,8 +103,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset); static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int standalone); -static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream); -static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream); +static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream, int standalone); /* EA SCHl with variable header - from EA games (roughly 1997~2010); generated by EA Canada's sx.exe/Sound eXchange */ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { @@ -866,15 +865,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ /* TODO: Figure out how to get stream size for BNK sounds */ } else { - vgmstream->stream_size = get_ea_stream_size(streamFile, start_offset, vgmstream); - - /* regular SCHls, except ATRAC3plus */ - if (standalone) { - /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this */ - int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream); - if (total_samples > vgmstream->num_samples) - vgmstream->num_samples = total_samples; - } + update_ea_stream_size_and_samples(streamFile, start_offset, vgmstream, standalone); } return vgmstream; @@ -1264,65 +1255,52 @@ fail: return 0; } -/* Stream size is almost never provided in bank files so we have to calc it manually */ -static int get_ea_stream_size(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream) { - size_t stream_size, block_size; +static void update_ea_stream_size_and_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM *vgmstream, int standalone) { uint32_t block_id; - off_t block_offset; + int32_t num_samples; + size_t stream_size, file_size; + int multiple_schl; - stream_size = 0; - block_offset = start_offset; + stream_size = 0, num_samples = 0, multiple_schl = 0; + file_size = get_streamfile_size(streamFile); + vgmstream->next_block_offset = start_offset; - while (1) { - block_id = read_32bitBE(block_offset + 0x00, streamFile); - if (block_id == EA_BLOCKID_END) - break; + while (vgmstream->next_block_offset < file_size) { + block_update_ea_schl(vgmstream->next_block_offset, vgmstream); - if (vgmstream->codec_config & 0x02) /* size is always LE, except in early SS/MAC */ - block_size = read_32bitBE(block_offset + 0x04, streamFile); - else - block_size = read_32bitLE(block_offset + 0x04, streamFile); + block_id = read_32bitBE(vgmstream->current_block_offset + 0x00, streamFile); + if (block_id == EA_BLOCKID_END) { /* banks should never contain movie "SHxx" */ + if (!standalone) + break; + } + else if (block_id == EA_BLOCKID_HEADER) { /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ + multiple_schl = 1; + } - if (block_size == 0x00 || block_size > 0xFFFFF) /* EOF read */ - break; + /* HACK: fix num_samples for streams with multiple SCHl. Need to eventually get rid of this. + * Get total samples by parsing block headers, needed when multiple files are stitched together. + * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped + * music (.map/lin). Subfiles always share header, except num_samples. */ + num_samples += vgmstream->current_block_samples; - stream_size += block_size - 0x12; - block_offset += block_size; - } - - return stream_size; -} - -/* Get total samples by parsing block headers, needed when multiple files are stitched together. - * Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped - * music (.map/lin). Subfiles always share header, except num_samples. */ -static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) { - int num_samples = 0; - int multiple_schl = 0; - - /* calc num_samples as playable data size varies between files/blocks */ - { - vgmstream->next_block_offset = start_offset; - do { - uint32_t block_id = read_32bitBE(vgmstream->next_block_offset + 0x00, streamFile); - if (block_id == EA_BLOCKID_HEADER) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ - multiple_schl = 1; - - block_update_ea_schl(vgmstream->next_block_offset, vgmstream); - num_samples += vgmstream->current_block_samples; - } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - - /* reset after getting samples */ - block_update(start_offset, vgmstream); + /* Stream size is almost never provided in bank files so we have to calc it manually */ + if (vgmstream->current_block_samples != 0) { + stream_size += vgmstream->next_block_offset - vgmstream->current_block_offset - 0x0c; + } } + /* reset once we're done */ + block_update(start_offset, vgmstream); + /* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */ - if (multiple_schl) { - ; VGM_LOG("EA SCHl: multiple SCHl found\n"); - return num_samples; - } else { - return 0; + if (standalone && multiple_schl) { + VGM_LOG("EA SCHl: multiple SCHl found\n"); + if (num_samples > vgmstream->num_samples) { + vgmstream->num_samples = num_samples; + } } + + vgmstream->stream_size = stream_size; } /* find data start offset inside the first SCDl; not very elegant but oh well */ From 4d73443bae26cd312a52236ab3bfb6a14ed3bb7c Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Fri, 28 Dec 2018 02:39:19 +0300 Subject: [PATCH 12/12] Removed unused local variable --- src/meta/ea_eaac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index ba7b7040..8c0d274d 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -472,7 +472,7 @@ VGMSTREAM * init_vgmstream_ea_mpf_mus_new(STREAMFILE *streamFile) { int16_t(*read_16bit)(off_t, STREAMFILE*); STREAMFILE *musFile = NULL; VGMSTREAM *vgmstream = NULL; - int target_stream = streamFile->stream_index, total_streams; + int target_stream = streamFile->stream_index; /* check extension */ if (!check_extensions(streamFile, "mpf"))