#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 // Compression Version #define EAXA_R1 0x01 #define EAXA_R2 0x02 #define EAXA_R3 0x03 // 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 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; 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=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_offsetplatform==EA_PSX) ea->compression_type=EA_VAG; if(ea->compression_type==0) ea->compression_type=EA_EAXA; } VGMSTREAM * init_vgmstream_ea(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; EA_STRUCT ea; char filename[PATH_LIMIT]; 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 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 { 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; } } } // Set default compression scheme if not define in the header switch(vgmstream->ea_platform) { case EA_X360: vgmstream->ea_compression_version=0x03; break; } 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 { // 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; } 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; 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; } /* open the file for reading by each channel */ { for (i=0;ich[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); 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)); } 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); return NULL; }