redo EA SCHl support [EA games from roughly 1997~2010]

* tested most variations using EA's tools
* added looping [Need for Speed II PC]
* added DSP support [3DS games]
* fixed PCM/EAXA/MT in some cases [PS2/GC/XBOX/Wii games]
* fixed sample rate/codec defaults, num_samples in some cases
* documented more header patches and other details (ex. "EA ADPCM" is
now EA MT/MicroTalk, as per EA's tools)
* (dev) some internals adapted for EA stuff
This commit is contained in:
bnnm 2017-07-01 23:02:24 +02:00
parent aa93941e64
commit bf34339b41
10 changed files with 822 additions and 548 deletions

View File

@ -106,6 +106,7 @@ DECLARE_MULTIPLE_FILE_TYPE("DXH Audio File (*.DXH)", dxh);
DECLARE_MULTIPLE_FILE_TYPE("EAM Audio File (*.EAM)", eam);
DECLARE_MULTIPLE_FILE_TYPE("EMFF Audio File (*.EMFF)", emff);
DECLARE_MULTIPLE_FILE_TYPE("ENTH Audio File (*.ENTH)", enth);
DECLARE_MULTIPLE_FILE_TYPE("EXA Audio File (*.EXA)", exa);
DECLARE_MULTIPLE_FILE_TYPE("FAG Audio File (*.FAG)", fag);
DECLARE_MULTIPLE_FILE_TYPE("FFW Audio File (*.FFW)", ffw);

View File

@ -75,7 +75,7 @@ void init_get_high_nibble(VGMSTREAM * vgmstream);
/*eaxa_decoder */
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_ea_adpcm(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_mt10(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_maxis_adpcm(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* sdx2_decoder */

View File

@ -1,170 +1,178 @@
#include "coding.h"
#include "../util.h"
int32_t EA_XA_TABLE[28] = {0,0,240,0,460,-208,0x0188,-220,
0x0000,0x0000,0x00F0,0x0000,
0x01CC,0x0000,0x0188,0x0000,
0x0000,0x0000,0x0000,0x0000,
-208,-1,-220,-1,
0x0000,0x0000,0x0000,0x3F70};
/* Various EA ADPCM codecs */
int32_t EA_TABLE[20]= { 0x00000000, 0x000000F0, 0x000001CC, 0x00000188,
0x00000000, 0x00000000, 0xFFFFFF30, 0xFFFFFF24,
0x00000000, 0x00000001, 0x00000003, 0x00000004,
0x00000007, 0x00000008, 0x0000000A, 0x0000000B,
0x00000000, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFC};
static const int32_t EA_XA_TABLE[28] = {
0,0,240,0,
460,-208,0x0188,-220,
0x0000,0x0000,0x00F0,0x0000,
0x01CC,0x0000,0x0188,0x0000,
0x0000,0x0000,0x0000,0x0000,
-208,-1,-220,-1,
0x0000,0x0000,0x0000,0x3F70
};
static const int32_t EA_TABLE[20]= {
0x00000000, 0x000000F0, 0x000001CC, 0x00000188,
0x00000000, 0x00000000, 0xFFFFFF30, 0xFFFFFF24,
0x00000000, 0x00000001, 0x00000003, 0x00000004,
0x00000007, 0x00000008, 0x0000000A, 0x0000000B,
0x00000000, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFC
};
/* EA EAXA, evolved from CDXA */
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
uint8_t frame_info;
int32_t sample_count;
int32_t coef1,coef2;
int i,shift;
off_t channel_offset=stream->channel_start_offset;
first_sample = first_sample%28;
frame_info = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile);
int32_t coef1,coef2;
int i,shift;
off_t channel_offset = stream->channel_start_offset; //suboffset within channel
if(frame_info==0xEE) {
first_sample = first_sample%28;
channel_offset++;
stream->adpcm_history1_32 = read_16bitBE(stream->offset+channel_offset,stream->streamfile);
stream->adpcm_history2_32 = read_16bitBE(stream->offset+channel_offset+2,stream->streamfile);
/* header */
frame_info = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
channel_offset+=4;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count]=read_16bitBE(stream->offset+channel_offset,stream->streamfile);
channel_offset+=2;
}
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), always BE */
stream->adpcm_history1_32 = read_16bitBE(stream->offset+channel_offset+0x00,stream->streamfile);
stream->adpcm_history2_32 = read_16bitBE(stream->offset+channel_offset+0x02,stream->streamfile);
channel_offset += 4;
// Only increment offset on complete frame
if(channel_offset-stream->channel_start_offset==(2*28)+5)
stream->channel_start_offset+=(2*28)+5;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count] = read_16bitBE(stream->offset+channel_offset,stream->streamfile);
channel_offset+=2;
}
} else {
coef1 = EA_XA_TABLE[((frame_info >> 4) & 0x0F) << 1];
coef2 = EA_XA_TABLE[(((frame_info >> 4) & 0x0F) << 1) + 1];
shift = (frame_info & 0x0F) + 8;
channel_offset++;
// Only increment offset on complete frame
if (channel_offset-stream->channel_start_offset == (2*28)+5)
stream->channel_start_offset += (2*28)+5;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset+i/2,stream->streamfile);
int32_t sample = ((((i&1?
sample_byte & 0x0F:
sample_byte >> 4
) << 0x1C) >> shift) +
(coef1 * stream->adpcm_history1_32) + (coef2 * stream->adpcm_history2_32)) >> 8;
outbuf[sample_count] = clamp16(sample);
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
}
channel_offset+=i/2;
} else { /* ADPCM frame */
coef1 = EA_XA_TABLE[(((frame_info >> 4) & 0x0F) << 1)];
coef2 = EA_XA_TABLE[(((frame_info >> 4) & 0x0F) << 1) + 1];
shift = (frame_info & 0x0F) + 8;
// Only increment offset on complete frame
if(channel_offset-stream->channel_start_offset==0x0F)
stream->channel_start_offset+=0x0F;
}
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset+i/2,stream->streamfile);
int32_t sample = ((((i&1?
sample_byte & 0x0F:
sample_byte >> 4
) << 0x1C) >> shift) +
(coef1 * stream->adpcm_history1_32) + (coef2 * stream->adpcm_history2_32)) >> 8;
outbuf[sample_count] = clamp16(sample);
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
}
channel_offset+=i/2;
// Only increment offset on complete frame
if(channel_offset - stream->channel_start_offset == 0x0F)
stream->channel_start_offset += 0x0F;
}
}
void decode_ea_adpcm(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
/* EA MicroTalk 10:1 (aka "EA ADPCM") */
void decode_ea_mt10(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
uint8_t frame_info;
int32_t sample_count;
int32_t coef1,coef2;
int i,shift;
int32_t coef1,coef2;
int i, shift;
VGMSTREAMCHANNEL *stream = &(vgmstream->ch[channel]);
off_t channel_offset=stream->channel_start_offset;
off_t channel_offset=stream->channel_start_offset;
vgmstream->get_high_nibble=!vgmstream->get_high_nibble;
vgmstream->get_high_nibble = !vgmstream->get_high_nibble; /* for stereo subinterleave, L=high nibble, R=low nibble */
first_sample = first_sample%28;
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
first_sample = first_sample%28;
coef1 = EA_TABLE[(vgmstream->get_high_nibble? frame_info & 0x0F: frame_info >> 4)];
coef2 = EA_TABLE[(vgmstream->get_high_nibble? frame_info & 0x0F: frame_info >> 4) + 4];
/* header */ //todo mono/interleave decoder
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
coef1 = EA_TABLE[(vgmstream->get_high_nibble ? frame_info & 0x0F: frame_info >> 4)];
coef2 = EA_TABLE[(vgmstream->get_high_nibble ? frame_info & 0x0F: frame_info >> 4) + 4];
channel_offset++;
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
shift = (vgmstream->get_high_nibble ? frame_info & 0x0F : frame_info >> 4) + 8;
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
shift = (vgmstream->get_high_nibble? frame_info & 0x0F : frame_info >> 4)+8;
channel_offset++;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte;
int32_t sample;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte;
int32_t sample;
sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset+i,stream->streamfile);
sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset+i,stream->streamfile);
sample = ((((vgmstream->get_high_nibble?
sample_byte & 0x0F:
sample_byte >> 4
) << 0x1C) >> shift) +
(coef1 * stream->adpcm_history1_32) + (coef2 * stream->adpcm_history2_32) + 0x80) >> 8;
sample = ((((vgmstream->get_high_nibble?
sample_byte & 0x0F:
sample_byte >> 4
) << 0x1C) >> shift) +
(coef1 * stream->adpcm_history1_32) + (coef2 * stream->adpcm_history2_32) + 0x80) >> 8;
outbuf[sample_count] = clamp16(sample);
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
}
channel_offset+=i;
outbuf[sample_count] = clamp16(sample);
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
}
channel_offset+=i;
// Only increment offset on complete frame
if(channel_offset-stream->channel_start_offset==0x1E)
stream->channel_start_offset+=0x1E;
// Only increment offset on complete frame
if(channel_offset-stream->channel_start_offset==0x1E)
stream->channel_start_offset+=0x1E;
}
/* EA MicroTalk 5:1, unknown variation */
//void decode_ea_mt5(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel)
/* Maxis EAXA, yet another CDXA variation */
void decode_maxis_adpcm(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
uint8_t frame_info;
int32_t sample_count;
int32_t coef1,coef2;
int i,shift;
int frameSize = channelspacing*15;//mono samples have a frame of 15, stereo files have frames of 30
int32_t coef1,coef2;
int i,shift;
int frameSize = channelspacing*15;//mono samples have a frame of 15, stereo files have frames of 30
VGMSTREAMCHANNEL *stream = &(vgmstream->ch[channel]);
off_t channel_offset=stream->channel_start_offset;
off_t channel_offset=stream->channel_start_offset;
first_sample = first_sample%28;
frame_info = read_8bit(channel_offset,stream->streamfile);
first_sample = first_sample%28;
frame_info = read_8bit(channel_offset,stream->streamfile);
coef1 = EA_TABLE[frame_info >> 4];
coef2 = EA_TABLE[(frame_info >> 4) + 4];
shift = (frame_info & 0x0F)+8;
coef1 = EA_TABLE[frame_info >> 4];
coef2 = EA_TABLE[(frame_info >> 4) + 4];
shift = (frame_info & 0x0F)+8;
channel_offset+=channelspacing;
//stream->offset = first_sample*channelspacing/2;
channel_offset+=channelspacing;
//stream->offset = first_sample*channelspacing/2;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte;
int32_t sample;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte;
int32_t sample;
sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile);
sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile);
sample = (((((i&1)?
sample_byte & 0x0F:
sample_byte >> 4
) << 0x1C) >> shift) +
(coef1 * stream->adpcm_history1_32) + (coef2 * stream->adpcm_history2_32) + 0x80) >> 8;
outbuf[sample_count] = clamp16(sample);
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
sample = (((((i&1)?
sample_byte & 0x0F:
sample_byte >> 4
) << 0x1C) >> shift) +
(coef1 * stream->adpcm_history1_32) + (coef2 * stream->adpcm_history2_32) + 0x80) >> 8;
if(i&1)
stream->offset+=channelspacing;
}
channel_offset+=i;
outbuf[sample_count] = clamp16(sample);
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
// Only increment offset on complete frame
if(i&1)
stream->offset+=channelspacing;
}
if(channel_offset-stream->channel_start_offset==frameSize) {
stream->channel_start_offset+=frameSize;
stream->offset=0;
}
channel_offset+=i;
// Only increment offset on complete frame
if(channel_offset-stream->channel_start_offset==frameSize) {
stream->channel_start_offset+=frameSize;
stream->offset=0;
}
}

