From aa93941e64a730a18e01fb2e565d7e83dde336b5 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 1 Jul 2017 22:44:00 +0200 Subject: [PATCH 1/4] fix some .MED with JUNK chunk; minor cleanup --- src/meta/riff.c | 55 +++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/src/meta/riff.c b/src/meta/riff.c index 16d606fe..e0ac857c 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -98,7 +98,7 @@ int read_fmt(int big_endian, int sns, int mwv) { - int codec; + int codec, bps; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; @@ -117,15 +117,12 @@ int read_fmt(int big_endian, fmt->channel_count = read_16bit(current_chunk+0x0a,streamFile); fmt->block_size = read_16bit(current_chunk+0x14,streamFile); + bps = read_16bit(current_chunk+0x16,streamFile); codec = (uint16_t)read_16bit(current_chunk+0x8,streamFile); - /* 0x007A is apparently "Voxware SC3" but in .MED it's just fake MS-IMA */ - if (check_extensions(streamFile,"med") && codec == 0x007A) - codec = 0x11; - switch (codec) { - case 1: /* PCM */ - switch (read_16bit(current_chunk+0x16,streamFile)) { + case 0x01: /* PCM */ + switch (bps) { case 16: if (big_endian) { fmt->coding_type = coding_PCM16BE; @@ -142,39 +139,54 @@ int read_fmt(int big_endian, goto fail; } break; - case 2: /* MS ADPCM */ - /* ensure 4bps */ - if (read_16bit(current_chunk+0x16,streamFile)!=4) - goto fail; + case 0x02: /* MS ADPCM */ + if (bps != 4) /* ensure 4bps */ + goto fail; fmt->coding_type = coding_MSADPCM; fmt->interleave = 0; + break; - break; - case 0x11: /* MS IMA ADCM */ - /* ensure 4bps */ - if (read_16bit(current_chunk+0x16,streamFile)!=4) + case 0x11: /* MS IMA ADPCM */ + if (bps != 4) /* ensure 4bps */ goto fail; fmt->coding_type = coding_MS_IMA; fmt->interleave = 0; break; - case 0x69: /* MS IMA ADCM - Rayman Raving Rabbids 2 (PC) */ - /* ensure 4bps */ - if (read_16bit(current_chunk+0x16,streamFile)!=4) + + case 0x69: /* MS IMA ADPCM - Rayman Raving Rabbids 2 (PC) */ + if (bps != 4) /* ensure 4bps */ goto fail; fmt->coding_type = coding_MS_IMA; fmt->interleave = 0; break; - case 0x555: /* Level-5 0x555 ADPCM */ + + case 0x007A: /* MS IMA ADPCM (LA Rush, Psi Ops PC) */ + /* 0x007A is apparently "Voxware SC3" but in .MED it's just MS-IMA */ + if (!check_extensions(streamFile,"med")) + goto fail; + + if (bps == 4) /* normal MS IMA */ + fmt->coding_type = coding_MS_IMA; + else if (bps == 3) /* 3-bit MS IMA, used in a very few files */ + goto fail; //fmt->coding_type = coding_MS_IMA_3BIT; + else + goto fail; + fmt->interleave = 0; + break; + + case 0x0555: /* Level-5 0x555 ADPCM */ if (!mwv) goto fail; fmt->coding_type = coding_L5_555; fmt->interleave = 0x12; break; + case 0x5050: /* Ubisoft .sns uses this for DSP */ if (!sns) goto fail; fmt->coding_type = coding_NGC_DSP; fmt->interleave = 8; break; + #ifdef VGM_USE_FFMPEG case 0x270: /* ATRAC3 */ #if defined(VGM_USE_FFMPEG) && !defined(VGM_USE_MAIATRAC3PLUS) @@ -184,6 +196,7 @@ int read_fmt(int big_endian, fmt->interleave = 0; break; #endif /* VGM_USE_FFMPEG */ + #ifdef VGM_USE_MAIATRAC3PLUS case 0xFFFE: /* WAVEFORMATEXTENSIBLE / ATRAC3plus */ if (read_32bit(current_chunk+0x20,streamFile) == 0xE923AABF && @@ -386,7 +399,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { * To ensure their stuff is parsed in wwise.c we reject their JUNK, which they put almost always. * As JUNK is legal (if unusual) we only reject those codecs. * (ex. Cave PC games have PCM16LE + JUNK + smpl created by "Samplitude software") */ - if (JunkFound && (fmt.coding_type==coding_MSADPCM || fmt.coding_type==coding_MS_IMA)) goto fail; + if (JunkFound + && check_extensions(streamFile,"wav,lwav") /* for some .MED IMA */ + && (fmt.coding_type==coding_MSADPCM || fmt.coding_type==coding_MS_IMA)) goto fail; switch (fmt.coding_type) { case coding_PCM16LE: From bf34339b4116a9284910c56675f9c9d0fba7eaa2 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 1 Jul 2017 23:02:24 +0200 Subject: [PATCH 2/4] 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 --- fb2k/foo_filetypes.h | 1 + src/coding/coding.h | 2 +- src/coding/eaxa_decoder.c | 254 +++++++------ src/formats.c | 20 +- src/layout/blocked.c | 12 +- src/layout/ea_block.c | 259 +++++++------ src/meta/ea_header.c | 775 +++++++++++++++++++++++++------------- src/meta/meta.h | 2 +- src/vgmstream.c | 10 +- src/vgmstream.h | 35 +- 10 files changed, 822 insertions(+), 548 deletions(-) diff --git a/fb2k/foo_filetypes.h b/fb2k/foo_filetypes.h index 0beb2125..3dddc017 100644 --- a/fb2k/foo_filetypes.h +++ b/fb2k/foo_filetypes.h @@ -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); diff --git a/src/coding/coding.h b/src/coding/coding.h index eec73c91..43cd3d86 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -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 */ diff --git a/src/coding/eaxa_decoder.c b/src/coding/eaxa_decoder.c index 9d748a94..b1eb2bf5 100644 --- a/src/coding/eaxa_decoder.c +++ b/src/coding/eaxa_decoder.c @@ -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; ioffset+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; ioffset+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; ioffset+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; ioffset+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; ioffset+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; ioffset+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; + } } diff --git a/src/formats.c b/src/formats.c index c67702f1..919b2a95 100644 --- a/src/formats.c +++ b/src/formats.c @@ -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"}, diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 21587c8b..2ff3a9ea 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -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; diff --git a/src/layout/ea_block.c b/src/layout/ea_block.c index 800192a1..99622f4c 100644 --- a/src/layout/ea_block.c +++ b/src/layout/ea_block.c @@ -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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;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;ichannels;i++) - vgmstream->ch[i].offset = vgmstream->current_block_offset+8+(i*(vgmstream->current_block_size/2)); - } else { + for(i=0;ichannels;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;ichannels;i++) + vgmstream->ch[i].offset = vgmstream->current_block_offset+8+(i*(vgmstream->current_block_size/2)); + } else { - for (i=0;ichannels;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;ichannels;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; } diff --git a/src/meta/ea_header.c b/src/meta/ea_header.c index 293f4c3e..428027d8 100644 --- a/src/meta/ea_header.c +++ b/src/meta/ea_header.c @@ -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_offsetplatform==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;ich[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; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 7f9d5f91..98b4c4e7 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -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); diff --git a/src/vgmstream.c b/src/vgmstream.c index c14f311c..11d8979b 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -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;chanchannels;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); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 06f59ddc..78469221 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -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 */ From d85c033c9deb473b86c89ce48500bb806d6ac0a7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 1 Jul 2017 23:27:35 +0200 Subject: [PATCH 3/4] rename some EA related things (cleanup) - eaxa_decoder to ea_decoder - ea_header to ea_schl - ea_block* to ea_schl_block* --- src/coding/coding.h | 2 +- src/coding/{eaxa_decoder.c => ea_decoder.c} | 0 src/layout/blocked.c | 2 +- src/layout/ea_block.c | 2 +- src/layout/layout.h | 2 +- src/libvgmstream.vcproj | 4 ++-- src/libvgmstream.vcxproj | 4 ++-- src/libvgmstream.vcxproj.filters | 4 ++-- src/meta/{ea_header.c => ea_schl.c} | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) rename src/coding/{eaxa_decoder.c => ea_decoder.c} (100%) rename src/meta/{ea_header.c => ea_schl.c} (99%) diff --git a/src/coding/coding.h b/src/coding/coding.h index 43cd3d86..3e04865e 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -73,7 +73,7 @@ size_t ps_bytes_to_samples(size_t bytes, int channels); void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void init_get_high_nibble(VGMSTREAM * vgmstream); -/*eaxa_decoder */ +/* ea_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_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); diff --git a/src/coding/eaxa_decoder.c b/src/coding/ea_decoder.c similarity index 100% rename from src/coding/eaxa_decoder.c rename to src/coding/ea_decoder.c diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 2ff3a9ea..326fdb01 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -69,7 +69,7 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * xa_block_update(vgmstream->next_block_offset,vgmstream); break; case layout_ea_blocked: - ea_block_update(vgmstream->next_block_offset,vgmstream); + ea_schl_block_update(vgmstream->next_block_offset,vgmstream); break; case layout_eacs_blocked: eacs_block_update(vgmstream->next_block_offset,vgmstream); diff --git a/src/layout/ea_block.c b/src/layout/ea_block.c index 99622f4c..fea2cf7a 100644 --- a/src/layout/ea_block.c +++ b/src/layout/ea_block.c @@ -3,7 +3,7 @@ #include "../vgmstream.h" /* set up for the block at the given offset */ -void ea_block_update(off_t block_offset, VGMSTREAM * vgmstream) { +void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream) { int i; STREAMFILE* streamFile = vgmstream->ch[0].streamfile; uint32_t id; diff --git a/src/layout/layout.h b/src/layout/layout.h index bb4cb560..0c806cb6 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -15,7 +15,7 @@ void halpst_block_update(off_t block_ofset, VGMSTREAM * vgmstream); void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream); -void ea_block_update(off_t block_offset, VGMSTREAM * vgmstream); +void ea_schl_block_update(off_t block_offset, VGMSTREAM * vgmstream); void eacs_block_update(off_t block_offset, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 77b6daec..3ea793d0 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -337,7 +337,7 @@ > - + @@ -412,7 +412,7 @@ - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 749f899c..2e2eedf4 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -184,7 +184,7 @@ meta\Source Files - + meta\Source Files @@ -781,7 +781,7 @@ coding\Source Files - + coding\Source Files diff --git a/src/meta/ea_header.c b/src/meta/ea_schl.c similarity index 99% rename from src/meta/ea_header.c rename to src/meta/ea_schl.c index 428027d8..69c6fc86 100644 --- a/src/meta/ea_header.c +++ b/src/meta/ea_schl.c @@ -191,7 +191,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - ea_block_update(start_offset,vgmstream); + ea_schl_block_update(start_offset,vgmstream); return vgmstream; From 68071467c23aa9b1a7f4a84303262ef9c837b163 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 1 Jul 2017 23:55:10 +0200 Subject: [PATCH 4/4] Add MPEG to EA SCHl [Harry Potter and the Chamber of Secrets (PS2)] --- src/coding/mpeg_decoder.c | 20 +++++++++----- src/meta/ea_schl.c | 55 ++++++++++++++++++++++++++++++++++++--- src/vgmstream.h | 2 +- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c index d891cd61..aac39747 100644 --- a/src/coding/mpeg_decoder.c +++ b/src/coding/mpeg_decoder.c @@ -373,7 +373,8 @@ static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data data->bytes_used_in_interleave_buffer = 0; for (i=0; i < data->ms_size; i++) { - if (data->interleave_type == MPEG_P3D) /* P3D have a strange way to interleave so just use first offset */ + if (data->interleave_type == MPEG_P3D /* P3D have a strange way to interleave so just use first offset */ + || data->interleave_type == MPEG_EA) /* EA MPEG is simply frame by frame normal MPEG */ decode_mpeg_interleave_samples(&vgmstream->ch[0], data, data->ms[i], channels, i, vgmstream->interleave_block_size); else decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size); @@ -529,12 +530,6 @@ static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, of /* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */ if ( !mpeg_get_frame_info(streamfile, offset, &info) ) goto fail; - - /* could mess some calcs */ - VGM_ASSERT(data->sample_rate_per_frame != info.sample_rate || data->samples_per_frame != info.frame_samples, - "MPEG: variable frame info found @ 0x%08lx", offset); - - /* new frame */ data->current_frame_size = info.frame_size; /* get FSB padding for MPEG1/2 Layer III (MPEG1 Layer II doesn't use it, and Layer I doesn't seem to be supported) */ @@ -543,6 +538,10 @@ static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, of ? data->interleave_value - (data->current_frame_size % data->interleave_value) : 0; } + + /* could mess some calcs */ + VGM_ASSERT(data->sample_rate_per_frame != info.sample_rate || data->samples_per_frame != info.frame_samples, + "MPEG: variable frame info found @ 0x%08lx", offset); } else if (data->interleave_type == MPEG_P3D) { /* varying frames size, even though the frame header says 0x120 */ uint32_t header = read_32bitBE(offset,streamfile); @@ -561,7 +560,14 @@ static int update_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamfile, of VGM_LOG("MPEG: unknown frame size @ %lx, %x\n", offset, read_32bitBE(offset+0x120,streamfile)); goto fail; } + } + else if (data->interleave_type == MPEG_EA) { /* straight frame by frame */ + mpeg_frame_info info; + /* Manually find new frame size. Not ideal but mpg123_info.framesize is wrong sometimes */ + if ( !mpeg_get_frame_info(streamfile, offset, &info) ) + goto fail; + data->current_frame_size = info.frame_size; } return 1; diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index 69c6fc86..1b8fff1b 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -71,6 +71,7 @@ typedef struct { 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); +static off_t get_ea_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); /* EA SCHl - from EA games (roughly 1997~2010, generated by EA Canada's sx.exe / Sound eXchange) */ @@ -170,9 +171,26 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { } break; - case EA_CODEC2_MT5: /* MicroTalk (5:1) */ +#ifdef VGM_USE_MPEG case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */ - case EA_CODEC2_LAYER3: /* MPEG Layer III, aka MP3 */ + case EA_CODEC2_LAYER3: { /* MPEG Layer III, aka MP3 */ + mpeg_codec_data *mpeg_data = NULL; + coding_t mpeg_coding_type; + + off_t mpeg_start_offset = get_ea_mpeg_start_offset(streamFile, start_offset, &ea); + if (!mpeg_start_offset) goto fail; + + mpeg_data = init_mpeg_codec_data_interleaved(streamFile, mpeg_start_offset, &mpeg_coding_type, vgmstream->channels, MPEG_EA, 0); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->coding_type = mpeg_coding_type; + //vgmstream->layout_type = layout_mpeg; + //mpeg_set_error_logging(mpeg_data, 0); /* should not be needed anymore with the interleave decoder */ + break; + } +#endif + + case EA_CODEC2_MT5: /* MicroTalk (5:1) */ case EA_CODEC2_EALAYER3: /* MP3 variant */ default: VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea.codec2, ea.platform); @@ -504,8 +522,7 @@ static int get_ea_total_samples(STREAMFILE* streamFile, off_t start_offset, cons 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; + int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; while (block_offset < file_size) { uint32_t id, block_size; @@ -559,3 +576,33 @@ static int get_ea_total_samples(STREAMFILE* streamFile, off_t start_offset, cons return num_samples; } + +/* find data start offset inside the first SCDl; not very elegant but oh well */ +static off_t get_ea_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { + 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); + 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 */ + off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first channel offset is ok, MPEG channels share offsets */ + return block_offset + 0x0c + ea->channels*0x04 + offset; + } else if (id == 0x5343436C) { /* "SCCl" data count found */ + block_offset += block_size; /* size includes header */ + continue; + } else { + goto fail; + } + } + +fail: + return 0; +} diff --git a/src/vgmstream.h b/src/vgmstream.h index 78469221..17773818 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -825,7 +825,7 @@ typedef struct { #endif #ifdef VGM_USE_MPEG -typedef enum { /*MPEG_NONE,*/ MPEG_FIXED, MPEG_FSB, MPEG_P3D } mpeg_interleave_type; +typedef enum { /*MPEG_NONE,*/ MPEG_FIXED, MPEG_FSB, MPEG_P3D, MPEG_EA } mpeg_interleave_type; typedef struct { uint8_t *buffer; /* raw (coded) data buffer */ size_t buffer_size;