From a558f9b8b57f367f177a99acc220e79f4604e927 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 23 Jul 2017 16:49:07 -0700 Subject: [PATCH] Updated VGMStream to r1050-590-gbb8966a6. --- .../vgmstream.xcodeproj/project.pbxproj | 16 + .../vgmstream/vgmstream/src/coding/coding.h | 10 +- .../vgmstream/src/coding/ea_decoder.c | 253 ++++--- .../vgmstream/src/coding/ima_decoder.c | 96 ++- Frameworks/vgmstream/vgmstream/src/formats.c | 19 +- .../vgmstream/vgmstream/src/layout/blocked.c | 3 + .../vgmstream/vgmstream/src/layout/ea_block.c | 364 +++++----- .../vgmstream/src/layout/hwas_blocked.c | 24 + .../vgmstream/vgmstream/src/layout/layout.h | 2 + .../vgmstream/vgmstream/src/meta/bfstm.c | 5 +- .../vgmstream/vgmstream/src/meta/bfwav.c | 48 +- .../vgmstream/vgmstream/src/meta/ea_schl.c | 485 +++++++++---- .../vgmstream/src/meta/ea_schl_fixed.c | 124 ++++ .../vgmstream/vgmstream/src/meta/maxis_xa.c | 55 +- .../vgmstream/vgmstream/src/meta/meta.h | 6 + .../vgmstream/vgmstream/src/meta/nds_hwas.c | 68 +- .../vgmstream/vgmstream/src/meta/nds_strm.c | 67 +- .../vgmstream/src/meta/nds_strm_ffta2.c | 48 ++ .../vgmstream/vgmstream/src/meta/ngc_adpdtk.c | 45 +- .../vgmstream/vgmstream/src/meta/pc_adp.c | 6 +- .../vgmstream/vgmstream/src/meta/ps2_strlr.c | 10 +- .../vgmstream/vgmstream/src/meta/psx_cdxa.c | 2 + .../vgmstream/vgmstream/src/meta/txth.c | 678 ++++++++++++++++++ Frameworks/vgmstream/vgmstream/src/meta/xma.c | 22 +- .../vgmstream/vgmstream/src/streamfile.c | 26 + .../vgmstream/vgmstream/src/streamfile.h | 4 + .../vgmstream/vgmstream/src/vgmstream.c | 46 +- .../vgmstream/vgmstream/src/vgmstream.h | 15 +- 28 files changed, 1864 insertions(+), 683 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/nds_strm_ffta2.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/txth.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index ae7c76add..56cad539b 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 48C2650E1A5D420800A0A3D6 /* vorbisfile.h */; }; + 8301659A1F256BD000CA0941 /* txth.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165971F256BD000CA0941 /* txth.c */; }; + 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165981F256BD000CA0941 /* ea_schl_fixed.c */; }; + 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165991F256BD000CA0941 /* nds_strm_ffta2.c */; }; + 830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165A11F256BF400CA0941 /* hwas_blocked.c */; }; 8313E3E61902020400B4B6F1 /* mpg123.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8313E3431901FBDD00B4B6F1 /* mpg123.framework */; }; 8313E3E71902021800B4B6F1 /* mpg123.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8313E3431901FBDD00B4B6F1 /* mpg123.framework */; }; 831BA6181EAC61A500CF89B0 /* adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA60E1EAC61A500CF89B0 /* adx.c */; }; @@ -492,6 +496,10 @@ /* Begin PBXFileReference section */ 48C2650E1A5D420800A0A3D6 /* vorbisfile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = vorbisfile.h; path = ../Vorbis/include/vorbis/vorbisfile.h; sourceTree = ""; }; + 830165971F256BD000CA0941 /* txth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = txth.c; sourceTree = ""; }; + 830165981F256BD000CA0941 /* ea_schl_fixed.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl_fixed.c; sourceTree = ""; }; + 830165991F256BD000CA0941 /* nds_strm_ffta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_strm_ffta2.c; sourceTree = ""; }; + 830165A11F256BF400CA0941 /* hwas_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hwas_blocked.c; sourceTree = ""; }; 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = mpg123.xcodeproj; path = ../mpg123/mpg123.xcodeproj; sourceTree = ""; }; 831BA60E1EAC61A500CF89B0 /* adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx.c; sourceTree = ""; }; 831BA60F1EAC61A500CF89B0 /* ogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogl.c; sourceTree = ""; }; @@ -1061,6 +1069,7 @@ 836F6DFF18BDC2180095E648 /* layout */ = { isa = PBXGroup; children = ( + 830165A11F256BF400CA0941 /* hwas_blocked.c */, 831BD1201EEE1D2A00198540 /* rws_blocked.c */, 836F6E0018BDC2180095E648 /* aax_layout.c */, 836F6E0118BDC2180095E648 /* aix_layout.c */, @@ -1102,6 +1111,9 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( + 830165971F256BD000CA0941 /* txth.c */, + 830165981F256BD000CA0941 /* ea_schl_fixed.c */, + 830165991F256BD000CA0941 /* nds_strm_ffta2.c */, 83CAB8DC1F0B0744001BC993 /* pc_xa30.c */, 83CAB8E11F0B0745001BC993 /* wii_04sw.c */, 831BD11F1EEE1CF200198540 /* ngc_ulw.c */, @@ -1570,6 +1582,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8301659A1F256BD000CA0941 /* txth.c in Sources */, + 8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */, + 8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */, 83CAB8E31F0B0755001BC993 /* pc_xa30.c in Sources */, 83CAB8E21F0B0752001BC993 /* wii_04sw.c in Sources */, 839B54571EEE1DA000048A2D /* rws_blocked.c in Sources */, @@ -1708,6 +1723,7 @@ 836F6F3218BDC2190095E648 /* ngc_dsp_decoder.c in Sources */, 836F704218BDC2190095E648 /* wii_sts.c in Sources */, 836F703918BDC2190095E648 /* vgs.c in Sources */, + 830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */, 836F6F2C18BDC2190095E648 /* mp4_aac_decoder.c in Sources */, 836F701F18BDC2190095E648 /* riff.c in Sources */, 836F6F9318BDC2190095E648 /* ivaud.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 3e04865e3..1e871ecfe 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -18,7 +18,7 @@ void g72x_init_state(struct g72x_state *state_ptr); void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); -void decode_int_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_xbox_ima_int(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); void decode_dvi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_eacs_ima(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); @@ -32,6 +32,7 @@ void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * o void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); +size_t ima_bytes_to_samples(size_t bytes, int channels); /* ngc_dsp_decoder */ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -74,9 +75,10 @@ void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t void init_get_high_nibble(VGMSTREAM * vgmstream); /* 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); +void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* sdx2_decoder */ void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c index b1eb2bf57..6466b1e9f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ea_decoder.c @@ -1,33 +1,40 @@ #include "coding.h" #include "../util.h" -/* Various EA ADPCM codecs */ +/* Various EA ADPCM codecs evolved from CDXA */ -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 +/* + * Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games): + * coef1 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 0]; + * coef2 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 1]; + */ +/* +static const int32_t EA_XA_TABLE2[28] = { + 0, 0, 240, 0, + 460, -208, 392, -220, + 0, 0, 240, 0, + 460, 0, 392, 0, + 0, 0, 0, 0, + -208, -1, -220, -1, + 0, 0, 0, 0x3F70 +}; +*/ + +static const int EA_XA_TABLE[20] = { + 0, 240, 460, 392, + 0, 0, -208, -220, + 0, 1, 3, 4, + 7, 8, 10, 11, + 0, -1, -3, -4 }; -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) { +/* EA XA v2 (inconsistently called EAXA or EA-XA too); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on nibble expand */ +void decode_ea_xa_v2(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; //suboffset within channel + int32_t coef1, coef2; + int i, shift; + off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */ first_sample = first_sample%28; @@ -35,7 +42,7 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing frame_info = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile); channel_offset++; - if (frame_info == 0xEE) { /* PCM frame (used in later revisions), always BE */ + if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples 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; @@ -45,134 +52,182 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing channel_offset+=2; } - // Only increment offset on complete frame + /* Only increment offset on complete frame */ if (channel_offset-stream->channel_start_offset == (2*28)+5) stream->channel_start_offset += (2*28)+5; } else { /* ADPCM frame */ - coef1 = EA_XA_TABLE[(((frame_info >> 4) & 0x0F) << 1)]; - coef2 = EA_XA_TABLE[(((frame_info >> 4) & 0x0F) << 1) + 1]; + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; shift = (frame_info & 0x0F) + 8; 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; + uint8_t sample_byte, sample_nibble; + int32_t sample; + off_t byte_offset = (stream->offset + channel_offset + i/2); - outbuf[sample_count] = clamp16(sample); + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (!(i%2) ? sample_byte >> 4 : sample_byte & 0x0F); /* i=even > high nibble */ + sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8; + sample = clamp16(sample); + + outbuf[sample_count] = sample; stream->adpcm_history2_32 = stream->adpcm_history1_32; stream->adpcm_history1_32 = sample; } + channel_offset += i/2; - channel_offset+=i/2; - - // Only increment offset on complete frame - if(channel_offset - stream->channel_start_offset == 0x0F) + /* Only increment offset on complete frame */ + if (channel_offset - stream->channel_start_offset == 0x0F) stream->channel_start_offset += 0x0F; } } -/* 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) { +/* EA XA v1 stereo (aka "EA ADPCM") */ +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; - VGMSTREAMCHANNEL *stream = &(vgmstream->ch[channel]); - off_t channel_offset=stream->channel_start_offset; + int32_t coef1, coef2; + int i, sample_count, shift; + off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */ + int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */ - vgmstream->get_high_nibble = !vgmstream->get_high_nibble; /* for stereo subinterleave, L=high nibble, R=low nibble */ + first_sample = first_sample % 28; - first_sample = first_sample%28; - - /* header */ //todo mono/interleave decoder + /* header (coefs ch0+ch1 + shift ch0+ch1) */ 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]; + coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0]; + coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4]; + shift = (frame_info & 0x0F) + 8; frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile); channel_offset++; - shift = (vgmstream->get_high_nibble ? frame_info & 0x0F : frame_info >> 4) + 8; - + shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8; + /* samples */ for (i=first_sample,sample_count=0; ioffset + channel_offset + i); - sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset+i,stream->streamfile); + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (hn ? sample_byte >> 4 : sample_byte & 0x0F); + sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; + sample = clamp16(sample); - 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); + outbuf[sample_count] = sample; stream->adpcm_history2_32 = stream->adpcm_history1_32; stream->adpcm_history1_32 = sample; } - channel_offset+=i; + 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) { +/* EA-XA v1 mono/interleave */ +void decode_ea_xa_int(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; - 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; + int32_t coef1, coef2; + int i, sample_count, shift; + off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */ - first_sample = first_sample%28; - frame_info = read_8bit(channel_offset,stream->streamfile); + first_sample = first_sample % 28; - 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; + /* header (coefs+shift ch0) */ + frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile); + channel_offset++; + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; + /* samples */ for (i=first_sample,sample_count=0; ioffset + channel_offset + i/2); - sample_byte = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile); + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (!(i%2) ? sample_byte >> 4 : sample_byte & 0x0F); /* i=even > high nibble */ + sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; + sample = clamp16(sample); - 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] = sample; + stream->adpcm_history2_32 = stream->adpcm_history1_32; + stream->adpcm_history1_32 = sample; + } + channel_offset += i/2; - outbuf[sample_count] = clamp16(sample); + /* Only increment offset on complete frame */ + if(channel_offset - stream->channel_start_offset == 0x0F) + stream->channel_start_offset += 0x0F; +} + +/* Maxis EA-XA v1 (mono+stereo), differing slightly in the header layout in stereo mode */ +void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + uint8_t frame_info; + int32_t coef1, coef2; + int i, sample_count, shift; + off_t channel_offset = stream->channel_start_offset; + int frame_size = channelspacing * 15; /* mono samples have a frame of 15, stereo files have frames of 30 */ + + first_sample = first_sample % 28; + + /* header (coefs+shift ch0 + coefs+shift ch1) */ + frame_info = read_8bit(channel_offset,stream->streamfile); + channel_offset += channelspacing; + coef1 = EA_XA_TABLE[(frame_info >> 4) + 0]; + coef2 = EA_XA_TABLE[(frame_info >> 4) + 4]; + shift = (frame_info & 0x0F) + 8; + + /* samples */ + for (i=first_sample,sample_count=0; ioffset + channel_offset); + + sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile); + sample_nibble = (i&1) ? sample_byte & 0x0F : sample_byte >> 4; + sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */ + sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8; + sample = clamp16(sample); + + outbuf[sample_count] = sample; stream->adpcm_history2_32 = stream->adpcm_history1_32; stream->adpcm_history1_32 = sample; if(i&1) stream->offset+=channelspacing; } - channel_offset+=i; - // Only increment offset on complete frame - - if(channel_offset-stream->channel_start_offset==frameSize) { - stream->channel_start_offset+=frameSize; + /* Only increment offset on complete frame */ + if (channel_offset - stream->channel_start_offset == frame_size) { + stream->channel_start_offset += frame_size; stream->offset=0; } } + + +/* EA MicroTalk 10:1 / 5:1 */ +/** + * Rarely used but can be found in the wild: FIFA 2001 (PS2), FIFA Soccer 2002 (PS2) + * + * Decoding algorithm is unknown; some info found by analyzing sx.exe output: + * - Comes in 10:1 or 5:1 compression varieties (the later's byte layout looks similar but has roughly double frame size) + * - Also with "PCM samples" flag before each frame (later version) or without (first version) + * - When PCM flag is 0xEE it has 16b ? + 16b num_samples + PCM samples placed right after the frame, but they + * are written *before* (presumably so they have something while the frame is decoded), like EALayer3. + * - VBR ADPCM, apparently similar to Westwood VBR ADPCM: first byte seems a header with mode+count, but after it may + * be 8 bytes(?) of coefs/hist (unlike Westwood's), then data. Samples per frame changes with the mode used. + * ex. decoding pure silence (0000) takes 0x2E (10:1) or 0x48 (5:1) into 432 samples (RLE mode) + * - Variable frame size but seems to range from 0x20 to 0x80 (in 5:1 at least) + * - After a new SCDl block, first byte (in each channel) is a flag but various values have no effect in the output + * (01=first block, 00=normal block?) and should be skipped in the block parser. + * + */ +//void decode_ea_mt10(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index b2210ae0f..1f73a3aaa 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -307,8 +307,7 @@ void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel stream->adpcm_step_index = step_index; } -/* For multichannel the internal layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) - * Has extra support for EA blocks, probably could be simplified */ +/* For multichannel the internal layout is (I think) mixed stereo channels (ex. 6ch: 2ch + 2ch + 2ch) */ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { int i, sample_count; @@ -326,11 +325,7 @@ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * o //normal header (per channel) if (first_sample == 0) { off_t header_offset; - if(vgmstream->layout_type==layout_ea_blocked) { - header_offset = stream->offset; - } else { - header_offset = stream->offset + 4*(channel%2); - } + header_offset = stream->offset + 4*(channel%2); hist1 = read_16bitLE(header_offset,stream->streamfile); step_index = read_16bitLE(header_offset+2,stream->streamfile); @@ -341,13 +336,9 @@ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * o for (i=first_sample,sample_count=0; ilayout_type==layout_ea_blocked) - offset = stream->offset + 4 + i/8*4 + (i%8)/2; - else { - offset = (channelspacing==1) ? - stream->offset + 4*(channel%2) + 4 + i/8*4 + (i%8)/2 : - stream->offset + 4*(channel%2) + 4*2 + i/8*4*2 + (i%8)/2; - } + offset = (channelspacing==1) ? + stream->offset + 4*(channel%2) + 4 + i/8*4 + (i%8)/2 : + stream->offset + 4*(channel%2) + 4*2 + i/8*4*2 + (i%8)/2; nibble_shift = (i&1?4:0); //low nibble first ms_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index); @@ -355,35 +346,27 @@ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * o } //internal interleave: increment offset on complete frame - if(vgmstream->layout_type==layout_ea_blocked) { + if (channelspacing==1) { if(offset-stream->offset==32+3) // ?? stream->offset+=36; } else { - if(channelspacing==1) { - if(offset-stream->offset==32+3) // ?? - stream->offset+=36; - } else { - if(offset-stream->offset==64+(4*(channel%2))+3) // ?? - stream->offset+=36*channelspacing; - } + if(offset-stream->offset==64+(4*(channel%2))+3) // ?? + stream->offset+=36*channelspacing; } stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } -void decode_int_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i, sample_count; +/* mono XBOX ADPCM for interleave */ +void decode_xbox_ima_int(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count = 0; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset = stream->offset; - - //semi-internal interleave (0x24 size), mixed channels (4 byte per ch)? - int block_samples = (vgmstream->channels==1) ? - 32 : - 32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % below + //internal interleave (0x24 size), mono + int block_samples = (0x24 - 0x4) * 2; /* block size - header, 2 samples per byte */ first_sample = first_sample % block_samples; //normal header @@ -394,27 +377,31 @@ void decode_int_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample step_index = read_16bitLE(header_offset+2,stream->streamfile); if (step_index < 0) step_index=0; if (step_index > 88) step_index=88; - } - for (i=first_sample,sample_count=0; ioffset + 4 + i/8*4 + (i%8)/2; - nibble_shift = (i&1?4:0); //low nibble first - - ms_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index); + //must write history from header as last nibble/sample in block is almost always 0 / not encoded outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; + first_sample += 1; + samples_to_do -= 1; } - //internal interleave: increment offset on complete frame - if(channelspacing==1) { - if(offset-stream->offset==32+3) // ?? - stream->offset+=36; - } else { - if(offset-stream->offset==64+(4*(channel%2))+3) // ?? - stream->offset+=36*channelspacing; + for (i=first_sample; i < first_sample + samples_to_do; i++) { /* first_sample + samples_to_do should be block_samples at most */ + off_t byte_offset = stream->offset + 4 + (i-1)/2; + int nibble_shift = ((i-1)&1?4:0); //low nibble first + + //last nibble/sample in block is ignored (next header sample contains it) + if (i < block_samples) { + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + sample_count+=channelspacing; + } } + //internal interleave: increment offset on complete frame; layout test so it works in full mono + // (EA SCHl, internally moves offset) or full interleave (XBOX ADS, externally moves offset in layout) + if (i == block_samples && vgmstream->layout_type != layout_interleave) + stream->offset += 0x24; /*full mono */ + stream->adpcm_history1_32 = hist1; stream->adpcm_step_index = step_index; } @@ -441,6 +428,7 @@ void decode_dvi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci stream->adpcm_step_index = step_index; } +/* basically DVI stereo (high=L + low=R nibbles) and DVI mono (high=L, low=L) all-in-one, can be simplified/removed */ void decode_eacs_ima(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);//todo pass externally for consistency int i, sample_count; @@ -452,14 +440,13 @@ void decode_eacs_ima(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, //no header - //variable nibble order - vgmstream->get_high_nibble = !vgmstream->get_high_nibble; - if((first_sample) && (channelspacing==1)) - vgmstream->get_high_nibble = !vgmstream->get_high_nibble; - for (i=first_sample,sample_count=0; ioffset + i; - int nibble_shift = (vgmstream->get_high_nibble?0:4); //variable nibble order + off_t byte_offset = channelspacing == 1 ? + stream->offset + i/2 : /* mono mode */ + stream->offset + i; /* stereo mode */ + int nibble_shift = channelspacing == 1 ? + (!(i%2) ? 4:0) : /* mono mode (high first) */ + (channel==0 ? 4:0); /* stereo mode (high=L,low=R) */ ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); @@ -695,3 +682,8 @@ size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { /* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels; } + +size_t ima_bytes_to_samples(size_t bytes, int channels) { + /* 2 samples per byte (2 nibbles) in stereo or mono config */ + return bytes / channels * 2; +} diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 919b2a953..48c70d24d 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -197,6 +197,7 @@ static const char* extension_list[] = { "omu", "otm", + "p1d", //txth/reserved [Farming Simulator 18 (3DS)] "p2bt", "p3d", "past", @@ -275,6 +276,7 @@ static const char* extension_list[] = { "swag", "swav", "swd", + "sx", "sxd", "sxd2", @@ -315,11 +317,13 @@ static const char* extension_list[] = { "wb", "wem", "wii", + "wip", //txth/reserved [Colin McRae DiRT (PC)] "wmus", "wp2", "wpd", "wsd", "wsi", + "wv2", //txth/reserved [Slave Zero (PC)] "wvs", "xa", @@ -419,8 +423,10 @@ 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 EA-XA 4-bit ADPCM"}, - {coding_EA_MT10, "Electronic Arts MicroTalk (10:1) 4-bit ADPCM"}, + {coding_EA_XA, "Electronic Arts EA-XA 4-bit ADPCM (v1)"}, + {coding_EA_XA_int, "Electronic Arts EA-XA 4-bit ADPCM (v1) (interleaved)"}, + {coding_EA_XA_V2, "Electronic Arts EA-XA 4-bit ADPCM (v2)"}, + {coding_MAXIS_XA, "Maxis EA-XA 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"}, @@ -428,7 +434,6 @@ 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 ADPCM"}, {coding_IMA_int, "IMA 4-bit ADPCM (interleaved)"}, {coding_IMA, "IMA 4-bit ADPCM"}, {coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"}, @@ -522,6 +527,7 @@ static const layout_info layout_info_list[] = { {layout_ps2_iab_blocked, "IAB blocked"}, {layout_ps2_strlr_blocked, "The Bouncer STR blocked"}, {layout_rws_blocked, "RWS blocked"}, + {layout_hwas_blocked, "HWAS blocked"}, {layout_tra_blocked, "TRA blocked"}, {layout_acm, "ACM blocked"}, {layout_mus_acm, "multiple ACM files, ACM blocked"}, @@ -593,7 +599,8 @@ 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_EA_SCHL, "Electronic Arts SCHl header"}, + {meta_EA_SCHL, "Electronic Arts SCHl header (variable)"}, + {meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"}, {meta_CFN, "tri-Crescendo CAF Header"}, {meta_PS2_VPK, "VPK Header"}, {meta_GENH, "GENH Generic Header"}, @@ -744,7 +751,7 @@ static const meta_info meta_info_list[] = { {meta_WII_NDP, "Vertigo NDP Header"}, {meta_PS2_SPS, "Ape Escape 2 SPS Header"}, {meta_PS2_XA2_RRP, "Acclaim XA2 Header"}, - {meta_NDS_HWAS, "NDS 'HWAS' Header"}, + {meta_NDS_HWAS, "Vicarious Visions HWAS header"}, {meta_NGC_LPS, "Rave Master LPS Header"}, {meta_NAOMI_ADPCM, "NAOMI/NAOMI2 Arcade games ADPCM header"}, {meta_SD9, "beatmania IIDX SD9 header"}, @@ -871,6 +878,8 @@ static const meta_info meta_info_list[] = { {meta_NGC_ULW, "Criterion ULW raw header"}, {meta_PC_XA30, "Reflections XA30 PC header"}, {meta_WII_04SW, "Reflections 04SW header"}, + {meta_TXTH, "TXTH Generic Header"}, + {meta_EA_BNK, "Electronic Arts BNK header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c index 326fdb013..12c25ac61 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked.c @@ -136,6 +136,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * break; case layout_rws_blocked: rws_block_update(vgmstream->next_block_offset,vgmstream); + break; + case layout_hwas_blocked: + hwas_block_update(vgmstream->next_block_offset,vgmstream); break; default: break; diff --git a/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c b/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c index fea2cf7ab..7efa1585e 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/ea_block.c @@ -1,164 +1,200 @@ -#include "layout.h" -#include "../coding/coding.h" -#include "../vgmstream.h" - -/* set up for the block at the given offset */ -void ea_schl_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); /* swap nibble for codecs with stereo subinterleave */ - - /* 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 + block_size; - vgmstream->current_block_size = 0; - vgmstream->current_block_samples = block_samples; - - /* 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; - } - } - - - /* 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; - - 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; - } - - 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); - - 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; -} +#include "layout.h" +#include "../coding/coding.h" +#include "../vgmstream.h" + +/* set up for the block at the given offset */ +void ea_schl_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; + //int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE; + + + /* 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 > 0x00F00000) /* size is always LE, except in early SS/MAC */ + block_size = read_32bitBE(block_offset+0x04,streamFile); + + /* SCxx blocks have size in the header, but others may not. To simplify we just try to find + * a SCDl (main data) every 0x04. EA sometimes concats many small files, so after a SCEl (end block) + * there may be a new SCHl + SCDl too, so this pretends they are a single stream. */ + if (id == 0x5343446C) { /* "SCDl" data block found */ + + /* use num_samples from header if possible; don't calc as 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; + } + + /* guard against false positives (happens in "pIQT" blocks) */ + if (block_size > 0xFFFF || block_samples > 0xFFFF) { /* observed max is ~0xf00 but who knows */ + block_offset += 0x04; + continue; + } + + break; + } + else { + /* movie "pIQT" may be bigger than what block_size says, but seems to help */ + if (id == 0x5343486C || id == 0x5343436C || id == 0x53434C6C || id == 0x70495154) { /* "SCHl" "SCCl" "SCLl" "SCEl" "pIQT" */ + block_offset += block_size; + } else { + block_offset += 0x04; + } + + if (id == 0x5343456C) { /* "SCEl" end block found */ + block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* 32b-aligned, important */ + /* Usually there is padding between SCEl and SCHl too (aligned to 0x80) */ + } + + continue; + } + } + + /* EOF reads: pretend we have samples to please the layout (unsure if this helps) */ + if (block_offset >= file_size) { + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + 0x04; + vgmstream->current_block_samples = vgmstream->num_samples; + return; + } + + + /* set new channel offsets and ADPCM history */ + /* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs + * (with no blocks) may also have them in the first offset, but also may not. To simplify we just read them here. */ + switch(vgmstream->coding_type) { + /* id, size, unk1, unk2, interleaved data */ + case coding_PSX: + for (i = 0; i < vgmstream->channels; i++) { + size_t interleave = (block_size-0x10) / vgmstream->channels; + vgmstream->ch[i].offset = block_offset + 0x10 + i*interleave; + } + /* 0x08/0x0c: unknown (doesn't look like hist or offsets, as 1ch files has them too) */ + + break; + + /* id, size, IMA hist, stereo/mono data */ + case coding_EACS_IMA: + for(i = 0; i < vgmstream->channels; i++) { + off_t header_offset = block_offset + 0xc + i*4; + vgmstream->ch[i].adpcm_history1_32 = read_16bitLE(header_offset+0x00, vgmstream->ch[i].streamfile); + vgmstream->ch[i].adpcm_step_index = read_16bitLE(header_offset+0x02, vgmstream->ch[i].streamfile); + vgmstream->ch[i].offset = block_offset + 0xc + (4*vgmstream->channels); + } + + break; + + /* id, size, samples, hists-per-channel, stereo/interleaved data */ + case coding_EA_XA: + case coding_EA_XA_int: + for (i = 0; i < vgmstream->channels; i++) { + int is_interleaved = vgmstream->coding_type == coding_EA_XA_int; + size_t interleave; + + /* read ADPCM history from all channels before data (not actually read in sx.exe) */ + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); + //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); + + /* the block can have padding so find the channel size from num_samples */ + interleave = is_interleaved ? (block_samples / 28 * 0x0f) : 0; + vgmstream->ch[i].offset = block_offset + 0x0c + vgmstream->channels*0x04 + i*interleave; + } + + break; + + /* id, size, samples, offsets-per-channel, interleaved data (w/ optional hist per channel) */ + default: + for (i = 0; i < vgmstream->channels; i++) { + off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile); + vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start; + } + + /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ + if (vgmstream->codec_version == 1) { + for (i = 0; i < vgmstream->channels; i++) { + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); + //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); + vgmstream->ch[i].offset += 4; + } + } + + break; + } + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + block_size; + vgmstream->current_block_samples = block_samples; + vgmstream->current_block_size = 0; /* uses current_block_samples instead */ + + + /* reset channel sub offset for codecs using it */ + if (vgmstream->coding_type == coding_EA_XA + || vgmstream->coding_type == coding_EA_XA_int + || vgmstream->coding_type == coding_EA_XA_V2) { + 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; + + 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; + } + + vgmstream->current_block_size=block_size-8; + + if(vgmstream->coding_type==coding_EACS_IMA) { + 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++) { + 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/Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c b/Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c new file mode 100644 index 000000000..f55a40fc5 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/layout/hwas_blocked.c @@ -0,0 +1,24 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* a simple headerless block with special adpcm history handling */ +void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + size_t block_size; + + /* no header */ + block_size = vgmstream->full_block_size; + + vgmstream->current_block_offset = block_offset; + vgmstream->next_block_offset = block_offset + + block_size; + vgmstream->current_block_size = block_size; + + /* reset ADPCM history every block (no header with hist or anything) */ + /* probably not 100% exact but good enough to get something decently playable (otherwise there are wild volume swings) */ + for (i=0;ichannels;i++) { + vgmstream->ch[i].adpcm_history1_32 = 0; + vgmstream->ch[i].adpcm_step_index = 0; + vgmstream->ch[i].offset = block_offset; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/layout/layout.h b/Frameworks/vgmstream/vgmstream/src/layout/layout.h index 0c806cb66..2cf51f534 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/layout.h +++ b/Frameworks/vgmstream/vgmstream/src/layout/layout.h @@ -61,6 +61,8 @@ void ps2_strlr_block_update(off_t block_offset, VGMSTREAM * vgmstream); void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream); +void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream); + /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c index 2b93a08ff..6e86c22a6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfstm.c @@ -6,6 +6,7 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; coding_t coding_type; + coding_t coding_PCM16; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; @@ -30,9 +31,11 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; + coding_PCM16 = coding_PCM16BE; } else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* Blaster Master Zero 3DS */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; + coding_PCM16 = coding_PCM16LE; } else { goto fail; } @@ -81,7 +84,7 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { coding_type = coding_PCM8; break; case 1: - coding_type = coding_PCM16BE; + coding_type = coding_PCM16; break; case 2: coding_type = coding_NGC_DSP; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c index a02f09e2a..acf938599 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bfwav.c @@ -6,11 +6,12 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { char filename[PATH_LIMIT]; coding_t coding_type; + coding_t coding_PCM16; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /*int ima = 0;*/ int nsmbu_flag = 0; - int32_t(*read_32bit)(off_t, STREAMFILE*) = read_32bitBE; - int16_t(*read_16bit)(off_t, STREAMFILE*) = read_16bitBE; off_t data_offset; off_t head_offset; @@ -30,8 +31,17 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { if ((uint32_t)read_32bitBE(0, streamFile) != 0x46574156) /* "FWAV" */ goto fail; - if ((uint32_t)read_32bitBE(4, streamFile) != 0xFEFF0040) /* "FWAV" */ + if ((uint16_t)read_16bitBE(4, streamFile) == 0xFEFF) { /* endian marker (BE most common) */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + coding_PCM16 = coding_PCM16BE; + } else if ((uint16_t)read_16bitBE(4, streamFile) == 0xFFFE) { /* LE endian marker */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + coding_PCM16 = coding_PCM16LE; + } else { goto fail; + } /* get head offset, check */ head_offset = read_32bit(0x18, streamFile); @@ -44,14 +54,14 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { /* check type details */ codec_number = read_8bit(head_offset + 0x8, streamFile); loop_flag = read_8bit(head_offset + 0x9, streamFile); - channel_count = read_8bit(head_offset + 0x1F, streamFile); + channel_count = read_32bit(head_offset + 0x1C, streamFile); switch (codec_number) { case 0: coding_type = coding_PCM8; break; case 1: - coding_type = coding_PCM16BE; + coding_type = coding_PCM16; break; case 2: coding_type = coding_NGC_DSP; @@ -72,7 +82,7 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { if (nsmbu_flag) vgmstream->sample_rate = 16000; else - vgmstream->sample_rate = (uint16_t)read_16bit(head_offset + 0xE, streamFile); + vgmstream->sample_rate = (uint16_t)read_32bit(head_offset + 0xC, streamFile); /* channels and loop flag are set by allocate_vgmstream */ vgmstream->loop_start_sample = read_32bit(head_offset + 0x10, streamFile); @@ -87,34 +97,22 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE *streamFile) { vgmstream->meta_type = meta_FWAV; - vgmstream->interleave_block_size = read_32bitBE(read_32bitBE(0x6c, streamFile) + 0x60, streamFile) - 0x18; + vgmstream->interleave_block_size = read_32bit(read_32bit(0x6c, streamFile) + 0x60, streamFile) - 0x18; start_offset = data_offset + 0x20; if (vgmstream->coding_type == coding_NGC_DSP) { - off_t coef_offset; int i, j; - int coef_spacing = 0x2E; - - off_t coeffheader = head_offset + 0x28; - int foundcoef = 0; - while (!(foundcoef)) - { - if ((uint32_t)read_32bit(coeffheader, streamFile) == 0x1F000000) - { - coef_offset = read_32bit(coeffheader + 0xC, streamFile) + coeffheader; - foundcoef = 1; - break; - } - coeffheader++; - } - - for (j = 0; jchannels; j++) { for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset + j*coef_spacing + i * 2, streamFile); + off_t coeffheader = head_offset + 0x1C + read_32bit(head_offset + 0x24 + (j*8), streamFile); + off_t coef_offset; + if ((uint32_t)read_16bit(coeffheader, streamFile) != 0x1F00) goto fail; + + coef_offset = read_32bit(coeffheader + 0xC, streamFile) + coeffheader; + vgmstream->ch[j].adpcm_coef[i] = read_16bit(coef_offset + i * 2, streamFile); } } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c index 1b8fff1bd..ce1c550b0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl.c @@ -15,7 +15,7 @@ #define EA_PLATFORM_PSX 0x01 #define EA_PLATFORM_N64 0x02 #define EA_PLATFORM_MAC 0x03 -//#define EA_PLATFORM_SAT 0x04 // ? +#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 @@ -26,9 +26,10 @@ /* 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_PCM 0x00 +#define EA_CODEC1_VAG 0x01 // unsure +#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, Fifa 98 SAT +#define EA_CODEC1_MT10 0x09 //#define EA_CODEC1_N64 ? #define EA_CODEC2_NONE -1 @@ -48,12 +49,12 @@ #define EA_MAX_CHANNELS 6 typedef struct { - uint8_t id; int32_t num_samples; int32_t sample_rate; int32_t channels; int32_t platform; int32_t version; + int32_t bps; int32_t codec1; int32_t codec2; @@ -68,23 +69,23 @@ typedef struct { int codec_version; } ea_header; -static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length); +static int parse_variable_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); +static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); +static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea); +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int total_streams); -/* EA SCHl - from EA games (roughly 1997~2010, generated by EA Canada's sx.exe / Sound eXchange) */ +/* EA SCHl with variable header - 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; - off_t start_offset; + off_t start_offset, header_offset; size_t header_size; ea_header ea; /* 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")) + if (!check_extensions(streamFile,"str,asf,mus,eam,sng,aud,sx,strm,xa,xsf,exa,stm")) goto fail; /* check header */ @@ -95,44 +96,173 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { goto fail; header_size = read_32bitLE(0x04,streamFile); - if (header_size > 0xF0000000) /* size is always LE, except in early MAC apparently */ + if (header_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ header_size = read_32bitBE(0x04,streamFile); + header_offset = 0x08; - memset(&ea,0,sizeof(ea_header)); - if (!parse_stream_header(streamFile,&ea, 0x08, header_size-4-4)) + if (!parse_variable_header(streamFile,&ea, 0x08, header_size - header_offset)) goto fail; - 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" */ + start_offset = header_size; /* starts in "SCCl" (skipped in block layout) or very rarely "SCDl" and maybe movie blocks */ + + /* rest is common */ + return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, 0, 1); + +fail: + return NULL; +} + +/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */ +VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { + off_t start_offset, header_offset, offset; + size_t header_size; + ea_header ea; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + int i, bnk_version; + int target_stream = 0, total_streams; + + + /* check extension */ + /* .bnk: sfx, .sdt: speech, .mus: streams/jingles (rare) */ + if (!check_extensions(streamFile,"bnk,sdt,mus")) goto fail; + /* check header (doesn't use EA blocks, otherwise very similar to SCHl) */ + if (read_32bitBE(0x00,streamFile) == 0x424E4B6C || /* "BNKl" (common) */ + read_32bitBE(0x00,streamFile) == 0x424E4B62) /* "BNKb" (FIFA 98 SS) */ + offset = 0; + else if (read_32bitBE(0x100,streamFile) == 0x424E4B6C) /* "BNKl" (common) */ + offset = 0x100; /* Harry Potter and the Goblet of Fire (PS2) .mus have weird extra 0x100 bytes */ + else + goto fail; + + /* use header size as endianness flag */ + if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + bnk_version = read_8bit(offset + 0x04,streamFile); + total_streams = read_16bit(offset + 0x06,streamFile); + /* check multi-streams */ + if (target_stream == 0) target_stream = 1; + if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail; + + switch(bnk_version) { + case 0x02: /* early (Need For Speed PC, Fifa 98 SS) */ + header_size = read_32bit(offset + 0x08,streamFile); /* full size */ + header_offset = offset + 0x0c + 0x04*(target_stream-1) + read_32bit(offset + 0x0c + 0x04*(target_stream-1),streamFile); + break; + + case 0x04: /* mid (last used in PSX banks) */ + case 0x05: /* late (generated by sx.exe ~v2+) */ + /* 0x08: header/file size, 0x0C: file size/null, 0x10: always null */ + header_size = get_streamfile_size(streamFile); /* unknown (header is variable and may have be garbage until data) */ + header_offset = offset + 0x14 + 0x04*(target_stream-1) + read_32bit(offset + 0x14 + 0x04*(target_stream-1),streamFile); + break; + + default: + VGM_LOG("EA BNK: unknown version %x\n", bnk_version); + goto fail; + } + + if (!parse_variable_header(streamFile,&ea, header_offset, header_size - header_offset)) + goto fail; + + /* fix absolute offsets so it works in next funcs */ + if (offset) { + for (i = 0; i < ea.channels; i++) { + ea.coefs[i] += offset; + ea.offsets[i] += offset; + } + } + + start_offset = ea.offsets[0]; /* first channel, presumably needed for MPEG */ + + /* special case found in some tests (pcstream had hist, pcbnk no hist, no patch diffs) + * I think this works but what decides if hist is used or not a secret to everybody */ + if (ea.codec2 == EA_CODEC2_EAXA && ea.codec1 == EA_CODEC1_NONE && ea.version == EA_VERSION_V1) { + ea.codec_version = 0; + } + + + /* rest is common */ + return init_vgmstream_ea_variable_header(streamFile, &ea, start_offset, bnk_version, total_streams); + +fail: + return NULL; +} + +/* inits VGMSTREAM from a EA header */ +static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header * ea, off_t start_offset, int bnk_version, int total_streams) { + VGMSTREAM * vgmstream = NULL; + int i, ch; + int is_bnk = bnk_version; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag); + vgmstream = allocate_vgmstream(ea->channels, ea->loop_flag); if (!vgmstream) goto fail; - 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->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->codec_endian = ea.big_endian; - vgmstream->codec_version = ea.codec_version; + vgmstream->codec_endian = ea->big_endian; + vgmstream->codec_version = ea->codec_version; - vgmstream->meta_type = meta_EA_SCHL; - vgmstream->layout_type = layout_ea_blocked; + vgmstream->meta_type = is_bnk ? meta_EA_BNK : meta_EA_SCHL; + + if (is_bnk) { + vgmstream->layout_type = layout_none; + + /* BNKs usually have absolute offsets for all channels ("full" interleave) except in some versions */ + if (vgmstream->channels > 1 && ea->codec1 == EA_CODEC1_PCM) { + int interleave = (vgmstream->num_samples * (ea->bps == 8 ? 0x01 : 0x02)); /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + ea->offsets[i] = ea->offsets[0] + interleave*i; + } + } + else if (vgmstream->channels > 1 && ea->codec1 == EA_CODEC1_VAG) { + int interleave = (vgmstream->num_samples / 28 * 16); /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + ea->offsets[i] = ea->offsets[0] + interleave*i; + } + } + else if (vgmstream->channels > 1 && ea->codec2 == EA_CODEC2_GCADPCM && ea->offsets[0] == ea->offsets[1]) { + /* pcstream+gcadpcm with sx.exe v2, this is probably an bug (even with this parts of the wave are off) */ + int interleave = (vgmstream->num_samples / 14 * 8); /* full interleave */ + for (i = 0; i < vgmstream->channels; i++) { + ea->offsets[i] = ea->offsets[0] + interleave*i; + } + } + } + else { + vgmstream->layout_type = layout_ea_blocked; + } + + if (is_bnk) + vgmstream->num_streams = total_streams; /* 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) { + switch (ea->codec2) { case EA_CODEC2_EAXA: /* EA-XA, CDXA ADPCM variant */ - vgmstream->coding_type = coding_EA_XA; - break; - - case EA_CODEC2_MT10: /* MicroTalk (10:1), aka EA ADPCM (stereo or interleaved) */ - vgmstream->coding_type = coding_EA_MT10; + if (ea->codec1 == EA_CODEC1_EAXA) { + if (ea->platform != EA_PLATFORM_SAT && ea->channels > 1) + vgmstream->coding_type = coding_EA_XA; /* original version, stereo stream */ + else + vgmstream->coding_type = coding_EA_XA_int; /* interleaved mono streams */ + } + else { /* later revision with PCM blocks and slighty modified decoding */ + vgmstream->coding_type = coding_EA_XA_V2; + } break; case EA_CODEC2_S8: /* PCM8 */ @@ -152,7 +282,7 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { break; case EA_CODEC2_XBOXADPCM: /* XBOX IMA (interleaved mono) */ - vgmstream->coding_type = coding_XBOX; /* stereo decoder actually, but has a special case for EA */ + vgmstream->coding_type = coding_XBOX_int; break; case EA_CODEC2_GCADPCM: /* DSP */ @@ -160,12 +290,11 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { /* 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; + 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); + vgmstream->ch[ch].adpcm_coef[i] = read_16bit(ea->coefs[ch] + i*2, streamFile); } } } @@ -174,42 +303,92 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) { #ifdef VGM_USE_MPEG case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */ 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); + off_t mpeg_start_offset = is_bnk ? + start_offset : + get_ea_stream_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 */ + vgmstream->codec_data = init_mpeg_codec_data_interleaved(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, 0); + if (!vgmstream->codec_data) goto fail; + break; } #endif - case EA_CODEC2_MT5: /* MicroTalk (5:1) */ + case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */ + case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */ case EA_CODEC2_EALAYER3: /* MP3 variant */ default: - VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea.codec2, ea.platform); + VGM_LOG("EA: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform); goto fail; } - /* 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; - } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - ea_schl_block_update(start_offset,vgmstream); + /* fix num_samples for streams with multiple SCHl */ + if (!is_bnk) { + int total_samples = get_ea_stream_total_samples(streamFile, start_offset, ea); + if (total_samples > vgmstream->num_samples) + vgmstream->num_samples = total_samples; + } + + + if (is_bnk) { + /* setup channel offsets */ + if (vgmstream->coding_type == coding_EA_XA) { /* shared */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[0]; + } + //} else if (vgmstream->layout_type == layout_interleave) { /* interleaved */ + // for (i = 0; i < vgmstream->channels; i++) { + // vgmstream->ch[i].offset = ea->offsets[0] + vgmstream->interleave_block_size*i; + // } + } else { /* absolute */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = ea->offsets[i]; + } + } + + /* setup ADPCM hist */ + switch(vgmstream->coding_type) { + /* id, size, samples, hists-per-channel, stereo/interleaved data */ + case coding_EA_XA: + /* read ADPCM history from all channels before data (not actually read in sx.exe) */ + for (i = 0; i < vgmstream->channels; i++) { + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x00,streamFile); + //vgmstream->ch[i].adpcm_history2_32 = read_16bit(block_offset + 0x0C + (i*0x04) + 0x02,streamFile); + vgmstream->ch[i].offset += vgmstream->channels*0x04; + } + break; + + default: + /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ + if (vgmstream->codec_version == 1) { + for (i = 0; i < vgmstream->channels; i++) { + //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); + //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); + vgmstream->ch[i].offset += 4; + } + } + break; + } + + /* reset channel sub offset for codecs using it */ + if (vgmstream->coding_type == coding_EA_XA + || vgmstream->coding_type == coding_EA_XA_int + || vgmstream->coding_type == coding_EA_XA_V2) { + for(i=0;ichannels;i++) { + vgmstream->ch[i].channel_start_offset = 0; + } + } + } + else { + /* setup first block to update offsets */ + ea_schl_block_update(start_offset,vgmstream); + } return vgmstream; @@ -245,11 +424,12 @@ static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset) { } /* 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) { +static int parse_variable_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; + memset(ea,0,sizeof(ea_header)); /* null defaults as 0 can be valid */ ea->version = EA_VERSION_NONE; @@ -274,26 +454,37 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi goto fail; } - /* parse mini-chunks/tags (variable, ommited if default exists) */ - while (offset - begin_offset < max_length) { + + /* parse mini-chunks/tags (variable, ommited if default exists; some are removed in later versions of sx.exe) */ + while (!is_header_end && 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 */ + case 0x00: /* signals non-default block rate and maybe other stuff; or padding after 0xFF */ 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 0x06: /* priority (0..100, always 0x65 for streams, others for BNKs; rarely ommited) */ + case 0x07: /* unknown (BNK only: 36|3A) */ + case 0x08: /* release envelope (BNK only) */ + case 0x09: /* related to playback envelope (BNK only) */ + case 0x0A: /* bend range (BNK only) */ case 0x0B: /* unknown (always 0x02) */ + case 0x0C: /* pan offset (BNK only) */ + case 0x0D: /* random pan offset range (BNK only) */ + case 0x0E: /* volume (BNK only) */ + case 0x0F: /* random volume range (BNK only) */ + case 0x10: /* detune (BNK only) */ + case 0x11: /* random detune range (BNK only) */ case 0x13: /* effect bus (0..127) */ case 0x14: /* emdedded user data (free size/value) */ + case 0x19: /* related to playback envelope (BNK only) */ + case 0x1B: /* unknown (movie only?) */ + case 0x1C: /* initial envelope volume (BNK only) */ + case 0x24: /* master random detune range (BNK only) */ read_patch(streamFile, &offset); break; @@ -302,6 +493,9 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi case 0xFD: /* info section start marker */ break; + case 0x83: /* codec1 defines, used early revisions */ + ea->codec1 = read_patch(streamFile, &offset); + break; case 0xA0: /* codec2 defines */ ea->codec2 = read_patch(streamFile, &offset); break; @@ -309,15 +503,13 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi case 0x80: /* version, affecting some codecs */ ea->version = read_patch(streamFile, &offset); break; + case 0x81: /* bits per sample for codec1 PCM */ + ea->bps = 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; @@ -333,7 +525,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi break; /* channel offsets (BNK only), can be the equal for all channels or interleaved; not necessarily contiguous */ - case 0x88: /* absolute offset of ch1 */ + case 0x88: /* absolute offset of ch1 (or ch1+ch2 for stereo EAXA) */ ea->offsets[0] = read_patch(streamFile, &offset); break; case 0x89: /* absolute offset of ch2 */ @@ -360,7 +552,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi ea->coefs[1] = offset+1; read_patch(streamFile, &offset); break; - case 0x91: /* DSP coefs ch3 */ + case 0x91: /* DSP coefs ch3, and unknown in older versions */ ea->coefs[2] = offset+1; read_patch(streamFile, &offset); break; @@ -377,9 +569,9 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi 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 0x8A: /* long padding (always 0x00000000) */ + case 0x8C: /* flags (ex. play type = 01=static/02=dynamic | spatialize = 20=pan/etc) */ + /* (ex. PS1 VAG=0, PS2 PCM/LAYER2=4, GC EAXA=4, 3DS DSP=512, Xbox EAXA=36, N64 BLK=05E800, N64 MT10=01588805E800) */ case 0x92: /* bytes per sample? */ case 0x98: /* embedded time stretch 1 (long data for who-knows-what) */ case 0x99: /* embedded time stretch 2 */ @@ -393,7 +585,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi read_patch(streamFile, &offset); break; - case 0xFF: /* header end (then 0-padded) */ + case 0xFF: /* header end (then 0-padded so it's 32b aligned) */ is_header_end = 1; break; @@ -403,44 +595,72 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi } } - 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 */ + /* Set defaults per platform, as the header ommits them when possible */ ea->loop_flag = (ea->loop_end); + /* affects blocks/codecs */ + if (ea->platform == EA_PLATFORM_N64 + || ea->platform == EA_PLATFORM_MAC + || ea->platform == EA_PLATFORM_SAT + || ea->platform == EA_PLATFORM_GC_WII + || ea->platform == EA_PLATFORM_X360 + || ea->platform == EA_PLATFORM_GENERIC) { + ea->big_endian = 1; + } + 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 */ + /* version mainly affects defaults and minor stuff, can come with all codecs */ + /* V0 is often just null but it's specified in some files (uncommon, with patch size 0x00) */ 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_N64: ea->version = EA_VERSION_V0; break; case EA_PLATFORM_MAC: ea->version = EA_VERSION_V0; break; + case EA_PLATFORM_SAT: 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; + case EA_PLATFORM_GENERIC: ea->version = EA_VERSION_V2; break; default: VGM_LOG("EA: unknown default version for platform 0x%02x\n", ea->platform); goto fail; } } + /* codec1 defaults */ + if (ea->codec1 == EA_CODEC1_NONE && ea->version == EA_VERSION_V0) { + switch(ea->platform) { + case EA_PLATFORM_PC: ea->codec1 = EA_CODEC1_PCM; break; + case EA_PLATFORM_PSX: ea->codec1 = EA_CODEC1_VAG; break; // assumed + //case EA_PLATFORM_N64: ea->codec1 = EA_CODEC1_N64; break; + case EA_PLATFORM_MAC: ea->codec1 = EA_CODEC1_PCM; break; // assumed + case EA_PLATFORM_SAT: ea->codec1 = EA_CODEC1_PCM; break; + default: + VGM_LOG("EA: unknown default codec1 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_PCM: + ea->codec2 = ea->bps==8 ? EA_CODEC2_S8 : (ea->big_endian ? EA_CODEC2_S16BE : EA_CODEC2_S16LE); + break; + case EA_CODEC1_VAG: ea->codec2 = EA_CODEC2_VAG; break; + case EA_CODEC1_EAXA: ea->codec2 = EA_CODEC2_EAXA; break; case EA_CODEC1_MT10: ea->codec2 = EA_CODEC2_MT10; break; default: VGM_LOG("EA: unknown codec1 0x%02x\n", ea->codec1); @@ -448,7 +668,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi } } - /* defaults don't seem to change with version or over time, fortunately */ + /* codec2 defaults */ if (ea->codec2 == EA_CODEC2_NONE) { switch(ea->platform) { case EA_PLATFORM_GENERIC: ea->codec2 = EA_CODEC2_EAXA; break; @@ -475,6 +695,7 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi 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_SAT: 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; @@ -487,26 +708,19 @@ static int parse_stream_header(STREAMFILE* streamFile, ea_header* ea, off_t begi } } - /* 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; + /* special flag: 1=has ADPCM history per block, 0=doesn't */ + if (ea->codec2 == EA_CODEC2_GCADPCM && ea->platform == EA_PLATFORM_3DS) { + ea->codec_version = 1; + } + else if (ea->codec2 == EA_CODEC2_EAXA && ea->codec1 == EA_CODEC1_NONE) { + /* console V2 uses hist, as does PC/MAC V1 (but not later versions) */ + if (ea->version <= EA_VERSION_V1 || + ((ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) + && ea->version == EA_VERSION_V2)) { + ea->codec_version = 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; @@ -518,67 +732,70 @@ fail: /* 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; +static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) { + int 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; + uint32_t id, block_size, block_samples; 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 */ + if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); + /* SCxx blocks have size in the header, but others may not. To simplify we just try to + * find a SCDl (main data) every 0x04. EA sometimes concats many small files, so after a SCEl (end block) + * there may be a new SCHl + SCDl too, so this pretends they are a single stream. */ 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); + block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels); break; default: - num_samples += read_32bit(block_offset+0x08,streamFile); + block_samples = read_32bit(block_offset+0x08,streamFile); break; } + + /* guard against false positives (happens in "pIQT" blocks) */ + if (block_size > 0xFFFF || block_samples > 0xFFFF) { /* observed max is ~0xf00 but who knows */ + block_offset += 0x04; + continue; + } + + num_samples += block_samples; + + block_offset += block_size; /* size includes header */ } - - 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 */ + else { + /* movie "pIQT" may be bigger than what block_size says, but seems to help */ + if (id == 0x5343486C || id == 0x5343436C || id == 0x53434C6C || id == 0x70495154) { /* "SCHl" "SCCl" "SCLl" "SCEl" "pIQT" */ + block_offset += block_size; + } else { block_offset += 0x04; } + + 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, important */ + } + + block_offset += 0x04; + continue; } - - 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; } /* 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) { +static off_t get_ea_stream_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; @@ -589,7 +806,7 @@ static off_t get_ea_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset 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 */ + if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */ block_size = read_32bitBE(block_offset+0x04,streamFile); if (id == 0x5343446C) { /* "SCDl" data block found */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c new file mode 100644 index 000000000..434f237ba --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_schl_fixed.c @@ -0,0 +1,124 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* Possibly the same as EA_CODEC_x in variable SCHl */ +#define EA_CODEC_PCM 0x00 +#define EA_CODEC_IMA 0x02 + +typedef struct { + int8_t version; + int8_t bps; + int8_t channels; + int8_t codec; + int16_t sample_rate; + int32_t num_samples; + + int big_endian; + int loop_flag; +} ea_header; + +static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); + + +/* EA SCHl with fixed header - from EA games (~1997? ex. NHL 97 PC) */ +VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t header_size; + ea_header ea; + + + /* check extension */ + if (!check_extensions(streamFile,"asf")) + goto fail; + + /* check header (see ea_schl.c for more info about blocks) */ + if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */ + goto fail; + + header_size = read_32bitLE(0x04,streamFile); + + if (!parse_fixed_header(streamFile,&ea, 0x08)) + goto fail; + + start_offset = header_size; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(ea.channels, ea.loop_flag); + if (!vgmstream) goto fail; + + 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->codec_endian = ea.big_endian; + + vgmstream->meta_type = meta_EA_SCHL_fixed; + + vgmstream->layout_type = layout_ea_blocked; + + switch (ea.codec) { + case EA_CODEC_PCM: + vgmstream->coding_type = ea.bps==8 ? coding_PCM8 : (ea.big_endian ? coding_PCM16BE : coding_PCM16LE); + break; + + case EA_CODEC_IMA: + vgmstream->coding_type = coding_EACS_IMA; + break; + + default: + VGM_LOG("EA: unknown codec 0x%02x\n", ea.codec); + goto fail; + } + + + /* open files; channel offsets are updated below */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* setup first block to update offsets */ + ea_schl_block_update(start_offset,vgmstream); + + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +static int parse_fixed_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset) { + off_t offset = begin_offset; + + memset(ea,0,sizeof(ea_header)); + + if (read_32bitBE(offset+0x00, streamFile) != 0x5041546C && /* "PATl" */ + read_32bitBE(offset+0x38, streamFile) != 0x544D706C) /* "TMpl" */ + goto fail; + + offset += 0x3c; /* after TMpl */ + ea->version = read_8bit(offset+0x00, streamFile); + ea->bps = read_8bit(offset+0x01, streamFile); + ea->channels = read_8bit(offset+0x02, streamFile); + ea->codec = read_8bit(offset+0x03, streamFile); + VGM_ASSERT(read_16bitLE(offset+0x04, streamFile) != 0, "EA SCHl fixed: unknown1 found\n"); + /* 0x04(16): unknown */ + ea->sample_rate = (uint16_t)read_16bitLE(offset+0x06, streamFile); + ea->num_samples = read_32bitLE(offset+0x08, streamFile); + VGM_ASSERT(read_32bitLE(offset+0x0c, streamFile) != -1, "EA SCHl fixed: unknown2 found\n"); /* loop start? */ + VGM_ASSERT(read_32bitLE(offset+0x10, streamFile) != -1, "EA SCHl fixed: unknown3 found\n"); /* loop end? */ + VGM_ASSERT(read_32bitLE(offset+0x14, streamFile) != 0, "EA SCHl fixed: unknown4 found\n"); /* data start? */ + VGM_ASSERT(read_32bitLE(offset+0x18, streamFile) != -1, "EA SCHl fixed: unknown5 found\n"); + VGM_ASSERT(read_32bitLE(offset+0x1c, streamFile) != 0x7F, "EA SCHl fixed: unknown6 found\n"); + + //ea->loop_flag = (ea->loop_end_sample); + + return 1; + +fail: + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/maxis_xa.c b/Frameworks/vgmstream/vgmstream/src/meta/maxis_xa.c index bf0588eca..73dcbebee 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/maxis_xa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/maxis_xa.c @@ -1,63 +1,50 @@ #include "meta.h" #include "../util.h" -/* Maxis XA - found in 'Sim City 3000' */ +/* Maxis XA - found in Sim City 3000 (PC) */ VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag; - int channel_count; + int loop_flag, channel_count, i; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xa",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"xa")) + goto fail; /* check header */ - if ((read_32bitBE(0x00,streamFile) != 0x58414900)&& /* "XAI\0" */ - (read_32bitBE(0x00,streamFile) != 0x58414A00)) /* "XAJ\0" */ + if ((read_32bitBE(0x00,streamFile) != 0x58414900) && /* "XAI\0" */ + (read_32bitBE(0x00,streamFile) != 0x58414A00)) /* "XAJ\0" (some odd song uses this, no apparent diffs) */ goto fail; - + loop_flag = 0; - channel_count = read_16bitLE(0xA,streamFile); - - /* build the VGMSTREAM */ + channel_count = read_16bitLE(0x0A,streamFile); + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ start_offset = 0x18; - vgmstream->channels = channel_count; + vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x0C,streamFile); - vgmstream->coding_type = coding_MAXIS_ADPCM; + vgmstream->coding_type = coding_MAXIS_XA; vgmstream->num_samples = read_32bitLE(0x04,streamFile)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitBE(0x0C,streamFile)-start_offset)/8/channel_count*14; - } - - vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_MAXIS_XA; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - vgmstream->ch[i].channel_start_offset=start_offset+i; - vgmstream->ch[i].offset=0; - } + /* open streams */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* fix channel offsets as needed by the codec (could be simplified) */ + for (i = 0; i < channel_count; i++) { + vgmstream->ch[i].channel_start_offset = start_offset+i; + vgmstream->ch[i].offset = 0; } return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 98b4c4e7a..281e36cc3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -678,4 +678,10 @@ VGMSTREAM * init_vgmstream_pc_xa30(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c index da727ea80..f9b9d0de1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_hwas.c @@ -1,69 +1,51 @@ #include "meta.h" -#include "../util.h" +#include "../layout/layout.h" +#include "../coding/coding.h" -/* HWAS (found in Spider-Man 3, Tony Hawk's Downhill Jam, possibly more...) */ +/* HWAS - found in Vicarious Visions NDS games (Spider-Man 3, Tony Hawk's Downhill Jam, etc) */ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; - int loop_flag = 0; - int channel_count; + int channel_count, loop_flag = 0; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("hwas",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"hwas")) + goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x73617768) /* "sawh" */ goto fail; - loop_flag = 0; + loop_flag = 1; /* almost all files seem to loop (usually if num_samples != loop_end doesn't loop, but not always) */ channel_count = read_32bitLE(0x0C,streamFile); - - /* build the VGMSTREAM */ + if (channel_count > 1) goto fail; /* unknown block layout when stereo, not seen */ + + start_offset = 0x200; + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x200; - vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->coding_type = coding_IMA_int; - vgmstream->num_samples = read_32bitLE(0x14,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x10,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile); - } - - if (channel_count == 1) { - vgmstream->layout_type = layout_none; - } else { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; // Not sure if there are stereo files - } + vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x14,streamFile), channel_count); + vgmstream->loop_start_sample = ima_bytes_to_samples(read_32bitLE(0x10,streamFile), channel_count); //assumed, always 0 + vgmstream->loop_end_sample = ima_bytes_to_samples(read_32bitLE(0x18,streamFile), channel_count); vgmstream->meta_type = meta_NDS_HWAS; - /* open the file for reading by each channel */ - - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; + vgmstream->coding_type = coding_IMA; + vgmstream->layout_type = layout_hwas_blocked; + vgmstream->full_block_size = read_32bitLE(0x04,streamFile); /* usually 0x2000, 0x4000 or 0x8000 */ + + /* open the file for reading by each channel */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + hwas_block_update(start_offset, vgmstream); - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } - return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c index 74b9c7d23..c41cd9ed8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_strm.c @@ -1,6 +1,7 @@ #include "meta.h" #include "../util.h" +/* STRM - common Nintendo NDS streaming format */ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; char filename[PATH_LIMIT]; @@ -103,69 +104,3 @@ fail: if (vgmstream) close_vgmstream(vgmstream); return NULL; } - - -/* STRM (from Final Fantasy Tactics A2 - Fuuketsu no Grimoire) */ -VGMSTREAM * init_vgmstream_nds_strm_ffta2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("strm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* RIFF */ - read_32bitBE(0x08,streamFile) != 0x494D4120) /* "IMA " */ - goto fail; - - loop_flag = (read_32bitLE(0x20,streamFile) !=0); - channel_count = read_32bitLE(0x24,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x2C; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x0C,streamFile); - vgmstream->coding_type = coding_IMA_int; - vgmstream->num_samples = (read_32bitLE(0x04,streamFile)-start_offset); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x28,streamFile); - } - - vgmstream->interleave_block_size = 0x80; - vgmstream->interleave_smallblock_size = (vgmstream->loop_end_sample)%((vgmstream->loop_end_sample)/vgmstream->interleave_block_size); - vgmstream->layout_type = layout_interleave_shortblock; - vgmstream->meta_type = meta_NDS_STRM_FFTA2; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nds_strm_ffta2.c b/Frameworks/vgmstream/vgmstream/src/meta/nds_strm_ffta2.c new file mode 100644 index 000000000..9f1e0e2e7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nds_strm_ffta2.c @@ -0,0 +1,48 @@ +#include "meta.h" +#include "../util.h" + +/* STRM - from Final Fantasy Tactics A2 (NDS) */ +VGMSTREAM * init_vgmstream_nds_strm_ffta2(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count; + + /* check extension, case insensitive (the ROM seems to use simply .bin though) */ + if (!check_extensions(streamFile,"strm")) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */ + read_32bitBE(0x08,streamFile) != 0x494D4120) /* "IMA " */ + goto fail; + + loop_flag = (read_32bitLE(0x20,streamFile) !=0); + channel_count = read_32bitLE(0x24,streamFile); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + start_offset = 0x2C; + vgmstream->channels = channel_count; + vgmstream->sample_rate = read_32bitLE(0x0C,streamFile); + vgmstream->coding_type = coding_IMA_int; //todo: seems it has some diffs vs regular IMA + vgmstream->num_samples = (read_32bitLE(0x04,streamFile)-start_offset); + if (loop_flag) { + vgmstream->loop_start_sample = read_32bitLE(0x20,streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x28,streamFile); + } + + vgmstream->interleave_block_size = 0x80; + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_NDS_STRM_FFTA2; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c index 214ac2ac4..82e0a70d5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_adpdtk.c @@ -4,50 +4,43 @@ VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE * chstreamfile; - char filename[PATH_LIMIT]; - - size_t file_size; - int i; + off_t start_offset = 0; + int channel_count = 2, loop_flag = 0; /* always stereo, no loop */ /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("adp",filename_extension(filename)) && - strcasecmp("dtk",filename_extension(filename))) goto fail; + if ( !check_extensions(streamFile,"dtk,adp")) + goto fail; - /* file size is the only way to determine sample count */ - file_size = get_streamfile_size(streamFile); + /* .adp files have no header, and the ext is common, so all we can do is look for valid first frames */ + if (check_extensions(streamFile,"adp")) { + int i; + for (i = 0; i < 10; i++) { /* try a bunch of frames */ + if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) || + read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile)) + goto fail; + } + } - /* .adp files have no header, so all we can do is look for a valid first frame */ - if (read_8bit(0,streamFile)!=read_8bit(2,streamFile) || read_8bit(1,streamFile)!=read_8bit(3,streamFile)) goto fail; - /* Hopefully we haven't falsely detected something else... */ /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(2,0); /* always stereo, no loop */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->num_samples = file_size/32*28; + vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28; vgmstream->sample_rate = 48000; vgmstream->coding_type = coding_NGC_DTK; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_ADPDTK; - /* locality is such that two streamfiles is silly */ - chstreamfile = streamFile->open(streamFile,filename,32*0x400); - if (!chstreamfile) goto fail; - for (i=0;i<2;i++) { - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = 0; - - vgmstream->ch[i].streamfile = chstreamfile; - } + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c index 3d2db19c5..66d174bd8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c @@ -60,9 +60,9 @@ VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE *streamFile) { /* no ID, only a basic 0x10 header with filesize and nulls; do some extra checks */ datasize = read_32bitLE(0x00,streamFile) & 0x00FFFFFF; /*24 bit*/ if (datasize + 0x10 != streamFile->get_size(streamFile) - && read_32bitLE(0x04,streamFile) != 0 - && read_32bitLE(0x08,streamFile) != 0 - && read_32bitLE(0x10,streamFile) != 0) + || read_32bitLE(0x04,streamFile) != 0 + || read_32bitLE(0x08,streamFile) != 0 + || read_32bitLE(0x10,streamFile) != 0) goto fail; stereo_flag = read_8bit(0x03, streamFile); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c index ce56b8cc7..0ef63ea59 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_strlr.c @@ -23,8 +23,14 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { goto fail; #endif - /* don't hijack Sonic & Sega All Stars Racing X360 (xma) */ - if (read_32bitBE(0x00,streamFile) == 0x52494646) /* "RIFF"*/ + /* don't hijack Sonic & Sega All Stars Racing X360 (xma) */ + if (read_32bitBE(0x00,streamFile) == 0x52494646) + goto fail; /* "RIFF"*/ + /* don't hijack Mad Dash Racing (Xbox) */ + if (read_32bitLE(0x0c,streamFile) == 1 + && read_32bitLE(0x010,streamFile) == 0 + && read_32bitLE(0x400,streamFile) == 0 + && read_32bitLE(0x7f0,streamFile) == 0) goto fail; loop_flag = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c index a3b24ad86..c2aeb8373 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psx_cdxa.c @@ -39,6 +39,8 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { /* don't misdetect Reflections' XA ("XA30" / "04SW") */ if (read_32bitBE(0x00,streamFile) == 0x58413330 || read_32bitBE(0x00,streamFile) == 0x30345357) goto fail; + /* don't misdetect Maxis XA ("XAI\0" / "XAJ\0") */ + if (read_32bitBE(0x00,streamFile) == 0x58414900 || read_32bitBE(0x00,streamFile) == 0x58414A00) goto fail; /* First init to have the correct info of the channel */ if (!headerless) { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c new file mode 100644 index 000000000..a51f81ab7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -0,0 +1,678 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../layout/layout.h" +#include "../util.h" + +/* known TXTH types */ +typedef enum { + PSX = 0, /* PSX ADPCM */ + XBOX = 1, /* XBOX IMA ADPCM */ + NGC_DTK = 2, /* NGC ADP/DTK ADPCM */ + PCM16BE = 3, /* 16bit big endian PCM */ + PCM16LE = 4, /* 16bit little endian PCM */ + PCM8 = 5, /* 8bit PCM */ + SDX2 = 6, /* SDX2 (3D0 games) */ + DVI_IMA = 7, /* DVI IMA ADPCM */ + MPEG = 8, /* MPEG (MP3) */ + IMA = 9, /* IMA ADPCM */ + AICA = 10, /* AICA ADPCM (dreamcast) */ + MSADPCM = 11, /* MS ADPCM (windows) */ + NGC_DSP = 12, /* NGC DSP (GC) */ + PCM8_U_int = 13, /* 8bit unsigned PCM (interleaved) */ + PSX_bf = 14, /* PSX ADPCM bad flagged */ + MS_IMA = 15, /* Microsoft IMA ADPCM */ + PCM8_U = 16, /* 8bit unsigned PCM */ + APPLE_IMA4 = 17, /* Apple Quicktime 4-bit IMA ADPCM */ + ATRAC3 = 18, /* raw ATRAC3 */ + ATRAC3PLUS = 19, /* raw ATRAC3PLUS */ + XMA1 = 20, /* raw XMA1 */ + XMA2 = 21, /* raw XMA2 */ + FFMPEG = 22, /* any headered FFmpeg format */ +} txth_type; + +typedef struct { + txth_type codec; + txth_type codec_mode; + uint32_t interleave; + + uint32_t id_value; + uint32_t id_offset; + + uint32_t channels; + uint32_t sample_rate; + + uint32_t data_size; + int data_size_set; + uint32_t start_offset; + + int sample_type_bytes; + uint32_t num_samples; + uint32_t loop_start_sample; + uint32_t loop_end_sample; + uint32_t loop_adjust; + int skip_samples_set; + uint32_t skip_samples; + + uint32_t loop_flag; + int loop_flag_set; + + uint32_t coef_offset; + uint32_t coef_spacing; + uint32_t coef_mode; + uint32_t coef_big_endian; + +} txth_header; + +static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth); +static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val); +static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value); +static int get_bytes_to_samples(txth_header * txth, uint32_t bytes); + + +/* TXTH - an artificial "generic" header for headerless streams. + * Similar to GENH, but with a single separate .txth file in the dir and text-based. */ +VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamText = NULL; + txth_header txth; + coding_t coding; + int i, j; + + /* no need for ID or ext checks -- if a .TXTH exists all is good + * (player still needs to accept the ext, so at worst rename to .vgmstream) */ + { + char filename[PATH_LIMIT]; + char fileext[PATH_LIMIT]; + + /* try "(path/)(name.ext).txth" */ + if (!get_streamfile_name(streamFile,filename,PATH_LIMIT)) goto fail; + strcat(filename, ".txth"); + streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamText) goto found; + + /* try "(path/)(.ext).txth" */ + if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail; + if (!get_streamfile_ext(streamFile,fileext,PATH_LIMIT)) goto fail; + strcat(filename,"."); + strcat(filename, fileext); + strcat(filename, ".txth"); + streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamText) goto found; + + /* try "(path/).txth" */ + if (!get_streamfile_path(streamFile,filename,PATH_LIMIT)) goto fail; + strcat(filename, ".txth"); + streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (streamText) goto found; + + /* not found */ + goto fail; + } +found: + + /* process the text file */ + memset(&txth,0,sizeof(txth_header)); + if (!parse_txth(streamFile, streamText, &txth)) + goto fail; + + + /* type to coding conversion */ + switch (txth.codec) { + case PSX: coding = coding_PSX; break; + case XBOX: coding = coding_XBOX; break; + case NGC_DTK: coding = coding_NGC_DTK; break; + case PCM16BE: coding = coding_PCM16BE; break; + case PCM16LE: coding = coding_PCM16LE; break; + case PCM8: coding = coding_PCM8; break; + case SDX2: coding = coding_SDX2; break; + case DVI_IMA: coding = coding_DVI_IMA; break; +#ifdef VGM_USE_MPEG + case MPEG: coding = coding_MPEG1_L3; break; /* we later find out exactly which */ +#endif + case IMA: coding = coding_IMA; break; + case AICA: coding = coding_AICA; break; + case MSADPCM: coding = coding_MSADPCM; break; + case NGC_DSP: coding = coding_NGC_DSP; break; + case PCM8_U_int: coding = coding_PCM8_U_int; break; + case PSX_bf: coding = coding_PSX_badflags; break; + case MS_IMA: coding = coding_MS_IMA; break; + case PCM8_U: coding = coding_PCM8_U; break; + case APPLE_IMA4: coding = coding_APPLE_IMA4; break; +#ifdef VGM_USE_FFMPEG + case ATRAC3: + case ATRAC3PLUS: + case XMA1: + case XMA2: + case FFMPEG: coding = coding_FFmpeg; break; +#endif + default: + goto fail; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(txth.channels,txth.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = txth.sample_rate; + vgmstream->num_samples = txth.num_samples; + vgmstream->loop_start_sample = txth.loop_start_sample; + vgmstream->loop_end_sample = txth.loop_end_sample; + + /* codec specific (taken from GENH with minimal changes) */ + switch (coding) { + case coding_PCM8_U_int: + vgmstream->layout_type = layout_none; + break; + case coding_PCM16LE: + case coding_PCM16BE: + case coding_PCM8: + case coding_PCM8_U: + case coding_SDX2: + case coding_PSX: + case coding_PSX_badflags: + case coding_DVI_IMA: + case coding_IMA: + case coding_AICA: + case coding_APPLE_IMA4: + vgmstream->interleave_block_size = txth.interleave; + if (vgmstream->channels > 1) + { + if (coding == coding_SDX2) { + coding = coding_SDX2_int; + } + + if (vgmstream->interleave_block_size==0xffffffff || vgmstream->interleave_block_size == 0) { + vgmstream->layout_type = layout_none; + } + else { + vgmstream->layout_type = layout_interleave; + if (coding == coding_DVI_IMA) + coding = coding_DVI_IMA_int; + if (coding == coding_IMA) + coding = coding_IMA_int; + } + + /* to avoid endless loops */ + if (!txth.interleave && ( + coding == coding_PSX || + coding == coding_PSX_badflags || + coding == coding_IMA_int || + coding == coding_DVI_IMA_int || + coding == coding_SDX2_int) ) { + goto fail; + } + } else { + vgmstream->layout_type = layout_none; + } + + /* setup adpcm */ + if (coding == coding_AICA) { + int i; + for (i=0;ichannels;i++) { + vgmstream->ch[i].adpcm_step_index = 0x7f; + } + } + + break; + case coding_MS_IMA: + if (!txth.interleave) goto fail; /* creates garbage */ + + vgmstream->interleave_block_size = txth.interleave; + vgmstream->layout_type = layout_none; + break; + case coding_MSADPCM: + if (vgmstream->channels > 2) goto fail; + if (!txth.interleave) goto fail; /* creates garbage */ + + vgmstream->interleave_block_size = txth.interleave; + vgmstream->layout_type = layout_none; + break; + case coding_XBOX: + vgmstream->layout_type = layout_none; + break; + case coding_NGC_DTK: + if (vgmstream->channels != 2) goto fail; + vgmstream->layout_type = layout_none; + break; + case coding_NGC_DSP: + if (txth.channels > 1 && txth.codec_mode == 0) { + if (!txth.interleave) goto fail; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = txth.interleave; + } else if (txth.channels > 1 && txth.codec_mode == 1) { + if (!txth.interleave) goto fail; + vgmstream->layout_type = layout_interleave_byte; + vgmstream->interleave_block_size = txth.interleave; + } else if (txth.channels == 1 || txth.codec_mode == 2) { + vgmstream->layout_type = layout_none; + } else { + goto fail; + } + + /* get coefs */ + for (i=0;ichannels;i++) { + int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; + + /* normal/split coefs */ + if (txth.coef_mode == 0) { + for (j=0;j<16;j++) { + vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2,streamFile); + } + } else { + goto fail; //IDK what is this + /* + for (j=0;j<8;j++) { + vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(coef[i]+j*2,streamFile); + vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(coef_splitted[i]+j*2,streamFile); + } + */ + } + } + + break; +#ifdef VGM_USE_MPEG + case coding_MPEG1_L3: + vgmstream->layout_type = layout_mpeg; + vgmstream->codec_data = init_mpeg_codec_data(streamFile, txth.start_offset, &coding, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; + + break; +#endif +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: { + ffmpeg_codec_data *ffmpeg_data = NULL; + + if (txth.codec == FFMPEG) { + /* default FFmpeg */ + ffmpeg_data = init_ffmpeg_offset(streamFile, txth.start_offset,txth.data_size); + if ( !ffmpeg_data ) goto fail; + } + else { + /* fake header FFmpeg */ + uint8_t buf[200]; + int32_t bytes; + + if (txth.codec == ATRAC3) { + int block_size = txth.interleave; + int joint_stereo; + switch(txth.codec_mode) { + case 0: joint_stereo = vgmstream->channels > 1 && txth.interleave/vgmstream->channels==0x60 ? 1 : 0; break; /* autodetect */ + case 1: joint_stereo = 1; break; /* force joint stereo */ + case 2: joint_stereo = 0; break; /* force stereo */ + default: goto fail; + } + + bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, txth.skip_samples); + } + else if (txth.codec == ATRAC3PLUS) { + int block_size = txth.interleave; + + bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_size, txth.skip_samples); + } + else if (txth.codec == XMA1) { + int xma_stream_mode = txth.codec_mode == 1 ? 1 : 0; + + bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); + } + else if (txth.codec == XMA2) { + int block_size = txth.interleave ? txth.interleave : 2048; + int block_count = txth.data_size / block_size; + + bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, txth.data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + } + else { + goto fail; + } + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, txth.start_offset,txth.data_size); + if ( !ffmpeg_data ) goto fail; + } + + vgmstream->codec_data = ffmpeg_data; + vgmstream->layout_type = layout_none; + + /* force encoder delay */ + if (txth.skip_samples_set) { + ffmpeg_set_skip_samples(ffmpeg_data, txth.skip_samples); + } + + break; + } +#endif + default: + break; + } + +#ifdef VGM_USE_FFMPEG + if (txth.sample_type_bytes && (txth.codec == XMA1 || txth.codec == XMA2)) { + /* manually find sample offsets */ + ms_sample_data msd; + memset(&msd,0,sizeof(ms_sample_data)); + + msd.xma_version = 1; + msd.channels = txth.channels; + msd.data_offset = txth.start_offset; + msd.data_size = txth.data_size; + msd.loop_flag = txth.loop_flag; + msd.loop_start_b = txth.loop_start_sample; + msd.loop_end_b = txth.loop_end_sample; + msd.loop_start_subframe = txth.loop_adjust & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */ + msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ + + xma_get_samples(&msd, streamFile); + vgmstream->num_samples = msd.num_samples; + vgmstream->loop_start_sample = msd.loop_start_sample; + vgmstream->loop_end_sample = msd.loop_end_sample; + //skip_samples = msd.skip_samples; //todo add skip samples + } +#endif + + vgmstream->coding_type = coding; + vgmstream->meta_type = meta_TXTH; + + + if ( !vgmstream_open_stream(vgmstream,streamFile,txth.start_offset) ) + goto fail; + + if (streamText) close_streamfile(streamText); + return vgmstream; + +fail: + if (streamText) close_streamfile(streamText); + close_vgmstream(vgmstream); + return NULL; +} + +/* Simple text parser of "key = value" lines. + * The code is meh and error handling not exactly the best. */ +static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth) { + off_t off = 0; + off_t file_size = get_streamfile_size(streamText); +#ifndef LINE_MAX /* some platforms define this via limits.h */ +#if defined(_MSC_VER) && (_MSC_VER < 1900) + enum { LINE_MAX = 0x2000 }; /* arbitrary max */ +#else + const size_t LINE_MAX = 0x2000; +#endif +#endif + + txth->data_size = get_streamfile_size(streamFile); /* for later use */ + + /* skip BOM if needed */ + if (read_16bitLE(0x00, streamText) == 0xFFFE || read_16bitLE(0x00, streamText) == 0xFEFF) + off = 0x02; + + /* read lines */ + while (off < file_size) { + char line[LINE_MAX]; + char key[LINE_MAX]; + char val[LINE_MAX]; /* at least as big as a line to avoid overflows (I hope) */ + int ok; + off_t line_start = off, line_end = 0; + line[0] = key[0] = val[0] = 0; + + /* find line end */ + while (line_end == 0 && off - line_start < LINE_MAX) { + char c = (char)read_8bit(off, streamText); + if (c == '\n') + line_end = off; + else if (off >= file_size) + line_end = off-1; + + off++; + } + + if (line_end == 0) + goto fail; /* bad file / line too long */ + + /* get key/val (ignores lead/trail spaces, stops at space/comment/separator) */ + read_streamfile((uint8_t*)line,line_start,line_end, streamText); + line[line_end - line_start + 1] = '\0'; + ok = sscanf(line, " %[^ \t#=] = %[^ \t#\r\n] ", key,val); + //VGM_LOG("TXTH: ok=%i, key=\"%s\", val=\"%s\" from 0x%lx to 0x%lx\n", ok, key, val, line_start, line_end); + + if (ok != 2) /* ignore line if no key=val (comment or garbage) */ + continue; + + if (!parse_keyval(streamFile, streamText, txth, key, val)) /* read key/val */ + goto fail; + } + + if (!txth->loop_flag_set) + txth->loop_flag = txth->loop_end_sample && txth->loop_end_sample != 0xFFFFFFFF; + + return 1; +fail: + return 0; +} + +static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val) { + + if (0==strcmp(key,"codec")) { + if (0==strcmp(val,"PSX")) txth->codec = PSX; + else if (0==strcmp(val,"XBOX")) txth->codec = XBOX; + else if (0==strcmp(val,"NGC_DTK")) txth->codec = NGC_DTK; + else if (0==strcmp(val,"PCM16BE")) txth->codec = PCM16BE; + else if (0==strcmp(val,"PCM16LE")) txth->codec = PCM16LE; + else if (0==strcmp(val,"PCM8")) txth->codec = PCM8; + else if (0==strcmp(val,"SDX2")) txth->codec = SDX2; + else if (0==strcmp(val,"DVI_IMA")) txth->codec = DVI_IMA; + else if (0==strcmp(val,"MPEG")) txth->codec = MPEG; + else if (0==strcmp(val,"IMA")) txth->codec = IMA; + else if (0==strcmp(val,"AICA")) txth->codec = AICA; + else if (0==strcmp(val,"MSADPCM")) txth->codec = MSADPCM; + else if (0==strcmp(val,"NGC_DSP")) txth->codec = NGC_DSP; + else if (0==strcmp(val,"PCM8_U_int")) txth->codec = PCM8_U_int; + else if (0==strcmp(val,"PSX_bf")) txth->codec = PSX_bf; + else if (0==strcmp(val,"MS_IMA")) txth->codec = MS_IMA; + else if (0==strcmp(val,"PCM8_U")) txth->codec = PCM8_U; + else if (0==strcmp(val,"APPLE_IMA4")) txth->codec = APPLE_IMA4; + else if (0==strcmp(val,"ATRAC3")) txth->codec = ATRAC3; + else if (0==strcmp(val,"ATRAC3PLUS")) txth->codec = ATRAC3PLUS; + else if (0==strcmp(val,"XMA1")) txth->codec = XMA1; + else if (0==strcmp(val,"XMA2")) txth->codec = XMA2; + else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG; + else goto fail; + } + else if (0==strcmp(key,"codec_mode")) { + if (!parse_num(streamFile,val, &txth->codec_mode)) goto fail; + } + else if (0==strcmp(key,"interleave")) { + if (!parse_num(streamFile,val, &txth->interleave)) goto fail; + } + else if (0==strcmp(key,"id_value")) { + if (!parse_num(streamFile,val, &txth->id_value)) goto fail; + } + else if (0==strcmp(key,"id_offset")) { + if (!parse_num(streamFile,val, &txth->id_offset)) goto fail; + if (txth->id_value != txth->id_offset) /* evaluate current ID */ + goto fail; + } + else if (0==strcmp(key,"channels")) { + if (!parse_num(streamFile,val, &txth->channels)) goto fail; + } + else if (0==strcmp(key,"sample_rate")) { + if (!parse_num(streamFile,val, &txth->sample_rate)) goto fail; + } + else if (0==strcmp(key,"start_offset")) { + if (!parse_num(streamFile,val, &txth->start_offset)) goto fail; + if (!txth->data_size_set) + txth->data_size = get_streamfile_size(streamFile) - txth->start_offset; /* re-evaluate */ + } + else if (0==strcmp(key,"data_size")) { + if (!parse_num(streamFile,val, &txth->data_size)) goto fail; + txth->data_size_set = 1; + } + else if (0==strcmp(key,"sample_type")) { + if (0==strcmp(val,"bytes")) txth->sample_type_bytes = 1; + else if (0==strcmp(val,"samples")) txth->sample_type_bytes = 0; + else goto fail; + } + else if (0==strcmp(key,"num_samples")) { + if (0==strcmp(val,"data_size")) { + txth->num_samples = get_bytes_to_samples(txth, txth->data_size); + } + else { + if (!parse_num(streamFile,val, &txth->num_samples)) goto fail; + if (txth->sample_type_bytes) + txth->num_samples = get_bytes_to_samples(txth, txth->num_samples); + } + } + else if (0==strcmp(key,"loop_start_sample")) { + if (!parse_num(streamFile,val, &txth->loop_start_sample)) goto fail; + if (txth->sample_type_bytes) + txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample); + if (txth->loop_adjust) + txth->loop_start_sample += txth->loop_adjust; + } + else if (0==strcmp(key,"loop_end_sample")) { + if (0==strcmp(val,"data_size")) { + txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size); + } + else { + if (!parse_num(streamFile,val, &txth->loop_end_sample)) goto fail; + if (txth->sample_type_bytes) + txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample); + } + if (txth->loop_adjust) + txth->loop_end_sample += txth->loop_adjust; + } + else if (0==strcmp(key,"skip_samples")) { + if (!parse_num(streamFile,val, &txth->skip_samples)) goto fail; + txth->skip_samples_set = 1; + if (txth->sample_type_bytes) + txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples); + } + else if (0==strcmp(key,"loop_adjust")) { + if (!parse_num(streamFile,val, &txth->loop_adjust)) goto fail; + if (txth->sample_type_bytes) + txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust); + } + else if (0==strcmp(key,"loop_flag")) { + if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail; + txth->loop_flag_set = 1; + } + else if (0==strcmp(key,"coef_offset")) { + if (!parse_num(streamFile,val, &txth->coef_offset)) goto fail; + } + else if (0==strcmp(key,"coef_spacing")) { + if (!parse_num(streamFile,val, &txth->coef_spacing)) goto fail; + } + else if (0==strcmp(key,"coef_endianness")) { + if (val[0]=='B' && val[1]=='E') + txth->coef_big_endian = 1; + else if (val[0]=='L' && val[1]=='E') + txth->coef_big_endian = 0; + else if (!parse_num(streamFile,val, &txth->coef_big_endian)) goto fail; + } + else if (0==strcmp(key,"coef_mode")) { + if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail; + } + else { + VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val); + goto fail; + } + + return 1; +fail: + return 0; +} + +static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value) { + + if (val[0] == '@') { /* offset */ + uint32_t off = 0; + char ed1 = 'L', ed2 = 'E'; + int size = 4; + int big_endian = 0; + int hex = (val[1]=='0' && val[2]=='x'); + + /* read exactly N fields in the expected format */ + if (strchr(val,':') && strchr(val,'$')) { + if (sscanf(val, hex ? "@%x:%c%c$%i" : "@%u:%c%c$%i", &off, &ed1,&ed2, &size) != 4) goto fail; + } else if (strchr(val,':')) { + if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &off, &ed1,&ed2) != 3) goto fail; + } else if (strchr(val,'$')) { + if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &off, &size) != 2) goto fail; + } else { + if (sscanf(val, hex ? "@%x" : "@%u", &off) != 1) goto fail; + } + + if (off < 0 || off > get_streamfile_size(streamFile)) + goto fail; + + if (ed1 == 'B' && ed2 == 'E') + big_endian = 1; + else if (!(ed1 == 'L' && ed2 == 'E')) + goto fail; + + switch(size) { + case 1: *out_value = read_8bit(off,streamFile); break; + case 2: *out_value = big_endian ? (uint16_t)read_16bitBE(off,streamFile) : (uint16_t)read_16bitLE(off,streamFile); break; + case 3: *out_value = (big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile)) & 0x00FFFFFF; break; + case 4: *out_value = big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile); break; + default: goto fail; + } + } + else { /* constant */ + int hex = (val[0]=='0' && val[1]=='x'); + + if (sscanf(val, hex ? "%x" : "%u", out_value)!=1) goto fail; + } + + //VGM_LOG("TXTH: value=%s, read %u\n", val, *out_value); + return 1; +fail: + return 0; +} + +static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { + switch(txth->codec) { + case MS_IMA: + return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels); + case XBOX: + return ms_ima_bytes_to_samples(bytes, 0x24 * txth->channels, txth->channels); + case NGC_DSP: + return dsp_bytes_to_samples(bytes, txth->channels); + case PSX: + case PSX_bf: + return ps_bytes_to_samples(bytes, txth->channels); + case PCM16BE: + case PCM16LE: + return pcm_bytes_to_samples(bytes, txth->channels, 16); + case PCM8: + case PCM8_U_int: + case PCM8_U: + return pcm_bytes_to_samples(bytes, txth->channels, 8); + case MSADPCM: + return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels); + case ATRAC3: + return atrac3_bytes_to_samples(bytes, txth->interleave); + case ATRAC3PLUS: + return atrac3plus_bytes_to_samples(bytes, txth->interleave); + + /* XMA bytes-to-samples is done at the end as the value meanings are a bit different */ + case XMA1: + case XMA2: + return bytes; /* preserve */ + + /* untested */ + case IMA: + case DVI_IMA: + case SDX2: + return bytes; + case AICA: + return bytes * 2 / txth->channels; + case NGC_DTK: + return bytes / 32 * 28; /* always stereo? */ + case APPLE_IMA4: + return (bytes / txth->interleave) * (txth->interleave - 2) * 2; + + case MPEG: /* a bit complex */ + case FFMPEG: /* too complex */ + default: + return 0; + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xma.c b/Frameworks/vgmstream/vgmstream/src/meta/xma.c index d8247f179..82e495e1d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xma.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xma.c @@ -8,6 +8,7 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { size_t data_size, chunk_size; int loop_flag, channel_count, sample_rate, is_xma2_old = 0, is_xma1 = 0; int num_samples, loop_start_sample, loop_end_sample, loop_start_b = 0, loop_end_b = 0, loop_subframe = 0; + int fmt_be = 0; /* check extension, case insensitive */ @@ -34,13 +35,24 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { } else if ( find_chunk_le(streamFile, 0x666d7420,first_offset,0, &chunk_offset,&chunk_size)) { int format = read_16bitLE(chunk_offset,streamFile); + + /* some Fable Heroes and Fable 3 XMA have a BE fmt chunk, but the rest of the file is still LE + * (incidentally they come with a "frsk" chunk) */ + if (format == 0x6601) { /* new XMA2 but BE */ + fmt_be = 1; + format = 0x0166; + } + if (format == 0x165) { /* XMA1 */ is_xma1 = 1; - xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 0); + xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, fmt_be); } else if (format == 0x166) { /* new XMA2 */ - channel_count = read_16bitLE(chunk_offset+0x02,streamFile); - sample_rate = read_32bitLE(chunk_offset+0x04,streamFile); - xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 0); + int32_t (*read_32bit)(off_t,STREAMFILE*) = fmt_be ? read_32bitBE : read_32bitLE; + int16_t (*read_16bit)(off_t,STREAMFILE*) = fmt_be ? read_16bitBE : read_16bitLE; + + channel_count = read_16bit(chunk_offset+0x02,streamFile); + sample_rate = read_32bit(chunk_offset+0x04,streamFile); + xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, fmt_be); } else { goto fail; } @@ -99,7 +111,7 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { if (is_xma2_old) { bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile); } else { - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 0); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, fmt_be); } if (bytes <= 0) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 861910a93..a2bb3b2da 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -553,3 +553,29 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 0; } + +int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { + streamFile->get_name(streamFile,buffer,size); + return 1; +} +int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) { + const char *path; + + streamFile->get_name(streamFile,buffer,size); + + path = strrchr(buffer,DIR_SEPARATOR); + if (path!=NULL) path = path+1; /* includes "/" */ + + if (path) { + buffer[path - buffer] = '\0'; + } else { + buffer[0] = '\0'; + } + + return 1; +} +int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) { + streamFile->get_name(streamFile,filename,size); + strcpy(filename, filename_extension(filename)); + return 1; +} diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index 3ac824bd0..2aa417cdb 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -159,4 +159,8 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts); int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size); int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int size_big_endian, int zero_size_end); + +int get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size); +int get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size); +int get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 11d8979b3..6d3084b42 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -347,6 +347,10 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_dsp_adx, init_vgmstream_akb_multi, init_vgmstream_akb2_multi, +#ifdef VGM_USE_FFMPEG + init_vgmstream_mp4_aac_ffmpeg, + init_vgmstream_bik, +#endif init_vgmstream_x360_ast, init_vgmstream_wwise, init_vgmstream_ubi_raki, @@ -364,11 +368,11 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_ulw, init_vgmstream_pc_xa30, init_vgmstream_wii_04sw, + init_vgmstream_ea_bnk, + init_vgmstream_ea_schl_fixed, + init_vgmstream_txth, /* should go at the end (lower priority) */ #ifdef VGM_USE_FFMPEG - init_vgmstream_mp4_aac_ffmpeg, - init_vgmstream_bik, - init_vgmstream_ffmpeg, /* should go at the end */ #endif }; @@ -940,6 +944,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_ps2_iab_blocked: case layout_ps2_strlr_blocked: case layout_rws_blocked: + case layout_hwas_blocked: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_interleave_byte: @@ -1045,9 +1050,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_FSB_IMA: return 64; case coding_EA_XA: + case coding_EA_XA_int: + case coding_EA_XA_V2: return 28; - case coding_MAXIS_ADPCM: - case coding_EA_MT10: + case coding_MAXIS_XA: return 14*vgmstream->channels; case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ @@ -1189,12 +1195,14 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_XBOX_int: case coding_FSB_IMA: return 36; - case coding_MAXIS_ADPCM: - return 15*vgmstream->channels; - case coding_EA_MT10: - return 30; case coding_EA_XA: - return 1; // the frame is variant in size + return 0x1E; + case coding_EA_XA_int: + return 0x0F; + case coding_MAXIS_XA: + return 0x0F*vgmstream->channels; + case coding_EA_XA_V2: + return 1; /* the frame is variant in size (ADPCM frames of 0x0F or PCM frames) */ case coding_WS: return vgmstream->current_block_size; case coding_IMA_int: @@ -1394,7 +1402,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to break; case coding_XBOX_int: for (chan=0;chanchannels;chan++) { - decode_int_xbox_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + decode_xbox_ima_int(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } @@ -1490,16 +1498,23 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; - case coding_EA_MT10: + case coding_EA_XA_int: for (chan=0;chanchannels;chan++) { - decode_ea_mt10(vgmstream,buffer+samples_written*vgmstream->channels+chan, + decode_ea_xa_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; - case coding_MAXIS_ADPCM: + case coding_EA_XA_V2: for (chan=0;chanchannels;chan++) { - decode_maxis_adpcm(vgmstream,buffer+samples_written*vgmstream->channels+chan, + decode_ea_xa_v2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,chan); + } + break; + case coding_MAXIS_XA: + for (chan=0;chanchannels;chan++) { + decode_maxis_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } @@ -1849,6 +1864,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { if (vgmstream->meta_type == meta_DSP_STD || vgmstream->meta_type == meta_DSP_RS03 || vgmstream->meta_type == meta_DSP_CSTR || + vgmstream->meta_type == meta_NDS_STRM_FFTA2 || /* clicks in some files otherwise */ vgmstream->coding_type == coding_PSX || vgmstream->coding_type == coding_PSX_bmdx || vgmstream->coding_type == coding_PSX_badflags) { diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index c7283aaf9..e6daad0f7 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -113,9 +113,10 @@ typedef enum { coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (FF XI, SGXD type 5, Bizarre Creations) */ coding_HEVAG, /* Sony PSVita 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_EA_XA, /* Electronic Arts EA-XA ADPCM v1 (stereo) aka "EA ADPCM" */ + coding_EA_XA_int, /* Electronic Arts EA-XA ADPCM v1 (mono/interleave) */ + coding_EA_XA_V2, /* Electronic Arts EA-XA ADPCM v2 */ + coding_MAXIS_XA, /* Maxis EA-XA ADPCM */ coding_XBOX, /* XBOX IMA ADPCM */ coding_XBOX_int, /* XBOX IMA ADPCM (interleaved) */ @@ -143,7 +144,7 @@ typedef enum { coding_L5_555, /* Level-5 0x555 ADPCM */ coding_SASSC, /* Activision EXAKT SASSC DPCM */ coding_LSF, /* lsf ADPCM (Fastlane Street Racing iPhone)*/ - coding_MTAF, /* Konami MTAF ADPCM (IMA-derived) */ + coding_MTAF, /* Konami MTAF ADPCM */ coding_MTA2, /* Konami MTA2 ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */ @@ -245,6 +246,7 @@ typedef enum { layout_ps2_iab_blocked, layout_ps2_strlr_blocked, layout_rws_blocked, + layout_hwas_blocked, /* otherwise odd */ layout_acm, /* libacm layout */ @@ -464,7 +466,9 @@ typedef enum { meta_XBOX_XMU, /* XBOX XMU */ meta_XBOX_XVAS, /* XBOX VAS */ - meta_EA_SCHL, /* Electronic Arts SCHl */ + meta_EA_SCHL, /* Electronic Arts SCHl with variable header */ + meta_EA_SCHL_fixed, /* Electronic Arts SCHl with fixed header */ + meta_EA_BNK, /* Electronic Arts BNK */ meta_EACS_PC, /* Electronic Arts EACS PC */ meta_EACS_PSX, /* Electronic Arts EACS PSX */ meta_EACS_SAT, /* Electronic Arts EACS SATURN */ @@ -626,6 +630,7 @@ typedef enum { meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_PC_XA30, /* Driver - Parallel Lines (PC) */ meta_WII_04SW, /* Driver - Parallel Lines (Wii) */ + meta_TXTH, /* generic text header */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */