Electronic Arts EA/XA R1,R2,R3 & EA PSX support added

git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@203 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
fastelbja 2008-06-02 17:58:08 +00:00
parent a25e86e359
commit 5de0c96986
13 changed files with 438 additions and 5 deletions

View File

@ -27,4 +27,6 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
void decode_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_eaxa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
#endif

68
src/coding/eaxa_decoder.c Normal file
View File

@ -0,0 +1,68 @@
#include "coding.h"
#include "../util.h"
long 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 int samples;
void decode_eaxa(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;
long 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);
if(frame_info==0xEE) {
channel_offset++;
stream->adpcm_history1_32 = read_16bitBE(stream->offset+channel_offset+(2*channel),stream->streamfile);
stream->adpcm_history2_32 = read_16bitBE(stream->offset+channel_offset+2+(2*channel),stream->streamfile);
channel_offset+=(2*channelspacing);
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;
}
// Only increment offset on complete frame
if(channel_offset-stream->channel_start_offset==(2*28)+5)
stream->channel_start_offset+=(2*28)+5;
} 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++;
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;
}
}

View File

@ -1,3 +1,4 @@
#include <math.h>
#include "coding.h"
#include "../util.h"

View File

@ -54,6 +54,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
case layout_xbox_blocked:
xbox_block_update(vgmstream->next_block_offset,vgmstream);
break;
case layout_ea_blocked:
ea_block_update(vgmstream->next_block_offset,vgmstream);
break;
default:
break;
}

60
src/layout/ea_block.c Normal file
View File

@ -0,0 +1,60 @@
#include "layout.h"
#include "../vgmstream.h"
/* set up for the block at the given offset */
void ea_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
// Search for next SCDL or SCEl block ...
do {
block_offset+=4;
if(block_offset>(off_t)get_streamfile_size(vgmstream->ch[0].streamfile))
return;
} while (read_32bitBE(block_offset,vgmstream->ch[0].streamfile)!=0x5343446C);
// reset channel offset
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].channel_start_offset=0;
}
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset+read_32bitLE(block_offset+4,vgmstream->ch[0].streamfile)-4;
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;
}
} else {
if(vgmstream->coding_type==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)/2;
vgmstream->ch[1].offset+=vgmstream->ch[0].offset;
vgmstream->current_block_size=read_32bitLE(block_offset+0x0C,vgmstream->ch[0].streamfile)*0x10;
} else {
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)) {
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;
}
}
}

View File

@ -14,6 +14,8 @@ void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void xbox_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void ea_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -6,7 +6,7 @@ void xbox_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
int i;
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = 36*vgmstream->channels;
vgmstream->next_block_offset = vgmstream->current_block_offset+vgmstream->current_block_size;
vgmstream->next_block_offset = vgmstream->current_block_offset+(off_t)vgmstream->current_block_size;
for (i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].offset = vgmstream->current_block_offset +

View File

@ -222,6 +222,10 @@
RelativePath=".\meta\Cstr.c"
>
</File>
<File
RelativePath=".\meta\ea_header.c"
>
</File>
<File
RelativePath=".\meta\gcsw.c"
>
@ -346,6 +350,10 @@
RelativePath=".\coding\adx_decoder.c"
>
</File>
<File
RelativePath=".\coding\eaxa_decoder.c"
>
</File>
<File
RelativePath=".\coding\g721_decoder.c"
>
@ -402,6 +410,10 @@
RelativePath=".\layout\blocked.c"
>
</File>
<File
RelativePath=".\layout\ea_block.c"
>
</File>
<File
RelativePath=".\layout\halpst_blocked.c"
>

241
src/meta/ea_header.c Normal file
View File

