From 15cc60e604ae1f5f71d9693e2bc550cdf96d2841 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 17:06:39 +0300 Subject: [PATCH 1/7] Some organization and better endianness checks in EA parsers --- src/layout/blocked_ea_schl.c | 4 +- src/meta/ea_eaac.c | 13 +++--- src/meta/ea_schl.c | 77 +++++++++++++++++++----------------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index aa21e206..6293feb9 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -23,7 +23,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { { uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile); - if (guess_endianness32bit(block_offset + 0x04,streamFile)) /* size is always LE, except in early SS/MAC */ + if (vgmstream->codec_version & 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); @@ -180,7 +180,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (vgmstream->codec_version == 1) { + if (vgmstream->codec_version & 0x01) { for (i = 0; i < vgmstream->channels; i++) { //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index d9b82306..eaf593c8 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -307,18 +307,17 @@ fail: /* EA S10A header - seen inside new ABK files. Putting it here in case it's encountered stand-alone. */ static VGMSTREAM * parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset) { - uint32_t header, num_sounds; + uint32_t num_sounds; off_t snr_offset, sns_offset; STREAMFILE *astFile = NULL; VGMSTREAM *vgmstream; /* header is always big endian */ - /* 0x00 - header magic */ - /* 0x04 - zero */ - /* 0x08 - number of files */ - /* 0x0C - offsets table */ - header = read_32bitBE(offset + 0x00, streamFile); - if (header != 0x53313041) /* "S10A" */ + /* 0x00: header magic */ + /* 0x04: zero */ + /* 0x08: number of files */ + /* 0x0C: offsets table */ + if (read_32bitBE(offset + 0x00, streamFile) != 0x53313041) /* "S10A" */ goto fail; num_sounds = read_32bitBE(offset + 0x08, streamFile); diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 34274d95..efbf8110 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -53,6 +53,7 @@ #define EA_BLOCKID_HEADER 0x5343486C /* "SCHl" */ #define EA_BLOCKID_COUNT 0x5343436C /* "SCCl" */ #define EA_BLOCKID_DATA 0x5343446C /* "SCDl" */ +#define EA_BLOCKID_LOOP 0x53434C6C /* "SCLl */ #define EA_BLOCKID_END 0x5343456C /* "SCEl" */ /* Localized block headers, Sxyy - where x is block ID and yy is lang code (e.g. "SHEN"), used in videos */ @@ -97,7 +98,7 @@ typedef struct { 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 int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length); +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); @@ -140,13 +141,8 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { goto fail; /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ - if (read_32bitBE(0x00,streamFile) == EA_BNK_HEADER_LE || - read_32bitBE(0x00,streamFile) == EA_BNK_HEADER_BE) - offset = 0; - else if (read_32bitBE(0x100,streamFile) == EA_BNK_HEADER_LE) + if (read_32bitBE(0x100,streamFile) == EA_BNK_HEADER_LE) offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ - else - goto fail; return parse_bnk_header(streamFile, offset, streamFile->stream_index, 0); @@ -263,10 +259,6 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (!bnk_offset) goto fail; - if (read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_LE && - read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_BE) - 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); if (!vgmstream) @@ -453,14 +445,17 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to size_t header_size; ea_header ea = { 0 }; - if (guess_endianness32bit(offset + 0x04, streamFile)) /* size is always LE, except in early SS/MAC */ + if (guess_endianness32bit(offset + 0x04, streamFile)) { /* size is always LE, except in early SS/MAC */ header_size = read_32bitBE(offset + 0x04, streamFile); - else + ea.codec_version |= 0x02; + } + else { header_size = read_32bitLE(offset + 0x04, streamFile); + } header_offset = offset + 0x08; - if (!parse_variable_header(streamFile, &ea, header_offset, header_size - 0x08)) + if (!parse_variable_header(streamFile, &ea, header_offset, header_size - 0x08, 0)) goto fail; start_offset = offset + header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ @@ -482,14 +477,19 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta int i, bnk_version; int total_bnk_sounds, real_bnk_sounds = 0; - /* use header size as endianness flag */ - if (guess_endianness32bit(offset + 0x08,streamFile)) { + /* check header */ + /* BNK header endianness is platform-native */ + if (read_32bitBE(start_offset + 0x00, streamFile) == EA_BNK_HEADER_BE) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; - } else { + } + else if (read_32bitBE(start_offset + 0x00, streamFile) == EA_BNK_HEADER_LE) { read_32bit = read_32bitLE; read_16bit = read_16bitLE; } + else { + goto fail; + } bnk_version = read_8bit(offset + 0x04,streamFile); total_bnk_sounds = read_16bit(offset + 0x06,streamFile); @@ -538,7 +538,7 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta if (target_stream < 0 || header_offset == 0 || real_bnk_sounds < 1) goto fail; - if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset)) + if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset, bnk_version)) goto fail; /* fix absolute offsets so it works in next funcs */ @@ -550,11 +550,6 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ - /* it looks like BNKs never store ADPCM history for EA-XA */ - if (ea.codec2 == EA_CODEC2_EAXA) { - ea.codec_version = 0; - } - /* rest is common */ return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams ? total_streams : real_bnk_sounds); @@ -619,7 +614,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ switch (ea->codec2) { case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */ - if (ea->codec1 == EA_CODEC1_EAXA) { + if (ea->version == EA_VERSION_V0) { if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1) vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */ else @@ -768,7 +763,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (vgmstream->codec_version == 1) { + if (vgmstream->codec_version & 0x01) { for (i = 0; i < vgmstream->channels; i++) { //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); @@ -822,11 +817,11 @@ static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) { } /* decodes EA's GSTR/PT header (mostly cross-referenced with sx.exe) */ -static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length) { +static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length, int bnk_version) { off_t offset = begin_offset; uint32_t platform_id; int is_header_end = 0; - + int is_bnk = bnk_version; /* null defaults as 0 can be valid */ ea->version = EA_VERSION_NONE; @@ -1125,16 +1120,24 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be } } - /* special flag: 1=has ADPCM history per block, 0=doesn't */ - if (ea->codec2 == EA_CODEC2_GCADPCM && ea->platform == EA_PLATFORM_3DS) { - ea->codec_version = 1; - } - else if (ea->codec2 == EA_CODEC2_EAXA && ea->codec1 == EA_CODEC1_NONE) { - /* console V2 uses hist, as does PC/MAC V1 (but not later versions) */ - if (ea->version <= EA_VERSION_V1 || - ((ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) - && ea->version == EA_VERSION_V2)) { - ea->codec_version = 1; + /* some codecs have ADPCM hist at the start of every block in streams (but not BNKs) */ + if (!is_bnk) { + if (ea->codec2 == EA_CODEC2_GCADPCM) { + if (ea->platform == EA_PLATFORM_3DS) + ea->codec_version |= 0x01; + } + else if (ea->codec2 == EA_CODEC2_EAXA) { + /* EA-XA has ADPCM hist in earlier versions */ + /* V0, V1: always */ + /* V2: consoles only */ + /* V3: never */ + if (ea->version == EA_VERSION_V1) { + ea->codec_version |= 0x01; + } + else if (ea->version == EA_VERSION_V2) { + if (ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) + ea->codec_version |= 0x01; + } } } From c88dee0ebba330e5077548275e9546c26413ec1c Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 17:07:01 +0300 Subject: [PATCH 2/7] Improved EA ABK support --- src/meta/ea_eaac.c | 6 +++++- src/meta/ea_schl.c | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index eaf593c8..1b21baf1 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -198,7 +198,8 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { goto fail; version = read_32bitBE(0x04, streamFile); - if (version != 0x01010202) + if (version != 0x01010100 && + version != 0x01010202) goto fail; /* use table offset to check endianness */ @@ -221,6 +222,9 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { bnk_target_index = 0xFFFF; ast_offset = 0; + if (!bnk_offset || read_32bitBE(bnk_offset, streamFile) != 0x53313041) /* "S10A" */ + goto fail; + /* set up some common values */ if (header_table_offset == 0x5C) { /* the usual variant */ diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index efbf8110..dedc9823 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -199,6 +199,12 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { target_entry_offset = 0; total_sound_tables = 0; + /* check to avoid clashing with the newer ABK format */ + if (bnk_offset && + read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_LE && + read_32bitBE(bnk_offset, streamFile) != EA_BNK_HEADER_BE) + goto fail; + for (i = 0; i < num_tables; i++) { num_entries = read_8bit(header_table_offset + 0x24, streamFile); base_offset = read_32bit(header_table_offset + 0x2C, streamFile); From 4d0926933b83b61663af9d973f28a27a833ea081 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 17:14:24 +0300 Subject: [PATCH 3/7] Fixed incorrect variable being used --- src/meta/ea_schl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index dedc9823..9cd7dc9c 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -485,11 +485,11 @@ static VGMSTREAM * parse_bnk_header(STREAMFILE *streamFile, off_t offset, int ta /* check header */ /* BNK header endianness is platform-native */ - if (read_32bitBE(start_offset + 0x00, streamFile) == EA_BNK_HEADER_BE) { + if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_BE) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } - else if (read_32bitBE(start_offset + 0x00, streamFile) == EA_BNK_HEADER_LE) { + else if (read_32bitBE(offset + 0x00, streamFile) == EA_BNK_HEADER_LE) { read_32bit = read_32bitLE; read_16bit = read_16bitLE; } From e4c34b7c0be2e9f9cbe0d1efbddab5a6cf446351 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 19:16:04 +0300 Subject: [PATCH 4/7] Added another ABK version stamp [FIFA 12 (3DS)] --- src/meta/ea_schl.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 9cd7dc9c..6a3377e5 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -175,6 +175,7 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { version = read_32bitBE(0x04, streamFile); if (version != 0x01010000 && version != 0x01010100 && + version != 0x01010200 && version != 0x02010000 && version != 0x02010100 && version != 0x02010202) @@ -767,15 +768,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->ch[i].offset = ea->offsets[i]; } } - - /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (vgmstream->codec_version & 0x01) { - for (i = 0; i < vgmstream->channels; i++) { - //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); - //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); - vgmstream->ch[i].offset += 4; - } - } } else if (vgmstream->layout_type == layout_blocked_ea_schl) { /* regular SCHls, except ATRAC3plus */ @@ -1137,7 +1129,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be /* V0, V1: always */ /* V2: consoles only */ /* V3: never */ - if (ea->version == EA_VERSION_V1) { + if (ea->version <= EA_VERSION_V1) { ea->codec_version |= 0x01; } else if (ea->version == EA_VERSION_V2) { From ca9cfc6917ee21eddce1627fcfe675431a87738d Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 19:23:51 +0300 Subject: [PATCH 5/7] Removed the supposed version check from ABK parsers The only reason I put it there is to distinguish old ABK from new ABK but then it turned out that they can have matching values in that field so... yeah, let's just do it another way --- src/meta/ea_eaac.c | 7 +------ src/meta/ea_schl.c | 11 +---------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 1b21baf1..f2a3a2ad 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -182,7 +182,7 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { int is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset; off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off; - uint32_t i, j, k, version, num_sounds, total_sound_tables; + uint32_t i, j, k, num_sounds, total_sound_tables; uint16_t num_tables, bnk_index, bnk_target_index; uint8_t num_entries, extra_entries; off_t sound_table_offsets[0x2000]; @@ -197,11 +197,6 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */ goto fail; - version = read_32bitBE(0x04, streamFile); - if (version != 0x01010100 && - version != 0x01010202) - goto fail; - /* use table offset to check endianness */ if (guess_endianness32bit(0x1C,streamFile)) { read_32bit = read_32bitBE; diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 6a3377e5..048e50aa 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -156,7 +156,7 @@ fail: VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { int bnk_target_stream, is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; off_t bnk_offset, header_table_offset, base_offset, value_offset, table_offset, entry_offset, target_entry_offset, schl_offset; - uint32_t i, j, k, version, num_sounds, total_sound_tables; + uint32_t i, j, k, num_sounds, total_sound_tables; uint16_t num_tables; uint8_t sound_type, num_entries; off_t sound_table_offsets[0x2000]; @@ -172,15 +172,6 @@ VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE *streamFile) { if (read_32bitBE(0x00, streamFile) != 0x41424B43) /* "ABKC" */ goto fail; - version = read_32bitBE(0x04, streamFile); - if (version != 0x01010000 && - version != 0x01010100 && - version != 0x01010200 && - version != 0x02010000 && - version != 0x02010100 && - version != 0x02010202) - goto fail; - /* use table offset to check endianness */ if (guess_endianness32bit(0x1C,streamFile)) { read_32bit = read_32bitBE; From 8d4ead42cf0095944c11f0739c127d82b7377df6 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 20:18:35 +0300 Subject: [PATCH 6/7] EA SCHl: added default 3DS sample rate (32000 Hz) --- 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 048e50aa..7fc34091 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -1102,7 +1102,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be case EA_PLATFORM_X360: ea->sample_rate = 44100; break; case EA_PLATFORM_PSP: ea->sample_rate = 22050; break; case EA_PLATFORM_PS3: ea->sample_rate = 44100; break; - //case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000) + case EA_PLATFORM_3DS: ea->sample_rate = 32000; break; default: VGM_LOG("EA SCHl: unknown default sample rate for platform 0x%02x\n", ea->platform); goto fail; From 6727c0de7b8a03041d8e7dc6377c0dcb551220e5 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 21:20:08 +0300 Subject: [PATCH 7/7] Fixed offset in BNK parser --- src/meta/ea_schl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 7fc34091..a51d57de 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -143,6 +143,8 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ if (read_32bitBE(0x100,streamFile) == EA_BNK_HEADER_LE) offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ + else + offset = 0x00; return parse_bnk_header(streamFile, offset, streamFile->stream_index, 0);