View File

@ -98,6 +98,7 @@ static const char* extension_list[] = {
"eam",
"emff",
"enth",
"exa",
"fag",
"ffw",
@ -418,8 +419,8 @@ static const coding_info coding_info_list[] = {
{coding_XA, "CD-ROM XA 4-bit ADPCM"},
{coding_XBOX, "XBOX 4-bit IMA ADPCM"},
{coding_XBOX_int, "XBOX 4-bit IMA ADPCM (interleaved)"},
{coding_EA_XA, "Electronic Arts 4-bit ADPCM (XA based)"},
{coding_EA_ADPCM, "Electronic Arts R1 4-bit ADPCM (XA based)"},
{coding_EA_XA, "Electronic Arts EA-XA 4-bit ADPCM"},
{coding_EA_MT10, "Electronic Arts MicroTalk (10:1) 4-bit ADPCM"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
{coding_CBD2, "Cuberoot-delta-exact (CBD2) 8-bit DPCM"},
@ -427,7 +428,7 @@ static const coding_info coding_info_list[] = {
{coding_DVI_IMA, "Intel DVI 4-bit IMA ADPCM"},
{coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (interleaved)"},
{coding_EACS_IMA, "EACS 4-bit IMA ADPCM"},
{coding_MAXIS_ADPCM, "Maxis XA (EA ADPCM Variant)"},
{coding_MAXIS_ADPCM, "Maxis XA ADPCM"},
{coding_IMA_int, "IMA 4-bit ADPCM (interleaved)"},
{coding_IMA, "IMA 4-bit ADPCM"},
{coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"},
@ -592,12 +593,7 @@ static const meta_info meta_info_list[] = {
{meta_XBOX_WAVM, "assumed Xbox WAVM file by .wavm extension"},
{meta_XBOX_RIFF, "Microsoft XWAV RIFF header"},
{meta_DSP_STR, "assumed Conan Gamecube STR File by .str extension"},
{meta_EAXA_R2, "Electronic Arts XA R2"},
{meta_EAXA_R3, "Electronic Arts XA R3"},
{meta_EA_ADPCM, "Electronic Arts XA R1"},
{meta_EA_IMA, "Electronic Arts container with IMA blocks"},
{meta_EAXA_PSX, "Electronic Arts With PSX ADPCM"},
{meta_EA_PCM, "Electronic Arts With PCM"},
{meta_EA_SCHL, "Electronic Arts SCHl header"},
{meta_CFN, "tri-Crescendo CAF Header"},
{meta_PS2_VPK, "VPK Header"},
{meta_GENH, "GENH Generic Header"},
@ -621,9 +617,9 @@ static const meta_info meta_info_list[] = {
{meta_HGC1, "Knights of the Temple 2 hgC1 Header"},
{meta_AUS, "Capcom AUS Header"},
{meta_RWS, "RenderWare RWS header"},
{meta_EACS_PC, "EACS Header (PC)"},
{meta_EACS_PSX, "EACS Header (PSX)"},
{meta_EACS_SAT, "EACS Header (SATURN)"},
{meta_EACS_PC, "Electronic Arts EACS header (PC)"},
{meta_EACS_PSX, "Electronic Arts EACS header (PSX)"},
{meta_EACS_SAT, "Electronic Arts EACS header (SATURN)"},
{meta_SL3, "SL3 Header"},
{meta_FSB1, "FMOD Sample Bank (FSB1) Header"},
{meta_FSB2, "FMOD Sample Bank (FSB2) Header"},

View File

@ -8,7 +8,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
int samples_this_block;
if (frame_size == 0) {
if (vgmstream->current_block_samples) {
samples_this_block = vgmstream->current_block_samples;
} else if (frame_size == 0) {
/* assume 4 bit */
/* TODO: get_vgmstream_frame_size() really should return bits... */
samples_this_block = vgmstream->current_block_size * 2 * samples_per_frame;
@ -20,7 +22,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
int samples_to_do;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
if (frame_size == 0) {
if (vgmstream->current_block_samples) {
samples_this_block = vgmstream->current_block_samples;
} else if (frame_size == 0) {
samples_this_block = vgmstream->current_block_size * 2 * samples_per_frame;
} else {
samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;
@ -141,7 +145,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
frame_size = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
if (frame_size == 0) {
if (vgmstream->current_block_samples) {
samples_this_block = vgmstream->current_block_samples;
} else if (frame_size == 0) {
samples_this_block = vgmstream->current_block_size * 2 * samples_per_frame;
} else {
samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;

View File

@ -5,145 +5,160 @@
/* set up for the block at the given offset */
void ea_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
uint32_t id;
size_t file_size, block_size = 0, block_samples;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
init_get_high_nibble(vgmstream);
// Search for next SCDL or SCEl block ...
do {
block_offset+=4;
if(block_offset>=(off_t)get_streamfile_size(vgmstream->ch[0].streamfile)) {
vgmstream->next_block_offset=block_offset;
return;
}
} while (read_32bitBE(block_offset,vgmstream->ch[0].streamfile)!=0x5343446C);
init_get_high_nibble(vgmstream); /* swap nibble for codecs with stereo subinterleave */
// reset channel offset
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].channel_start_offset=0;
}
/* find target block ID and skip the rest */
file_size = get_streamfile_size(streamFile);
while (block_offset < file_size) {
id = read_32bitBE(block_offset+0x00,streamFile);
block_size = read_32bitLE(block_offset+0x04,streamFile);
if (block_size > 0xF0000000) /* size size is always LE, except in early MAC apparently */
block_size = read_32bitBE(block_offset+0x04,streamFile);
if (id == 0x5343446C) /* "SCDl" data block found */
break;
block_offset += block_size; /* size includes header */
/* Some EA files concat many small subfiles, for mapped music (.map/lin), so after SCEl
* there may be a new SCHl. We'll find it and pretend they are a single stream. */
if (id == 0x5343456C && block_offset + 0x80 > file_size)
return;
if (id == 0x5343456C) { /* "SCEl" end block found */
/* Usually there is padding between SCEl and SCHl (aligned to 0x80) */
block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* also 32b-aligned */
for (i = 0; i < 0x80 / 4; i++) {
id = read_32bitBE(block_offset,streamFile);
if (id == 0x5343486C) /* "SCHl" new header block found */
break; /* next loop will parse and skip it */
block_offset += 0x04;
}
}
if (block_offset > file_size)
return;
if (id == 0 || id == 0xFFFFFFFF)
return; /* probably hit padding or EOF */
}
if (block_offset > file_size)
return;
/* use num_samples from header if possible; don't calc as rarely data may have padding (ex. PCM8) or not possible (ex. MP3) */
switch(vgmstream->coding_type) {
case coding_PSX:
block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);
break;
default:
block_samples = read_32bit(block_offset+0x08,streamFile);
break;
}
/* set new channel offsets */
switch(vgmstream->coding_type) {
case coding_PSX:
for (i = 0; i < vgmstream->channels; i++) {
size_t interleave = ((block_size-0x10)/vgmstream->channels) * i;
vgmstream->ch[i].offset = block_offset + 0x10 + interleave;
}
/* at 0x08/0x0c: unknown */
break;
default:
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start;
if (vgmstream->coding_type == coding_EA_MT10 && vgmstream->codec_version == 0)
channel_start = 0; /* MT10 R1 (codec1 v0) uses stereo, R2 (codec2 v1+) interleaved mono */
else
channel_start = read_32bit(block_offset+0x0C+(0x04*i),streamFile);
vgmstream->ch[i].offset = block_offset + 0x0C+(0x04*vgmstream->channels) + channel_start;
}
break;
}
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset+read_32bitLE(block_offset+4,vgmstream->ch[0].streamfile)-4;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_size = 0;
vgmstream->current_block_samples = block_samples;
if(vgmstream->ea_big_endian) {
vgmstream->current_block_size = read_32bitBE(block_offset+8,vgmstream->ch[0].streamfile);
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset=read_32bitBE(block_offset+0x0C+(i*4),vgmstream->ch[0].streamfile)+(4*vgmstream->channels);
vgmstream->ch[i].offset+=vgmstream->current_block_offset+0x0C;
}
vgmstream->current_block_size /= 28;
/* read ADPCM history (there is a small diff vs decoded hist) */
if (vgmstream->coding_type == coding_NGC_DSP
|| (vgmstream->coding_type == coding_EA_XA && vgmstream->codec_version == 0)
) {
//int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE;
for (i = 0; i < vgmstream->channels; i++) {
/* makes the output glitchy in rare cases (Harry Potter and the Chamber of Secrets (Xbox)) */
//vgmstream->ch[i].adpcm_history2_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile);
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile);
vgmstream->ch[i].offset += 4;
}
}
} else {
switch(vgmstream->coding_type) {
case coding_PSX:
vgmstream->ch[0].offset=vgmstream->current_block_offset+0x10;
vgmstream->ch[1].offset=(read_32bitLE(block_offset+0x04,vgmstream->ch[0].streamfile)-0x10)/vgmstream->channels;
vgmstream->ch[1].offset+=vgmstream->ch[0].offset;
vgmstream->current_block_size=read_32bitLE(block_offset+0x04,vgmstream->ch[0].streamfile)-0x10;
vgmstream->current_block_size/=vgmstream->channels;
break;
case coding_EA_ADPCM:
vgmstream->current_block_size = read_32bitLE(block_offset+4,vgmstream->ch[0].streamfile);
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset=vgmstream->current_block_offset+0x0C+(4*vgmstream->channels);
vgmstream->ch[i].adpcm_history1_32=(uint32_t)read_16bitLE(vgmstream->current_block_offset+0x0c+(i*4),vgmstream->ch[0].streamfile);
vgmstream->ch[i].adpcm_history2_32=(uint32_t)read_16bitLE(vgmstream->current_block_offset+0x0e +(i*4),vgmstream->ch[0].streamfile);
}
break;
case coding_PCM16BE:
vgmstream->current_block_size = read_32bitLE(block_offset+4,vgmstream->ch[0].streamfile)-0x14;
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset=block_offset+0x14+vgmstream->current_block_size/vgmstream->channels*i;
}
vgmstream->current_block_size/=vgmstream->channels;
break;
case coding_PCM16LE_int:
vgmstream->current_block_size = read_32bitLE(block_offset+4,vgmstream->ch[0].streamfile)-0x0C;
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset=block_offset+0x0C+(i*2);
}
vgmstream->current_block_size/=2;
vgmstream->current_block_size-=2;
break;
case coding_XBOX:
vgmstream->current_block_size = read_32bitLE(block_offset+0x10,vgmstream->ch[0].streamfile);
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset=read_32bitLE(block_offset+0x0C+(i*4),vgmstream->ch[0].streamfile)+(4*vgmstream->channels);
vgmstream->ch[i].offset+=vgmstream->current_block_offset+0x0C;
}
break;
default:
vgmstream->current_block_size = read_32bitLE(block_offset+8,vgmstream->ch[0].streamfile);
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset=read_32bitLE(block_offset+0x0C+(i*4),vgmstream->ch[0].streamfile)+(4*vgmstream->channels);
vgmstream->ch[i].offset+=vgmstream->current_block_offset+0x0C;
}
vgmstream->current_block_size /= 28;
}
}
if((vgmstream->ea_compression_version<3) && (vgmstream->coding_type!=coding_PSX) && (vgmstream->coding_type!=coding_EA_ADPCM) && (vgmstream->coding_type!=coding_XBOX) && (vgmstream->coding_type!=coding_PCM16BE)) {
for(i=0;i<vgmstream->channels;i++) {
if(vgmstream->ea_big_endian) {
vgmstream->ch[i].adpcm_history1_32=read_16bitBE(vgmstream->ch[i].offset,vgmstream->ch[0].streamfile);
vgmstream->ch[i].adpcm_history2_32=read_16bitBE(vgmstream->ch[i].offset+2,vgmstream->ch[0].streamfile);
} else {
vgmstream->ch[i].adpcm_history1_32=read_16bitLE(vgmstream->ch[i].offset,vgmstream->ch[0].streamfile);
vgmstream->ch[i].adpcm_history2_32=read_16bitLE(vgmstream->ch[i].offset+2,vgmstream->ch[0].streamfile);
}
vgmstream->ch[i].offset+=4;
}
}
/* reset channel sub offset */
if (vgmstream->coding_type == coding_EA_MT10 || vgmstream->coding_type == coding_EA_XA) {
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].channel_start_offset=0;
}
}
}
void eacs_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
off_t block_size=vgmstream->current_block_size;
int i;
off_t block_size=vgmstream->current_block_size;
if(read_32bitBE(block_offset,vgmstream->ch[0].streamfile)==0x31534E6C) {
block_offset+=0x0C;
}
if(read_32bitBE(block_offset,vgmstream->ch[0].streamfile)==0x31534E6C) {
block_offset+=0x0C;
}
vgmstream->current_block_offset = block_offset;
if(read_32bitBE(block_offset,vgmstream->ch[0].streamfile)==0x31534E64) { /* 1Snd */
block_offset+=4;
if(vgmstream->ea_platform==0)
block_size=read_32bitLE(vgmstream->current_block_offset+0x04,
vgmstream->ch[0].streamfile);
else
block_size=read_32bitBE(vgmstream->current_block_offset+0x04,
vgmstream->ch[0].streamfile);
block_offset+=4;
}
if(read_32bitBE(block_offset,vgmstream->ch[0].streamfile)==0x31534E64) { /* 1Snd */
block_offset+=4;
if(vgmstream->ea_platform==0)
block_size=read_32bitLE(vgmstream->current_block_offset+0x04,
vgmstream->ch[0].streamfile);
else
block_size=read_32bitBE(vgmstream->current_block_offset+0x04,
vgmstream->ch[0].streamfile);
block_offset+=4;
}
vgmstream->current_block_size=block_size-8;
vgmstream->current_block_size=block_size-8;
if(vgmstream->coding_type==coding_EACS_IMA) {
init_get_high_nibble(vgmstream);
vgmstream->current_block_size=read_32bitLE(block_offset,vgmstream->ch[0].streamfile);
if(vgmstream->coding_type==coding_EACS_IMA) {
init_get_high_nibble(vgmstream);
vgmstream->current_block_size=read_32bitLE(block_offset,vgmstream->ch[0].streamfile);
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset+0x04+i*4,vgmstream->ch[0].streamfile);
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset+0x04+i*4+(4*vgmstream->channels),vgmstream->ch[0].streamfile);
vgmstream->ch[i].offset = block_offset+0x14;
}
} else {
if(vgmstream->coding_type==coding_PSX) {
for (i=0;i<vgmstream->channels;i++)
vgmstream->ch[i].offset = vgmstream->current_block_offset+8+(i*(vgmstream->current_block_size/2));
} else {
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset+0x04+i*4,vgmstream->ch[0].streamfile);
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset+0x04+i*4+(4*vgmstream->channels),vgmstream->ch[0].streamfile);
vgmstream->ch[i].offset = block_offset+0x14;
}
} else {
if(vgmstream->coding_type==coding_PSX) {
for (i=0;i<vgmstream->channels;i++)
vgmstream->ch[i].offset = vgmstream->current_block_offset+8+(i*(vgmstream->current_block_size/2));
} else {
for (i=0;i<vgmstream->channels;i++) {
if(vgmstream->coding_type==coding_PCM16LE_int)
vgmstream->ch[i].offset = block_offset+(i*2);
else
vgmstream->ch[i].offset = block_offset+i;
}
}
vgmstream->current_block_size/=vgmstream->channels;
}
vgmstream->next_block_offset = vgmstream->current_block_offset +
(off_t)block_size;
for (i=0;i<vgmstream->channels;i++) {
if(vgmstream->coding_type==coding_PCM16LE_int)
vgmstream->ch[i].offset = block_offset+(i*2);
else
vgmstream->ch[i].offset = block_offset+i;
}
}
vgmstream->current_block_size/=vgmstream->channels;
}
vgmstream->next_block_offset = vgmstream->current_block_offset +
(off_t)block_size;
}