@ -0,0 +1,241 @@
#include "meta.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
// Compression Version
#define EAXA_R1 0x01
#define EAXA_R2 0x02
#define EAXA_R3 0x03
// Compression Type
#define EA_VAG 0x01
#define EA_EAXA 0x0A
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;
extern ea_block_update(off_t block_offset, VGMSTREAM * vgmstream);
uint32_t readPatch(STREAMFILE* streamFile, off_t* offset) {
uint32_t result=0;
uint8_t byteCount;
byteCount = read_8bit(*offset,streamFile);
(*offset)++;
for(;byteCount>0;byteCount--) {
result <<=8;
result+=(uint8_t)read_8bit(*offset,streamFile);
(*offset)++;
}
return result;
}
void Parse_Header(STREAMFILE* streamFile,EA_STRUCT* ea, off_t offset, int length) {
uint8_t byteRead;
off_t begin_offset=offset;
// default value ...
ea->channels=1;
ea->compression_type=EA_EAXA;
ea->compression_version=0x01;
ea->platform=EA_GC;
if(read_32bitBE(offset, streamFile)==0x47535452) { // GSTR
ea->compression_version=0x03;
offset+=8;
length-=4;
} 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;
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);
break;
case 0x84: // sample frequency
ea->sample_rate = readPatch(streamFile,&offset);
break;
case 0x85: // samples count
ea->num_samples = readPatch(streamFile, &offset);
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;
}
VGMSTREAM * init_vgmstream_ea(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
EA_STRUCT ea;
char filename[260];
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("sng",filename_extension(filename)) &&
strcasecmp("asf",filename_extension(filename)) &&
strcasecmp("str",filename_extension(filename)) &&
strcasecmp("eam",filename_extension(filename))) goto fail;
/* check Header */
if (read_32bitBE(0x00,streamFile) != 0x5343486C) // SCHl
goto fail;
header_length = read_32bitLE(0x04,streamFile);
start_offset=8;
if(header_length>0x100) goto fail;
Parse_Header(streamFile,&ea,start_offset,header_length-8);
/* unknown loop value for the moment */
loop_flag = 0;
channel_count=ea.channels;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->ea_platform=ea.platform;
vgmstream->ea_compression_type=ea.compression_type;
vgmstream->ea_compression_version=ea.compression_version;
// Set defaut sample rate if not define in the header
if(ea.sample_rate!=0) {
vgmstream->sample_rate = ea.sample_rate;
} else {
switch(vgmstream->ea_platform) {
case EA_XBOX:
vgmstream->sample_rate=24000;
break;
case EA_X360:
vgmstream->sample_rate=44100;
vgmstream->ea_compression_version=0x03;
break;
default:
vgmstream->sample_rate=22050;
}
}
vgmstream->num_samples=ea.num_samples;
switch(vgmstream->ea_compression_type) {
case EA_EAXA:
if(vgmstream->ea_compression_version==0x03)
vgmstream->meta_type=meta_EAXA_R3;
else
vgmstream->meta_type=meta_EAXA_R2;
vgmstream->coding_type=coding_EAXA;
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;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
}
}
// Special function for .EAM files ...
if(!strcasecmp("eam",filename_extension(filename))) {
size_t file_length=get_streamfile_size(streamFile);
size_t block_length;
vgmstream->next_block_offset=start_offset+header_length;
vgmstream->num_samples=0;
// 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);
vgmstream->num_samples+=vgmstream->current_block_size*28;
} while(vgmstream->next_block_offset<(file_length-block_length));
}
ea_block_update(start_offset+header_length,vgmstream);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -73,4 +73,6 @@ VGMSTREAM * init_vgmstream_xbox_xwav(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea(STREAMFILE *streamFile);
#endif

View File

@ -15,7 +15,7 @@
* List of functions that will recognize files. These should correspond pretty
* directly to the metadata types
*/
#define INIT_VGMSTREAM_FCNS 35
#define INIT_VGMSTREAM_FCNS 36
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(STREAMFILE *streamFile) = {
init_vgmstream_adx, /* 0 */
init_vgmstream_brstm, /* 1 */
@ -51,7 +51,8 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(STREAMFILE *streamFile)
init_vgmstream_ps2_pnb, /* 31 */
init_vgmstream_xbox_wavm, /* 32 */
init_vgmstream_xbox_xwav, /* 33 */
init_vgmstream_ngc_str /* 34 */
init_vgmstream_ngc_str, /* 34 */
init_vgmstream_ea /* 35 */
};
/* internal version with all parameters */
@ -227,6 +228,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_halpst_blocked:
case layout_xa_blocked:
case layout_xbox_blocked:
case layout_ea_blocked:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
}
@ -255,6 +257,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 28;
case coding_XBOX:
return 64;
case coding_EAXA:
return 28;
default:
return 0;
}
@ -294,6 +298,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 14*vgmstream->channels;
case coding_XBOX:
return 64+(4*vgmstream->channels);
case coding_EAXA:
return 1; // the frame is variant in size
default:
return 0;
}
@ -397,6 +403,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do);
}
break;
case coding_EAXA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_eaxa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,chan);
}
break;
}
}
@ -560,6 +573,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
break;
case coding_XBOX:
snprintf(temp,TEMPSIZE,"XBOX 4-bit IMA ADPCM");
break;
case coding_EAXA:
snprintf(temp,TEMPSIZE,"Electronic Arts XA Based 4-bit ADPCM");
break;
default:
snprintf(temp,TEMPSIZE,"CANNOT DECODE");
@ -593,6 +609,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
break;
case layout_xbox_blocked:
snprintf(temp,TEMPSIZE,"XBOX blocked");
break;
case layout_ea_blocked:
snprintf(temp,TEMPSIZE,"Electronic Arts Audio Blocks");
break;
default:
snprintf(temp,TEMPSIZE,"INCONCEIVABLE");
@ -752,6 +771,15 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
break;
case meta_DSP_STR:
snprintf(temp,TEMPSIZE,"assumed Conan Gamecube STR File by .str extension");
break;
case meta_EAXA_R2:
snprintf(temp,TEMPSIZE,"Electronic Arts XA R2");
break;
case meta_EAXA_R3:
snprintf(temp,TEMPSIZE,"Electronic Arts XA R3");
break;
case meta_EAXA_PSX:
snprintf(temp,TEMPSIZE,"Electronic Arts With PSX ADPCM");
break;
default:
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");

