mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
Add EA BNK support and fix minor EA header parsing defects
This commit is contained in:
parent
604f5ef507
commit
312b68c0b7
@ -877,6 +877,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_PC_XA30, "Reflections XA30 PC header"},
|
||||
{meta_WII_04SW, "Reflections 04SW header"},
|
||||
{meta_TXTH, "TXTH Generic Header"},
|
||||
{meta_EA_BNK, "Electronic Arts BNK header"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis"},
|
||||
|
@ -92,7 +92,7 @@ void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
int is_interleaved = vgmstream->coding_type == coding_EA_XA_int;
|
||||
size_t interleave;
|
||||
|
||||
/* read ADPCM history from all channels before data (not actually used in sx.exe) */
|
||||
/* read ADPCM history from all channels before data (not actually read in sx.exe) */
|
||||
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile);
|
||||
//vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile);
|
||||
|
||||
@ -110,9 +110,8 @@ void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
|
||||
}
|
||||
|
||||
/* read ADPCM history before each channel if needed (not actually used in sx.exe) */
|
||||
if ((vgmstream->coding_type == coding_NGC_DSP) ||
|
||||
(vgmstream->coding_type == coding_EA_XA_V2 && vgmstream->codec_version == 1)) {
|
||||
/* read ADPCM history before each channel if needed (not actually read in sx.exe) */
|
||||
if (vgmstream->codec_version == 1) {
|
||||
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);
|
||||
|
@ -26,8 +26,8 @@
|
||||
/* codec constants (undefined are probably reserved, ie.- sx.exe encodes PCM24/DVI but no platform decodes them) */
|
||||
/* CODEC1 values were used early, then they migrated to CODEC2 values */
|
||||
#define EA_CODEC1_NONE -1
|
||||
//#define EA_CODEC1_S16BE 0x00 //LE too?
|
||||
//#define EA_CODEC1_VAG 0x01 ?
|
||||
#define EA_CODEC1_PCM 0x00
|
||||
#define EA_CODEC1_VAG 0x01 // unsure
|
||||
#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, Fifa 98 SAT
|
||||
#define EA_CODEC1_MT10 0x09
|
||||
//#define EA_CODEC1_N64 ?
|
||||
@ -49,12 +49,12 @@
|
||||
#define EA_MAX_CHANNELS 6
|
||||
|
||||
typedef struct {
|
||||
uint8_t id;
|
||||
int32_t num_samples;
|
||||
int32_t sample_rate;
|
||||
int32_t channels;
|
||||
int32_t platform;
|
||||
int32_t version;
|
||||
int32_t bps;
|
||||
int32_t codec1;
|
||||
int32_t codec2;
|
||||
|
||||
@ -66,18 +66,19 @@ typedef struct {
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
int codec_version;
|
||||
} ea_header;
|
||||
|
||||
static int parse_stream_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);
|
||||
static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset);
|
||||
static int get_ea_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
static off_t get_ea_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
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);
|
||||
|
||||
|
||||
/* EA SCHl - from EA games (roughly 1997~2010, generated by EA Canada's sx.exe / Sound eXchange) */
|
||||
/* 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) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
off_t start_offset, header_offset;
|
||||
size_t header_size;
|
||||
ea_header ea;
|
||||
|
||||
@ -97,51 +98,170 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
header_size = read_32bitLE(0x04,streamFile);
|
||||
if (header_size > 0x00F00000) /* size is always LE, except in early SS/MAC */
|
||||
header_size = read_32bitBE(0x04,streamFile);
|
||||
header_offset = 0x08;
|
||||
|
||||
memset(&ea,0,sizeof(ea_header));
|
||||
if (!parse_stream_header(streamFile,&ea, 0x08, header_size-4-4))
|
||||
if (!parse_variable_header(streamFile,&ea, 0x08, header_size - header_offset))
|
||||
goto fail;
|
||||
|
||||
start_offset = header_size; /* start in "SCCl" or very rarely "SCDl" (skipped in block layout, though) */
|
||||
if (read_32bitBE(start_offset,streamFile) != 0x5343436C && read_32bitBE(start_offset,streamFile) != 0x5343446C ) /* "SCCl" / "SCDl" */
|
||||
start_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, 1);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
|
||||
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
|
||||
off_t start_offset, header_offset, offset;
|
||||
size_t header_size;
|
||||
ea_header ea;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
int i, bnk_version;
|
||||
int target_stream = 0, total_streams;
|
||||
|
||||
|
||||
/* check extension */
|
||||
/* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */
|
||||
if (!check_extensions(streamFile,"bnk,sdt,mus"))
|
||||
goto fail;
|
||||
|
||||
/* check header (doesn't use EA blocks, otherwise very similar to SCHl) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */
|
||||
read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */
|
||||
offset = 0;
|
||||
else if (read_32bitBE(0x100,streamFile) == 0x424E4B6C) /* "BNKl" (common) */
|
||||
offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */
|
||||
else
|
||||
goto fail;
|
||||
|
||||
/* use header size as endianness flag */
|
||||
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
bnk_version = read_8bit(offset + 0x04,streamFile);
|
||||
total_streams = read_16bit(offset + 0x06,streamFile);
|
||||
/* check multi-streams */
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
|
||||
|
||||
switch(bnk_version) {
|
||||
case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */
|
||||
header_size = read_32bit(offset + 0x08,streamFile); /* full size */
|
||||
header_offset = offset + 0x0c + 0x04*(target_stream-1) + read_32bit(offset + 0x0c + 0x04*(target_stream-1),streamFile);
|
||||
break;
|
||||
|
||||
case 0x04: /* mid (last used in PSX banks) */
|
||||
case 0x05: /* late (generated by sx.exe ~v2+) */
|
||||
/* 0x08: header/file size, 0x0C: file size/null, 0x10: always null */
|
||||
header_size = get_streamfile_size(streamFile); /* unknown (header is variable and may have be garbage until data) */
|
||||
header_offset = offset + 0x14 + 0x04*(target_stream-1) + read_32bit(offset + 0x14 + 0x04*(target_stream-1),streamFile);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA BNK: unknown version %x\n", bnk_version);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset))
|
||||
goto fail;
|
||||
|
||||
/* fix absolute offsets so it works in next funcs */
|
||||
if (offset) {
|
||||
for (i = 0; i < ea.channels; i++) {
|
||||
ea.coefs[i] += offset;
|
||||
ea.offsets[i] += offset;
|
||||
}
|
||||
}
|
||||
|
||||
start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */
|
||||
|
||||
/* special case found in some tests (pcstream had hist, pcbnk no hist, no patch diffs)
|
||||
* I think this works but what decides if hist is used or not a secret to everybody */
|
||||
if (ea.codec2 == EA_CODEC2_EAXA && ea.codec1 == EA_CODEC1_NONE && ea.version == EA_VERSION_V1) {
|
||||
ea.codec_version = 0;
|
||||
}
|
||||
|
||||
|
||||
/* rest is common */
|
||||
return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams);
|
||||
|
||||
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) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int i, ch;
|
||||
int is_bnk = bnk_version;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
|
||||
vgmstream = allocate_vgmstream(ea->channels, ea->loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = ea.sample_rate;
|
||||
vgmstream->num_samples = ea.num_samples;
|
||||
vgmstream->loop_start_sample = ea.loop_start;
|
||||
vgmstream->loop_end_sample = ea.loop_end;
|
||||
vgmstream->sample_rate = ea->sample_rate;
|
||||
vgmstream->num_samples = ea->num_samples;
|
||||
vgmstream->loop_start_sample = ea->loop_start;
|
||||
vgmstream->loop_end_sample = ea->loop_end;
|
||||
|
||||
vgmstream->codec_endian = ea.big_endian;
|
||||
vgmstream->codec_endian = ea->big_endian;
|
||||
vgmstream->codec_version = ea->codec_version;
|
||||
|
||||
vgmstream->layout_type = layout_ea_blocked;
|
||||
vgmstream->meta_type = meta_EA_SCHL;
|
||||
vgmstream->meta_type = is_bnk ? meta_EA_BNK : meta_EA_SCHL;
|
||||
|
||||
if (is_bnk) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* BNKs usually have absolute offsets for all channels ("full" interleave) except in some versions */
|
||||
if (vgmstream->channels > 1 && ea->codec1 == EA_CODEC1_PCM) {
|
||||
int interleave = (vgmstream->num_samples * (ea->bps == 8 ? 0x01 : 0x02)); /* full interleave */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
ea->offsets[i] = ea->offsets[0] + interleave*i;
|
||||
}
|
||||
}
|
||||
else if (vgmstream->channels > 1 && ea->codec1 == EA_CODEC1_VAG) {
|
||||
int interleave = (vgmstream->num_samples / 28 * 16); /* full interleave */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
ea->offsets[i] = ea->offsets[0] + interleave*i;
|
||||
}
|
||||
}
|
||||
else if (vgmstream->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) {
|
||||
/* pcstream+gcadpcm with sx.exe v2, this is probably an bug (even with this parts of the wave are off) */
|
||||
int interleave = (vgmstream->num_samples / 14 * 8); /* full interleave */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
ea->offsets[i] = ea->offsets[0] + interleave*i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
vgmstream->layout_type = layout_ea_blocked;
|
||||
}
|
||||
|
||||
if (is_bnk)
|
||||
vgmstream->num_streams = total_streams;
|
||||
|
||||
/* 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). */
|
||||
switch (ea.codec2) {
|
||||
switch (ea->codec2) {
|
||||
|
||||
case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */
|
||||
if (ea.codec1 == EA_CODEC1_EAXA) {
|
||||
if (ea.version == EA_VERSION_V0 && ea.platform != EA_PLATFORM_SAT)
|
||||
if (ea->codec1 == EA_CODEC1_EAXA) {
|
||||
if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1)
|
||||
vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */
|
||||
else
|
||||
vgmstream->coding_type = coding_EA_XA_int; /* interleaved mono streams */
|
||||
}
|
||||
else { /* later revision with PCM blocks and slighty modified decoding */
|
||||
vgmstream->coding_type = coding_EA_XA_V2;
|
||||
|
||||
/* 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)) {
|
||||
vgmstream->codec_version = 1; /* 1=has ADPCM history per block (early), 0=doesn't */
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -170,12 +290,11 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
|
||||
/* get them coefs (start offsets are not necessarily ordered) */
|
||||
{
|
||||
int ch, i;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = ea.big_endian ? read_16bitBE : read_16bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = ea->big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
for (ch=0; ch < vgmstream->channels; ch++) {
|
||||
for (i=0; i < 16; i++) { /* actual size 0x21, last byte unknown */
|
||||
vgmstream->ch[ch].adpcm_coef[i] = read_16bit(ea.coefs[ch] + i*2, streamFile);
|
||||
vgmstream->ch[ch].adpcm_coef[i] = read_16bit(ea->coefs[ch] + i*2, streamFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,7 +303,9 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
#ifdef VGM_USE_MPEG
|
||||
case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */
|
||||
case EA_CODEC2_LAYER3: { /* MPEG Layer III, aka MP3 */
|
||||
off_t mpeg_start_offset = get_ea_mpeg_start_offset(streamFile, start_offset, &ea);
|
||||
off_t mpeg_start_offset = is_bnk ?
|
||||
start_offset :
|
||||
get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea);
|
||||
if (!mpeg_start_offset) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_codec_data_interleaved(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, 0);
|
||||
@ -198,23 +319,77 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
|
||||
case EA_CODEC2_EALAYER3: /* MP3 variant */
|
||||
default:
|
||||
VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea.codec2, ea.platform);
|
||||
VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* fix num_samples for multifiles */
|
||||
{
|
||||
int total_samples = get_ea_total_samples(streamFile, start_offset, &ea);
|
||||
if (total_samples > vgmstream->num_samples)
|
||||
vgmstream->num_samples = total_samples;
|
||||
}
|
||||
|
||||
/* open files; channel offsets are updated below */
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
ea_schl_block_update(start_offset,vgmstream);
|
||||
/* fix num_samples for streams with multiple SCHl */
|
||||
if (!is_bnk) {
|
||||
int total_samples = get_ea_stream_total_samples(streamFile, start_offset, ea);
|
||||
if (total_samples > vgmstream->num_samples)
|
||||
vgmstream->num_samples = total_samples;
|
||||
}
|
||||
|
||||
|
||||
if (is_bnk) {
|
||||
/* setup channel offsets */
|
||||
if (vgmstream->coding_type == coding_EA_XA) { /* shared */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = ea->offsets[0];
|
||||
}
|
||||
//} else if (vgmstream->layout_type == layout_interleave) { /* interleaved */
|
||||
// for (i = 0; i < vgmstream->channels; i++) {
|
||||
// vgmstream->ch[i].offset = ea->offsets[0] + vgmstream->interleave_block_size*i;
|
||||
// }
|
||||
} else { /* absolute */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = ea->offsets[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* setup ADPCM hist */
|
||||
switch(vgmstream->coding_type) {
|
||||
/* id, size, samples, hists-per-channel, stereo/interleaved data */
|
||||
case coding_EA_XA:
|
||||
/* read ADPCM history from all channels before data (not actually read in sx.exe) */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile);
|
||||
//vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile);
|
||||
vgmstream->ch[i].offset += vgmstream->channels*0x04;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* read ADPCM history before each channel if needed (not actually read in sx.exe) */
|
||||
if (vgmstream->codec_version == 1) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset channel sub offset for codecs using it */
|
||||
if (vgmstream->coding_type == coding_EA_XA
|
||||
|| vgmstream->coding_type == coding_EA_XA_int
|
||||
|| vgmstream->coding_type == coding_EA_XA_V2) {
|
||||
for(i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].channel_start_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* setup first block to update offsets */
|
||||
ea_schl_block_update(start_offset,vgmstream);
|
||||
}
|
||||
|
||||
|
||||
return vgmstream;
|
||||
|
||||
@ -249,11 +424,12 @@ 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_stream_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) {
|
||||
off_t offset = begin_offset;
|
||||
uint32_t platform_id;
|
||||
int is_header_end = 0;
|
||||
|
||||
memset(ea,0,sizeof(ea_header));
|
||||
|
||||
/* null defaults as 0 can be valid */
|
||||
ea->version = EA_VERSION_NONE;
|
||||
@ -278,27 +454,37 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* parse mini-chunks/tags (variable, ommited if default exists) */
|
||||
while (offset - begin_offset < max_length) {
|
||||
|
||||
/* parse mini-chunks/tags (variable, ommited if default exists; some are removed in later versions of sx.exe) */
|
||||
while (!is_header_end && offset - begin_offset < max_length) {
|
||||
uint8_t patch_type = read_8bit(offset,streamFile);
|
||||
offset++;
|
||||
|
||||
switch(patch_type) {
|
||||
|
||||
case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFD */
|
||||
case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */
|
||||
if (!is_header_end)
|
||||
read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0x06: /* always 0x65 */
|
||||
ea->id = read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0x05: /* unknown (usually 0x50 except Madden NFL 3DS: 0x3e800) */
|
||||
case 0x06: /* priority (0..100, always 0x65 for streams, others for BNKs; rarely ommited) */
|
||||
case 0x07: /* unknown (BNK only: 36|3A) */
|
||||
case 0x08: /* release envelope (BNK only) */
|
||||
case 0x09: /* related to playback envelope (BNK only) */
|
||||
case 0x0A: /* bend range (BNK only) */
|
||||
case 0x0B: /* unknown (always 0x02) */
|
||||
case 0x0C: /* pan offset (BNK only) */
|
||||
case 0x0D: /* random pan offset range (BNK only) */
|
||||
case 0x0E: /* volume (BNK only) */
|
||||
case 0x0F: /* random volume range (BNK only) */
|
||||
case 0x10: /* detune (BNK only) */
|
||||
case 0x11: /* random detune range (BNK only) */
|
||||
case 0x13: /* effect bus (0..127) */
|
||||
case 0x14: /* emdedded user data (free size/value) */
|
||||
case 0x1B: /* unknown (movie related?) */
|
||||
case 0x19: /* related to playback envelope (BNK only) */
|
||||
case 0x1B: /* unknown (movie only?) */
|
||||
case 0x1C: /* initial envelope volume (BNK only) */
|
||||
case 0x24: /* master random detune range (BNK only) */
|
||||
read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
@ -307,6 +493,9 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
case 0xFD: /* info section start marker */
|
||||
break;
|
||||
|
||||
case 0x83: /* codec1 defines, used early revisions */
|
||||
ea->codec1 = read_patch(streamFile, &offset);
|
||||
break;
|
||||
case 0xA0: /* codec2 defines */
|
||||
ea->codec2 = read_patch(streamFile, &offset);
|
||||
break;
|
||||
@ -314,15 +503,13 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
case 0x80: /* version, affecting some codecs */
|
||||
ea->version = read_patch(streamFile, &offset);
|
||||
break;
|
||||
case 0x81: /* bits per sample for codec1 PCM */
|
||||
ea->bps = read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0x82: /* channel count */
|
||||
ea->channels = read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0x83: /* codec1 defines, used early revisions */
|
||||
ea->codec1 = read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0x84: /* sample rate */
|
||||
ea->sample_rate = read_patch(streamFile,&offset);
|
||||
break;
|
||||
@ -338,7 +525,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
break;
|
||||
|
||||
/* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */
|
||||
case 0x88: /* absolute offset of ch1 */
|
||||
case 0x88: /* absolute offset of ch1 (or ch1+ch2 for stereo EAXA) */
|
||||
ea->offsets[0] = read_patch(streamFile, &offset);
|
||||
break;
|
||||
case 0x89: /* absolute offset of ch2 */
|
||||
@ -365,7 +552,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
ea->coefs[1] = offset+1;
|
||||
read_patch(streamFile, &offset);
|
||||
break;
|
||||
case 0x91: /* DSP coefs ch3 */
|
||||
case 0x91: /* DSP coefs ch3, and unknown in older versions */
|
||||
ea->coefs[2] = offset+1;
|
||||
read_patch(streamFile, &offset);
|
||||
break;
|
||||
@ -382,9 +569,9 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0x8A: /* long padding? (always 0x00000000) */
|
||||
case 0x8C: /* platform+codec related? */
|
||||
/* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT=01588805E800) */
|
||||
case 0x8A: /* long padding (always 0x00000000) */
|
||||
case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */
|
||||
/* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT10=01588805E800) */
|
||||
case 0x92: /* bytes per sample? */
|
||||
case 0x98: /* embedded time stretch 1 (long data for who-knows-what) */
|
||||
case 0x99: /* embedded time stretch 2 */
|
||||
@ -398,9 +585,8 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
read_patch(streamFile, &offset);
|
||||
break;
|
||||
|
||||
case 0xFF: /* header end (then 0-padded) */
|
||||
case 0xFF: /* header end (then 0-padded so it's 32b aligned) */
|
||||
is_header_end = 1;
|
||||
/* offset always 32 padded (ex SHOW.eam) */
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -409,30 +595,35 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
}
|
||||
}
|
||||
|
||||
/* always 0x65 for non-BNK streams, rarely not specified (FIFA 14, some BNKs) */
|
||||
if (ea->id && ea->id != 0x65)
|
||||
goto fail;
|
||||
if (ea->channels > EA_MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* set defaults per platform, as the header ommits them when possible */
|
||||
/* Set defaults per platform, as the header ommits them when possible */
|
||||
|
||||
ea->loop_flag = (ea->loop_end);
|
||||
|
||||
/* affects blocks/codecs */
|
||||
if (ea->platform == EA_PLATFORM_N64
|
||||
|| ea->platform == EA_PLATFORM_MAC
|
||||
|| ea->platform == EA_PLATFORM_SAT
|
||||
|| ea->platform == EA_PLATFORM_GC_WII
|
||||
|| ea->platform == EA_PLATFORM_X360
|
||||
|| ea->platform == EA_PLATFORM_GENERIC) {
|
||||
ea->big_endian = 1;
|
||||
}
|
||||
|
||||
if (!ea->channels) {
|
||||
ea->channels = 1;
|
||||
}
|
||||
|
||||
/* version affects EAXA and MT codecs, but can be found with all other codecs */
|
||||
/* For PC/MAC V0 is simply no version when codec1 was used
|
||||
* Uncommon, but version 0 (with patch size 0x00) does exist. */
|
||||
/* version mainly affects defaults and minor stuff, can come with all codecs */
|
||||
/* V0 is often just null but it's specified in some files (uncommon, with patch size 0x00) */
|
||||
if (ea->version == EA_VERSION_NONE) {
|
||||
switch(ea->platform) {
|
||||
case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break;
|
||||
case EA_PLATFORM_PC: ea->version = EA_VERSION_V0; break;
|
||||
case EA_PLATFORM_PSX: ea->version = EA_VERSION_V0; break; // assumed
|
||||
case EA_PLATFORM_N64: ea->version = EA_VERSION_V0; break; // assumed
|
||||
case EA_PLATFORM_N64: ea->version = EA_VERSION_V0; break;
|
||||
case EA_PLATFORM_MAC: ea->version = EA_VERSION_V0; break;
|
||||
case EA_PLATFORM_SAT: ea->version = EA_VERSION_V0; break;
|
||||
case EA_PLATFORM_PS2: ea->version = EA_VERSION_V1; break;
|
||||
@ -441,15 +632,34 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
case EA_PLATFORM_X360: ea->version = EA_VERSION_V3; break;
|
||||
case EA_PLATFORM_PSP: ea->version = EA_VERSION_V3; break;
|
||||
case EA_PLATFORM_3DS: ea->version = EA_VERSION_V3; break;
|
||||
case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break;
|
||||
default:
|
||||
VGM_LOG("EA: unknown default version for platform 0x%02x\n", ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* codec1 defaults */
|
||||
if (ea->codec1 == EA_CODEC1_NONE && ea->version == EA_VERSION_V0) {
|
||||
switch(ea->platform) {
|
||||
case EA_PLATFORM_PC: ea->codec1 = EA_CODEC1_PCM; break;
|
||||
case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; // assumed
|
||||
//case EA_PLATFORM_N64: ea->codec1 = EA_CODEC1_N64; break;
|
||||
case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed
|
||||
case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break;
|
||||
default:
|
||||
VGM_LOG("EA: unknown default codec1 for platform 0x%02x\n", ea->platform);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* codec1 to codec2 to simplify later parsing */
|
||||
if (ea->codec1 != EA_CODEC1_NONE && ea->codec2 == EA_CODEC2_NONE) {
|
||||
switch (ea->codec1) {
|
||||
case EA_CODEC1_PCM:
|
||||
ea->codec2 = ea->bps==8 ? EA_CODEC2_S8 : (ea->big_endian ? EA_CODEC2_S16BE : EA_CODEC2_S16LE);
|
||||
break;
|
||||
case EA_CODEC1_VAG: ea->codec2 = EA_CODEC2_VAG; break;
|
||||
case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break;
|
||||
case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break;
|
||||
default:
|
||||
@ -458,7 +668,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
}
|
||||
}
|
||||
|
||||
/* defaults don't seem to change with version or over time, fortunately */
|
||||
/* codec2 defaults */
|
||||
if (ea->codec2 == EA_CODEC2_NONE) {
|
||||
switch(ea->platform) {
|
||||
case EA_PLATFORM_GENERIC: ea->codec2 = EA_CODEC2_EAXA; break;
|
||||
@ -498,14 +708,17 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi
|
||||
}
|
||||
}
|
||||
|
||||
/* affects blocks/codecs */
|
||||
if (ea->platform == EA_PLATFORM_N64
|
||||
|| ea->platform == EA_PLATFORM_MAC
|
||||
|| ea->platform == EA_PLATFORM_SAT
|
||||
|| ea->platform == EA_PLATFORM_GC_WII
|
||||
|| ea->platform == EA_PLATFORM_X360
|
||||
|| ea->platform == EA_PLATFORM_GENERIC) {
|
||||
ea->big_endian = 1;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -519,7 +732,7 @@ fail:
|
||||
/* Some EA files (.mus, .eam, .sng, etc) concat many small subfiles, used as mapped
|
||||
* music (.map/lin). We get total possible samples (counting all subfiles) and pretend
|
||||
* they are a single stream. Subfiles always share header, except num_samples. */
|
||||
static int get_ea_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) {
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) {
|
||||
int num_samples = 0;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
off_t block_offset = start_offset;
|
||||
@ -582,7 +795,7 @@ static int get_ea_total_samples(STREAMFILE* streamFile, off_t start_offset, cons
|
||||
}
|
||||
|
||||
/* find data start offset inside the first SCDl; not very elegant but oh well */
|
||||
static off_t get_ea_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) {
|
||||
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);
|
||||
off_t block_offset = start_offset;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
@ -680,4 +680,6 @@ VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -368,6 +368,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_ngc_ulw,
|
||||
init_vgmstream_pc_xa30,
|
||||
init_vgmstream_wii_04sw,
|
||||
init_vgmstream_ea_bnk,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
@ -457,7 +457,8 @@ typedef enum {
|
||||
meta_XBOX_XMU, /* XBOX XMU */
|
||||
meta_XBOX_XVAS, /* XBOX VAS */
|
||||
|
||||
meta_EA_SCHL, /* Electronic Arts SCHl */
|
||||
meta_EA_SCHL, /* Electronic Arts SCHl */
|
||||
meta_EA_BNK, /* Electronic Arts BNK */
|
||||
meta_EACS_PC, /* Electronic Arts EACS PC */
|
||||
meta_EACS_PSX, /* Electronic Arts EACS PSX */
|
||||
meta_EACS_SAT, /* Electronic Arts EACS SATURN */
|
||||
|
Loading…
x
Reference in New Issue
Block a user