From 15cc60e604ae1f5f71d9693e2bc550cdf96d2841 Mon Sep 17 00:00:00 2001 From: NicknineTheEagle Date: Wed, 8 Aug 2018 17:06:39 +0300 Subject: [PATCH] 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; + } } }