View File

@ -25,6 +25,7 @@ typedef enum {
coding_PSX, /* PSX & PS2 ADPCM */
coding_XA, /* PSX CD-XA */
coding_XBOX, /* XBOX IMA */
coding_EAXA, /* EA/XA ADPCM */
} coding_t;
/* The layout type specifies how the sound data is laid out in the file */
@ -42,6 +43,7 @@ typedef enum {
layout_halpst_blocked, /* blocks with HALPST-format header */
layout_xa_blocked,
layout_xbox_blocked,
layout_ea_blocked,
#if 0
layout_strm_blocked, /* */
#endif
@ -106,6 +108,10 @@ typedef enum {
meta_XBOX_WAVM, /* XBOX WAVM File */
meta_XBOX_RIFF, /* XBOX RIFF/WAVE File */
meta_EAXA_R2, /* EA XA Release 2 */
meta_EAXA_R3, /* EA XA Release 3 */
meta_EAXA_PSX, /* EA with PSX ADPCM */
meta_RAW, /* RAW PCM file */
} meta_t;
@ -183,6 +189,11 @@ typedef struct {
uint8_t xa_channel; /* Selected XA Channel */
int32_t xa_sector_length; /* XA block */
uint8_t ea_big_endian; /* Big Endian ? */
uint8_t ea_compression_type;
uint8_t ea_compression_version;
uint8_t ea_platform;
void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream */
} VGMSTREAM;

View File

@ -71,7 +71,7 @@ int decode_pos_samples = 0;
int stream_length_samples = 0;
int fade_samples = 0;
#define EXTENSION_LIST_SIZE 1024
#define EXTENSION_LIST_SIZE 2048
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
char * extension_list[] = {
"adx\0ADX Audio File (*.ADX)\0",
@ -108,7 +108,10 @@ char * extension_list[] = {
"pnb\0PNB Audio File (*.PNB)\0",
"wavm\0WAVM Audio File (*.WAVM)\0",
"xwav\0XWAV Audio File (*.XWAV)\0",
"wp2\0WP2 Audio File (*.WP2)\0"
"wp2\0WP2 Audio File (*.WP2)\0",
"sng\0SNG Audio File (*.SNG)\0",
"asf\0ASF Audio File (*.ASF)\0",
"eam\0EAM Audio File (*.EAM)\0"
};
void about(HWND hwndParent) {