View File

@ -1,308 +1,561 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "../util.h"
// Platform constants
#define EA_PC 0x00
#define EA_PSX 0x01
#define EA_PS2 0x05
#define EA_GC 0x06
#define EA_XBOX 0x07
#define EA_X360 0x09
/* header version */
#define EA_VERSION_NONE -1
#define EA_VERSION_V0 0x00 // ~early PC (when codec1 was used)
#define EA_VERSION_V1 0x01 // ~PC
#define EA_VERSION_V2 0x02 // ~PS era
#define EA_VERSION_V3 0x03 // ~PS2 era
// Compression Version
#define EAXA_R1 0x01
#define EAXA_R2 0x02
#define EAXA_R3 0x03
/* platform constants (unasigned values seem internal only) */
#define EA_PLATFORM_GENERIC -1 // typically Wii/X360/PS3
#define EA_PLATFORM_PC 0x00
#define EA_PLATFORM_PSX 0x01
#define EA_PLATFORM_N64 0x02
#define EA_PLATFORM_MAC 0x03
//#define EA_PLATFORM_SAT 0x04 // ?
#define EA_PLATFORM_PS2 0x05
#define EA_PLATFORM_GC_WII 0x06 // reused later for Wii
#define EA_PLATFORM_XBOX 0x07
#define EA_PLATFORM_X360 0x09 // also "Xenon"
#define EA_PLATFORM_PSP 0x0A
#define EA_PLATFORM_3DS 0x14
// Compression Type
#define EA_VAG 0x01
#define EA_EAXA 0x0A
#define EA_ADPCM 0x30
#define EA_PCM_BE 0x07
#define EA_PCM_LE 0x08
#define EA_IMA 0x14
/* 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_MT10 0x07 // Need for Speed 2 PC
//#define EA_CODEC1_N64 ?
#define EA_CODEC2_NONE -1
#define EA_CODEC2_MT10 0x04
#define EA_CODEC2_VAG 0x05
#define EA_CODEC2_S16BE 0x07
#define EA_CODEC2_S16LE 0x08
#define EA_CODEC2_S8 0x09
#define EA_CODEC2_EAXA 0x0A
#define EA_CODEC2_LAYER2 0x0F
#define EA_CODEC2_LAYER3 0x10
#define EA_CODEC2_GCADPCM 0x12
#define EA_CODEC2_XBOXADPCM 0x14
#define EA_CODEC2_MT5 0x16
#define EA_CODEC2_EALAYER3 0x17
#define EA_MAX_CHANNELS 6
typedef struct {
int32_t num_samples;
int32_t sample_rate;
uint8_t channels;
uint8_t platform;
int32_t interleave;
uint8_t compression_type;
uint8_t compression_version;
} EA_STRUCT;
uint8_t id;
int32_t num_samples;
int32_t sample_rate;
int32_t channels;
int32_t platform;
int32_t version;
int32_t codec1;
int32_t codec2;
uint32_t readPatch(STREAMFILE* streamFile, off_t* offset) {
uint32_t result=0;
uint8_t byteCount;
int32_t loop_start;
int32_t loop_end;
byteCount = read_8bit(*offset,streamFile);
(*offset)++;
for(;byteCount>0;byteCount--) {
result <<=8;
result+=(uint8_t)read_8bit(*offset,streamFile);
(*offset)++;
}
return result;
}
off_t offsets[EA_MAX_CHANNELS];
off_t coefs[EA_MAX_CHANNELS];
void Parse_Header(STREAMFILE* streamFile,EA_STRUCT* ea, off_t offset, int length) {
int big_endian;
int loop_flag;
int codec_version;
} ea_header;
uint8_t byteRead;
off_t begin_offset=offset;
static int parse_stream_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);
// default value ...
ea->channels=1;
ea->compression_type=0;
ea->compression_version=0x01;
ea->platform=EA_GC;
if(read_32bitBE(offset, streamFile)==0x47535452) { // GSTR
ea->compression_version=0x03;
offset+=8;
ea->platform=6;
} else {
if(read_16bitBE(offset,streamFile)!=0x5054) // PT
offset+=4;
ea->platform=(uint8_t)read_16bitLE(offset+2,streamFile);
offset+=4;
}
do {
byteRead = read_8bit(offset++,streamFile);
switch(byteRead) {
case 0xFF:
case 0xFE:
case 0xFC:
case 0xFD:
break;
#if 0
// was added for My Sims Kingdom, apparently this is not the right
// way to do it (see NFS Most Wanted and Underground 2)
case 0x06: //
if (readPatch(streamFile, &offset) == 0x65)
ea->compression_type = EA_PCM_BE;
break;
#endif
case 0x80: // compression version
ea->compression_version = (uint8_t)readPatch(streamFile, &offset);
break;
case 0x82: // channels count
ea->channels = (uint8_t)readPatch(streamFile, &offset);
break;
case 0x83: // compression type
ea->compression_type = (uint8_t)readPatch(streamFile, &offset);
if(ea->compression_type==0x07) ea->compression_type=0x30;
break;
case 0x84: // sample frequency
ea->sample_rate = readPatch(streamFile,&offset);
break;
case 0x85: // samples count
ea->num_samples = readPatch(streamFile, &offset);
break;
case 0x8A:
offset+=4;
if(ea->compression_type==0) ea->compression_type=EA_PCM_LE;
break;
case 0x86:
case 0x87:
case 0x8C:
case 0x92:
case 0x9C:
case 0x9D: // unknown patch
readPatch(streamFile, &offset);
break;
case 0x88: // interleave
ea->interleave = readPatch(streamFile, &offset);
break;
case 0xA0: // compression type
ea->compression_type = (uint8_t)readPatch(streamFile, &offset);
break;
}
} while(offset-begin_offset<length);
if(ea->platform==EA_PSX)
ea->compression_type=EA_VAG;
if(ea->compression_type==0)
ea->compression_type=EA_EAXA;
}
VGMSTREAM * init_vgmstream_ea(STREAMFILE *streamFile) {
/* EA SCHl - 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;
EA_STRUCT ea;
char filename[PATH_LIMIT];
off_t start_offset;
size_t header_size;
ea_header ea;
int loop_flag=0;
int channel_count;
int header_length;
off_t start_offset;
int i;
memset(&ea,0,sizeof(EA_STRUCT));
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("strm",filename_extension(filename)) &&
strcasecmp("xa",filename_extension(filename)) &&
strcasecmp("sng",filename_extension(filename)) &&
strcasecmp("asf",filename_extension(filename)) &&
strcasecmp("str",filename_extension(filename)) &&
strcasecmp("xsf",filename_extension(filename)) &&
strcasecmp("eam",filename_extension(filename))) goto fail;
/* check Header */
if (read_32bitBE(0x00,streamFile) != 0x5343486C) // SCHl
/* check extension; exts don't seem enforced by EA's tools, but usually:
* STR/ASF/MUS ~early, EAM ~mid, SNG/AUD ~late, rest uncommon/one game (ex. STRM: MySims Kingdom Wii) */
if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,strm,xa,xsf,exa,stm"))
goto fail;
header_length = read_32bitLE(0x04,streamFile);
start_offset=8;
/* check header */
/* EA's stream files are made of blocks called "chunks" (SCxx, presumably Sound Chunk xx)
* typically: SCHl=header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=stream end.
* The number/size of blocks is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */
goto fail;
if(header_length>0x100) goto fail;
header_size = read_32bitLE(0x04,streamFile);
if (header_size > 0xF0000000) /* size is always LE, except in early MAC apparently */
header_size = read_32bitBE(0x04,streamFile);
Parse_Header(streamFile,&ea,start_offset,header_length-8);
memset(&ea,0,sizeof(ea_header));
if (!parse_stream_header(streamFile,&ea, 0x08, header_size-4-4))
goto fail;
/* unknown loop value for the moment */
loop_flag = 0;
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" */
goto fail;
channel_count=ea.channels;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->ea_platform=ea.platform;
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->ea_compression_type=ea.compression_type;
vgmstream->ea_compression_version=ea.compression_version;
vgmstream->codec_endian = ea.big_endian;
vgmstream->codec_version = ea.codec_version;
// Set defaut sample rate if not define in the header
if(ea.sample_rate!=0) {
vgmstream->sample_rate = ea.sample_rate;
} else {
if(read_32bitBE(0x08,streamFile)==0x47535452) { // GSTR
vgmstream->sample_rate=44100;
} else {
switch(vgmstream->ea_platform) {
case EA_XBOX:
vgmstream->sample_rate=24000;
break;
case EA_X360:
vgmstream->sample_rate=44100;
break;
default:
vgmstream->sample_rate=22050;
}
}
}
vgmstream->meta_type = meta_EA_SCHL;
vgmstream->layout_type = layout_ea_blocked;
// Set default compression scheme if not define in the header
switch(vgmstream->ea_platform) {
case EA_X360:
vgmstream->ea_compression_version=0x03;
break;
}
/* 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) {
vgmstream->num_samples=ea.num_samples;
case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */
vgmstream->coding_type = coding_EA_XA;
break;
switch(vgmstream->ea_compression_type) {
case EA_EAXA:
if(vgmstream->ea_compression_version==0x03)
vgmstream->meta_type=meta_EAXA_R3;
else {
// seems there's no EAXA R2 on PC
if(ea.platform==EA_PC) {
vgmstream->ea_compression_version=0x03;
vgmstream->meta_type=meta_EAXA_R3;
} else
vgmstream->meta_type=meta_EAXA_R2;
}
case EA_CODEC2_MT10: /* MicroTalk (10:1), aka EA ADPCM (stereo or interleaved) */
vgmstream->coding_type = coding_EA_MT10;
break;
vgmstream->coding_type=coding_EA_XA;
vgmstream->layout_type=layout_ea_blocked;
if((vgmstream->ea_platform==EA_GC) || (vgmstream->ea_platform==EA_X360))
vgmstream->ea_big_endian=1;
break;
case EA_VAG:
vgmstream->meta_type=meta_EAXA_PSX;
vgmstream->coding_type=coding_PSX;
vgmstream->layout_type=layout_ea_blocked;
break;
case EA_PCM_LE:
vgmstream->meta_type=meta_EA_PCM;
vgmstream->coding_type=coding_PCM16LE_int;
vgmstream->layout_type=layout_ea_blocked;
break;
case EA_PCM_BE:
vgmstream->meta_type=meta_EA_PCM;
vgmstream->coding_type=coding_PCM16BE;
vgmstream->layout_type=layout_ea_blocked;
break;
case EA_ADPCM:
vgmstream->meta_type=meta_EA_ADPCM;
vgmstream->coding_type=coding_EA_ADPCM;
vgmstream->layout_type=layout_ea_blocked;
break;
case EA_IMA:
vgmstream->meta_type=meta_EA_IMA;
vgmstream->coding_type=coding_XBOX;
vgmstream->layout_type=layout_ea_blocked;
break;
}
case EA_CODEC2_S8: /* PCM8 */
vgmstream->coding_type = coding_PCM8;
break;
case EA_CODEC2_S16BE: /* PCM16BE */
vgmstream->coding_type = coding_PCM16BE;
break;
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000);
case EA_CODEC2_S16LE: /* PCM16LE */
vgmstream->coding_type = coding_PCM16LE;
break;
if (!vgmstream->ch[i].streamfile) goto fail;
}
case EA_CODEC2_VAG: /* PS-ADPCM */
vgmstream->coding_type = coding_PSX;
break;
case EA_CODEC2_XBOXADPCM: /* XBOX IMA (interleaved mono) */
vgmstream->coding_type = coding_XBOX; /* stereo decoder actually, but has a special case for EA */
break;
case EA_CODEC2_GCADPCM: /* DSP */
vgmstream->coding_type = coding_NGC_DSP;
/* 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;
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);
}
}
}
break;
case EA_CODEC2_MT5: /* MicroTalk (5:1) */
case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */
case EA_CODEC2_LAYER3: /* MPEG Layer III, aka MP3 */
case EA_CODEC2_EALAYER3: /* MP3 variant */
default:
VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea.codec2, ea.platform);
goto fail;
}
// Special function for .EAM files ...
if(!strcasecmp("eam",filename_extension(filename))) {
/* 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;
}
size_t file_length=get_streamfile_size(streamFile);
size_t block_length;
/* open files; channel offsets are updated below */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
vgmstream->next_block_offset=start_offset+header_length;
vgmstream->num_samples=0;
ea_block_update(start_offset,vgmstream);
// to initialize the block length
ea_block_update(start_offset+header_length,vgmstream);
block_length=vgmstream->next_block_offset-start_offset+header_length;
do {
ea_block_update(vgmstream->next_block_offset,vgmstream);
if(vgmstream->coding_type==coding_PSX)
vgmstream->num_samples+=(int32_t)vgmstream->current_block_size/16*28;
else if (vgmstream->coding_type==coding_EA_ADPCM)
vgmstream->num_samples+=(int32_t)vgmstream->current_block_size;
else if (vgmstream->coding_type==coding_PCM16LE_int)
vgmstream->num_samples+=(int32_t)vgmstream->current_block_size/vgmstream->channels;
else
vgmstream->num_samples+=(int32_t)vgmstream->current_block_size*28;
} while(vgmstream->next_block_offset<(off_t)(file_length-block_length));
}
return vgmstream;
ea_block_update(start_offset+header_length,vgmstream);
init_get_high_nibble(vgmstream);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}
static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) {
uint32_t result = 0;
uint8_t byte_count = read_8bit(*offset, streamFile);
(*offset)++;
if (byte_count == 0xFF) { /* signals 32b size (ex. custom user data) */
(*offset) += 4 + read_32bitBE(*offset, streamFile);
return 0;
}
if (byte_count > 4) { /* uncommon (ex. coef patches) */
(*offset) += byte_count;
return 0;
}
for ( ; byte_count > 0; byte_count--) { /* count of 0 is also possible, means value 0 */
result <<= 8;
result += (uint8_t)read_8bit(*offset, streamFile);
(*offset)++;
}
return result;
}
/* 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) {
off_t offset = begin_offset;
uint32_t platform_id;
int is_header_end = 0;
/* null defaults as 0 can be valid */
ea->version = EA_VERSION_NONE;
ea->codec1 = EA_CODEC1_NONE;
ea->codec2 = EA_CODEC2_NONE;
/* get platform info */
platform_id = read_32bitBE(offset, streamFile);
if (platform_id != 0x47535452 && (platform_id & 0xFFFF0000) != 0x50540000) {
offset += 4; /* skip unknown field (related to blocks/size?) in "nbapsstream" (NBA2000 PS, FIFA2001 PS) */
platform_id = read_32bitBE(offset, streamFile);
}
if (platform_id == 0x47535452) { /* "GSTR" = Generic STReam */
ea->platform = EA_PLATFORM_GENERIC;
offset += 4 + 4; /* GSTRs have an extra field (config?): ex. 0x01000000, 0x010000D8 BE */
}
else if ((platform_id & 0xFFFF0000) == 0x50540000) { /* "PT" = PlaTform */
ea->platform = (uint8_t)read_16bitLE(offset + 2,streamFile);
offset += 4;
}
else {
goto fail;
}
/* parse mini-chunks/tags (variable, ommited if default exists) */
while (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 */
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 0x0B: /* unknown (always 0x02) */
case 0x13: /* effect bus (0..127) */
case 0x14: /* emdedded user data (free size/value) */
read_patch(streamFile, &offset);
break;
case 0xFC: /* padding for alignment between patches */
case 0xFE: /* padding? (actually exists?) */
case 0xFD: /* info section start marker */
break;
case 0xA0: /* codec2 defines */
ea->codec2 = read_patch(streamFile, &offset);
break;
case 0x80: /* version, affecting some codecs */
ea->version = 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;
case 0x85: /* sample count */
ea->num_samples = read_patch(streamFile, &offset);
break;
case 0x86: /* loop start sample */
ea->loop_start = read_patch(streamFile, &offset);
break;
case 0x87: /* loop end sample */
ea->loop_end = read_patch(streamFile, &offset);
break;
/* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */
case 0x88: /* absolute offset of ch1 */
ea->offsets[0] = read_patch(streamFile, &offset);
break;
case 0x89: /* absolute offset of ch2 */
ea->offsets[1] = read_patch(streamFile, &offset);
break;
case 0x94: /* absolute offset of ch3 */
ea->offsets[2] = read_patch(streamFile, &offset);
break;
case 0x95: /* absolute offset of ch4 */
ea->offsets[3] = read_patch(streamFile, &offset);
break;
case 0xA2: /* absolute offset of ch5 */
ea->offsets[4] = read_patch(streamFile, &offset);
break;
case 0xA3: /* absolute offset of ch6 */
ea->offsets[5] = read_patch(streamFile, &offset);
break;
case 0x8F: /* DSP/N64BLK coefs ch1 */
ea->coefs[0] = offset+1;
read_patch(streamFile, &offset);
break;
case 0x90: /* DSP/N64BLK coefs ch2 */
ea->coefs[1] = offset+1;
read_patch(streamFile, &offset);
break;
case 0x91: /* DSP coefs ch3 */
ea->coefs[2] = offset+1;
read_patch(streamFile, &offset);
break;
case 0xAB: /* DSP coefs ch4 */
ea->coefs[3] = offset+1;
read_patch(streamFile, &offset);
break;
case 0xAC: /* DSP coefs ch5 */
ea->coefs[4] = offset+1;
read_patch(streamFile, &offset);
break;
case 0xAD: /* DSP coefs ch6 */
ea->coefs[5] = offset+1;
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 0x92: /* bytes per sample? */
case 0x98: /* embedded time stretch 1 (long data for who-knows-what) */
case 0x99: /* embedded time stretch 2 */
case 0x9C: /* azimuth ch1 */
case 0x9D: /* azimuth ch2 */
case 0x9E: /* azimuth ch3 */
case 0x9F: /* azimuth ch4 */
case 0xA6: /* azimuth ch5 */
case 0xA7: /* azimuth ch6 */
case 0xA1: /* unknown and very rare, always 0x02 (FIFA 2001 PS2) */
read_patch(streamFile, &offset);
break;
case 0xFF: /* header end (then 0-padded) */
is_header_end = 1;
break;
default:
VGM_LOG("EA: unknown patch 0x%02x at 0x%04lx\n", patch_type, (offset-1));
break;
}
}
if (ea->id && ea->id != 0x65) /* very rarely not specified (FIFA 14) */
goto fail;
if (ea->channels > EA_MAX_CHANNELS)
goto fail;
/* set defaults per platform, as the header ommits them when possible */
ea->loop_flag = (ea->loop_end);
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 */
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_MAC: ea->version = EA_VERSION_V0; break;
case EA_PLATFORM_PS2: ea->version = EA_VERSION_V1; break;
case EA_PLATFORM_GC_WII: ea->version = EA_VERSION_V2; break;
case EA_PLATFORM_XBOX: ea->version = EA_VERSION_V2; break;
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;
default:
VGM_LOG("EA: unknown default version 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_MT10: ea->codec2 = EA_CODEC2_MT10; break;
default:
VGM_LOG("EA: unknown codec1 0x%02x\n", ea->codec1);
goto fail;
}
}
/* defaults don't seem to change with version or over time, fortunately */
if (ea->codec2 == EA_CODEC2_NONE) {
switch(ea->platform) {
case EA_PLATFORM_GENERIC: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_PC: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_PSX: ea->codec2 = EA_CODEC2_VAG; break;
case EA_PLATFORM_MAC: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_PS2: ea->codec2 = EA_CODEC2_VAG; break;
case EA_PLATFORM_GC_WII: ea->codec2 = EA_CODEC2_S16BE; break;
case EA_PLATFORM_XBOX: ea->codec2 = EA_CODEC2_S16LE; break;
case EA_PLATFORM_X360: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_PSP: ea->codec2 = EA_CODEC2_EAXA; break;
case EA_PLATFORM_3DS: ea->codec2 = EA_CODEC2_GCADPCM; break;
default:
VGM_LOG("EA: unknown default codec2 for platform 0x%02x\n", ea->platform);
goto fail;
}
}
/* somehow doesn't follow machine's sample rate or anything sensical */
if (!ea->sample_rate) {
switch(ea->platform) {
case EA_PLATFORM_GENERIC: ea->sample_rate = 48000; break;
case EA_PLATFORM_PC: ea->sample_rate = 22050; break;
case EA_PLATFORM_PSX: ea->sample_rate = 22050; break;
case EA_PLATFORM_N64: ea->sample_rate = 22050; break;
case EA_PLATFORM_MAC: ea->sample_rate = 22050; break;
case EA_PLATFORM_PS2: ea->sample_rate = 22050; break;
case EA_PLATFORM_GC_WII: ea->sample_rate = 24000; break;
case EA_PLATFORM_XBOX: ea->sample_rate = 24000; break;
case EA_PLATFORM_X360: ea->sample_rate = 44100; break;
case EA_PLATFORM_PSP: ea->sample_rate = 22050; break;
//case EA_PLATFORM_3DS: ea->sample_rate = 44100; break;//todo (not 22050/16000)
default:
VGM_LOG("EA: unknown default sample rate for platform 0x%02x\n", ea->platform);
goto fail;
}
}
/* affects blocks/codecs */
if (ea->platform == EA_PLATFORM_N64
|| ea->platform == EA_PLATFORM_MAC
|| ea->platform == EA_PLATFORM_GC_WII
|| ea->platform == EA_PLATFORM_X360
|| ea->platform == EA_PLATFORM_GENERIC) {
ea->big_endian = 1;
}
/* config MT/EAXA variations */
if (ea->codec2 == EA_CODEC2_MT10) {
if (ea->version > EA_VERSION_V0)
ea->codec_version = 1; /* 0=stereo (early), 1:interleaved */
}
else if (ea->codec2 == EA_CODEC2_EAXA) {
/* console EAXA V2 uses hist, as does PC/MAC V1 */
if (ea->version > EA_VERSION_V1 && !(ea->version == EA_VERSION_V2
&& (ea->platform == EA_PLATFORM_PS2|| ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX)))
ea->codec_version = 1; /* 0=has ADPCM history per block (early), 1:doesn't */
}
return offset;
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 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) {
int i, num_samples = 0;
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;
while (block_offset < file_size) {
uint32_t id, block_size;
id = read_32bitBE(block_offset+0x00,streamFile);
block_size = read_32bitLE(block_offset+0x04,streamFile);
VGM_ASSERT(block_size > 0xF0000000, "EA: BE block size in MAC\n");
if (block_size > 0xF0000000) /* size is always LE, except in early MAC apparently */
block_size = read_32bitBE(block_offset+0x04,streamFile);
if (id == 0x5343446C) { /* "SCDl" data block found */
/* use num_samples from header if possible */
switch (ea->codec2) {
case EA_CODEC2_VAG: /* PS-ADPCM */
num_samples += ps_bytes_to_samples(block_size-0x10, ea->channels);
break;
default:
num_samples += read_32bit(block_offset+0x08,streamFile);
break;
}
}
block_offset += block_size; /* size includes header */
/* EA sometimes concats many small files, so after SCEl there may be a new SCHl.
* We'll find it and pretend they are a single stream. */
if (id == 0x5343456C && block_offset + 0x80 > file_size)
break;
if (id == 0x5343456C) { /* "SCEl" end block found */
/* Usually there is padding between SCEl and SCHl (aligned to 0x80) */
block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* also 32b-aligned */
for (i = 0; i < 0x80 / 4; i++) {
id = read_32bitBE(block_offset,streamFile);
if (id == 0x5343486C) /* "SCHl" new header block found */
break; /* next loop will parse and skip it */
block_offset += 0x04;
}
}
if (block_offset > file_size)
break;
if (id == 0 || id == 0xFFFFFFFF)
return num_samples; /* probably hit padding or EOF */
VGM_ASSERT(id != 0x5343486C && id != 0x5343436C && id != 0x5343446C && id != 0x53434C6C && id != 0x5343456C,
"EA: unknown block id 0x%x at 0x%lx\n", id, block_offset);
}
return num_samples;
}

