mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
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:
parent
aa93941e64
commit
bf34339b41
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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"},
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user