View File

@ -80,7 +80,7 @@ VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile);

View File

@ -63,7 +63,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_xbox_wavm,
init_vgmstream_xbox_xwav,
init_vgmstream_ngc_str,
init_vgmstream_ea,
init_vgmstream_ea_schl,
init_vgmstream_caf,
init_vgmstream_ps2_vpk,
init_vgmstream_genh,
@ -1047,7 +1047,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_EA_XA:
return 28;
case coding_MAXIS_ADPCM:
case coding_EA_ADPCM:
case coding_EA_MT10:
return 14*vgmstream->channels;
case coding_WS:
/* only works if output sample size is 8 bit, which always is for WS ADPCM */
@ -1191,7 +1191,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 36;
case coding_MAXIS_ADPCM:
return 15*vgmstream->channels;
case coding_EA_ADPCM:
case coding_EA_MT10:
return 30;
case coding_EA_XA:
return 1; // the frame is variant in size
@ -1490,9 +1490,9 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_EA_ADPCM:
case coding_EA_MT10:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_adpcm(vgmstream,buffer+samples_written*vgmstream->channels+chan,
decode_ea_mt10(vgmstream,buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,chan);
}

View File

@ -100,15 +100,14 @@ typedef enum {
coding_XA, /* CD-ROM XA */
coding_PSX, /* Sony PS ADPCM (VAG) */
coding_PSX_badflags, /* Sony PS ADPCM with garbage in the flag byte */
coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */
coding_PSX_bmdx, /* Sony PS ADPCM with BMDX encryption */
coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */
coding_HEVAG, /* Sony PSVita ADPCM */
coding_EA_XA, /* Electronic Arts XA ADPCM */
coding_EA_ADPCM, /* Electronic Arts R1 ADPCM */
coding_EA_MT10, /* Electronic Arts MicroTalk (10:1) ADPCM ('EA ADPCM')*/
coding_EA_XA, /* Electronic Arts EA-XA ADPCM */
coding_MAXIS_ADPCM, /* Maxis ADPCM */
coding_NDS_PROCYON, /* Procyon Studio ADPCM */
coding_XBOX, /* XBOX IMA ADPCM */
coding_XBOX_int, /* XBOX IMA ADPCM (interleaved) */
@ -117,7 +116,7 @@ typedef enum {
coding_DVI_IMA, /* DVI IMA ADPCM (high nibble first), aka ADP4 */
coding_DVI_IMA_int, /* DVI IMA ADPCM (Interleaved) */
coding_NDS_IMA, /* IMA ADPCM w/ NDS layout */
coding_EACS_IMA,
coding_EACS_IMA, /* Electronic Arts IMA ADPCM */
coding_MS_IMA, /* Microsoft IMA ADPCM */
coding_RAD_IMA, /* Radical IMA ADPCM */
coding_RAD_IMA_mono, /* Radical IMA ADPCM, mono (for interleave) */
@ -132,6 +131,7 @@ typedef enum {
coding_MSADPCM, /* Microsoft ADPCM */
coding_WS, /* Westwood Studios VBR ADPCM */
coding_AICA, /* Yamaha AICA ADPCM */
coding_NDS_PROCYON, /* Procyon Studio ADPCM */
coding_L5_555, /* Level-5 0x555 ADPCM */
coding_SASSC, /* Activision EXAKT SASSC DPCM */
coding_LSF, /* lsf ADPCM (Fastlane Street Racing iPhone)*/
@ -165,9 +165,9 @@ typedef enum {
#ifdef VGM_USE_MPEG
coding_fake_MPEG2_L2, /* MPEG-2 Layer 2 (AHX), with lying headers */
/* I don't even know offhand if all these combinations exist... */
coding_MPEG1_L1,
coding_MPEG1_L2,
/* MPEG audio variations (depending on sample rate and other config) */
coding_MPEG1_L1, /* MP1 */
coding_MPEG1_L2, /* MP2 */
coding_MPEG1_L3, /* good ol' MPEG-1 Layer 3 (MP3) */
coding_MPEG2_L1,
coding_MPEG2_L2,
@ -456,15 +456,10 @@ typedef enum {
meta_XBOX_XMU, /* XBOX XMU */
meta_XBOX_XVAS, /* XBOX VAS */
meta_EAXA_R2, /* EA XA Release 2 */
meta_EAXA_R3, /* EA XA Release 3 */
meta_EAXA_PSX, /* EA with PSX ADPCM */
meta_EACS_PC, /* EACS PC */
meta_EACS_PSX, /* EACS PSX */
meta_EACS_SAT, /* EACS SATURN */
meta_EA_ADPCM, /* EA header using XA ADPCM */
meta_EA_IMA, /* EA header using IMA */
meta_EA_PCM, /* EA header using PCM */
meta_EA_SCHL, /* Electronic Arts SCHl */
meta_EACS_PC, /* Electronic Arts EACS PC */
meta_EACS_PSX, /* Electronic Arts EACS PSX */
meta_EACS_SAT, /* Electronic Arts EACS SATURN */
meta_RAW, /* RAW PCM file */
@ -730,7 +725,8 @@ typedef struct {
size_t interleave_smallblock_size; /* smaller interleave for last block */
/* headered blocks */
off_t current_block_offset; /* start of this block (offset of block header) */
size_t current_block_size; /* size of the block we're in now (usable data) */
size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */
size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */
size_t full_block_size; /* size including padding and other unusable data */
off_t next_block_offset; /* offset of header of the next block */
int block_count; /* count of "semi" block in total block */
@ -751,6 +747,7 @@ typedef struct {
/* decoder specific */
int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */
int codec_version; /* flag for codecs with minor variations */
uint8_t xa_channel; /* XA ADPCM: selected channel */
int32_t xa_sector_length; /* XA ADPCM: XA block */
@ -759,8 +756,6 @@ typedef struct {
int8_t get_high_nibble; /* ADPCM: which nibble (XA, IMA, EA) */
uint8_t ea_big_endian; /* EA ADPCM stuff */
uint8_t ea_compression_type;
uint8_t ea_compression_version;
uint8_t ea_platform;
int32_t ws_output_size; /* WS ADPCM: output bytes for this block */